9

初めてのポスターはこちら。私は通常、自分で答えを見つけるのが好きですが(調査であれ試行錯誤であれ)、ここで困惑しています。

私がやろうとしていること: 私はシンプルなAndroidオーディオシンセサイザーを構築しています。現在、ユーザーが調整するとトーンの周波数を変更するUIのスライダーを使用して、リアルタイムで正弦波を再生しています。

構築方法: 基本的に、ワーカースレッドと出力スレッドの2つのスレッドがあります。ワーカースレッドは、tick()メソッドが呼び出されるたびに、正弦波データでバッファーを埋めるだけです。バッファがいっぱいになると、データをオーディオトラックに書き込む準備ができたことを出力スレッドに警告します。2つのスレッドを使用している理由は、audiotrack.write()ブロックであり、ワーカースレッドが(オーディオトラックの書き込みが完了するのを待つのではなく)できるだけ早くデータの処理を開始できるようにするためです。UIのスライダーは、ワーカースレッドの変数を変更するだけなので、(スライダーを介した)頻度の変更は、ワーカースレッドのtick()メソッドによって読み取られます。

動作するもの: ほとんどすべて。スレッドはうまく通信し、再生にギャップやクリックはないようです。大きなバッファサイズ(androidのおかげで)にもかかわらず、応答性はOKです。周波数変数は、tick()メソッドのバッファー計算中に使用される中間値(Log.i()によって検証される)と同様に変更されます。

動作しないもの: 何らかの理由で、可聴周波数が継続的に変化するようには見えません。スライダーを調整すると、周波数は段階的に変化します。多くの場合、4分の1または5分の1の幅です。理論的には、1Hz程度の変化が聞こえるはずですが、そうではありません。奇妙なことに、スライダーを変更すると、正弦波が調和級数の間隔で再生されているように見えます。ただし、周波数変数がデフォルト周波数の整数倍にスナップしていないことを確認できます。

私のオーディオトラックは次のように設定されています。

_buffSize = AudioTrack.getMinBufferSize(sampleRate, AudioFormat.CHANNEL_OUT_STEREO, AudioFormat.ENCODING_PCM_16BIT);
_audioTrackOut = new AudioTrack(AudioManager.STREAM_MUSIC, _sampleRate, AudioFormat.CHANNEL_OUT_STEREO, AudioFormat.ENCODING_PCM_16BIT, _buffSize, AudioTrack.MODE_STREAM);

ワーカースレッドのバッファーは、次のように(tick()を介して)入力されています。

public short[] tick()
{
    short[] outBuff = new short[_outBuffSize/2]; // (buffer size in Bytes) / 2
    for (int i = 0; i < _outBuffSize/2; i++) 
    {
        outBuff[i] = (short) (Short.MAX_VALUE * ((float) Math.sin(_currentAngle)));

        //Update angleIncrement, as the frequency may have changed by now
        _angleIncrement = (float) (2.0f * Math.PI) * _freq / _sampleRate;
        _currentAngle = _currentAngle + _angleIncrement;    
    }
    return outBuff;     
}

オーディオデータは次のように書き込まれます。

_audioTrackOut.write(fromWorker, 0, fromWorker.length);

どんな助けでも大歓迎です。どうすれば周波数を徐々に変化させることができますか?Log.i()は、変数angleIncrementとcurrentAngleが適切に更新されていることを確認するため、tick()のロジックは適切であると確信しています。

ありがとうございました!

アップデート:

私はここで同様の問題を見つけました:Android AudioTrackバッファリング の問題ソリューションは、audioTrackに十分な速度でサンプルを生成できなければならないことを提案しました。これは理にかなっています。サンプルレートを22050Hzに下げ、いくつかの経験的テストを実行しました。最悪の場合、約6msで(tick()を介して)バッファーを埋めることができます。これで十分です。22050Hzで、audioTrackは2048サンプル(または4096バイト)のバッファサイズを提供します。したがって、満たされた各バッファは約0.0928秒のオーディオで持続します。これは、データの作成にかかる時間(1〜6ミリ秒)よりもはるかに長くなります。だから、私はサンプルを十分に速く生成することに問題がないことを知っています。

また、アプリケーションのライフサイクルの最初の約3秒間は正常に機能します。スライダーをスムーズにスイープすると、オーディオ出力がスムーズにスイープされます。この後、それは本当に途切れ途切れになり始め(音は約100Mhzごとに変化するだけです)、その後、それはスライダー入力にまったく反応しなくなります。

バグも1つ修正しましたが、効果がないと思います。AudioTrack.getMinBufferSize()は、BYTESで許容される最小のバッファーサイズを返します。この数値をtick()のバッファーの長さとして使用していました。現在、この数値の半分(サンプルあたり2バイト)を使用しています。

4

1 に答える 1

5

見つけた!

問題はバッファやスレッド化とは何の関係もないことがわかりました。

計算の角度が比較的小さいため、最初の数秒は問題なく聞こえます。プログラムが実行され、角度が大きくなると、Math.sin(_currentAngle)は信頼できない値を生成し始めます。

そこで、に置き換えMath.sin()ましたFloatMath.sin()

私も交換しました _currentAngle = _currentAngle + _angleIncrement;

_currentAngle = ((_currentAngle + _angleIncrement) % (2.0f * (float) Math.PI));、したがって、角度は常に<2*PIです。

チャームのように機能します!プレトリアン・ドロイド、あなたの助けに感謝します!

于 2012-04-15T08:23:32.343 に答える