2012-04-15 7 views
8

初めてのポスターはここにあります。私は通常、自分自身で答えを見つけるのが好きです(研究や試行錯誤)、私はここで困惑しています。Androidオーディオ - ストリーミングサイントーンジェネレータの奇妙な動作

私がやろうとしていること: 私は単純なアンドロイドのオーディオシンセサイザーを構築しています。今は、ユーザーの調整に合わせて音色の周波数を変更するUIのスライダを使って、サイン・トーンをリアルタイムで再生しています。

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

効果: ほぼすべてです。スレッドはうまく通信し、再生にギャップやクリックがないように見えます。大きなバッファサイズ(アンドロイドに感謝)にもかかわらず、応答性は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); 

ワーカースレッドのバッファが移入されている(ダニを経由して())のような:

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 buffering problems ソリューション1は理にかなってaudioTrack、のために十分な速さでサンプルを生成することができなければならないと提案:

私はここで同様の問題が見つかりました。サンプルレートを22050Hzに下げ、いくつかの実験的テストを実行しました。最悪の場合、約6msでバッファを埋めることができます。これ以上は十分です。 22050Hzでは、audioTrackは私に2048サンプル(または4096バイト)のバッファサイズを与えます。つまり、塗りつぶされた各バッファは〜0まで続きます。0928秒のオーディオは、データの作成に要する時間(1〜6ミリ秒)よりもはるかに長い時間です。だから、私はサンプルをすばやく作成するのに問題がないことを知っています。

アプリケーションライフサイクルの最初の3秒間は、正常に動作することにも注意してください。スライダをスムーズにスイープすると、オーディオ出力が滑らかになります。その後、それは本当に不安定になり始めます(サウンドは100Mhzごとに変化します)。その後、スライダー入力には全く反応しなくなります。

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

+0

'_freq'の変更にはどのようなイベントを使用しますか?それは本当に1Hz毎に変わるのですか?申し訳ありませんが、私はあなたのことをはっきりと理解していません。 **あなたの問題を聞くことを可能にするために、フルワーキングコードサンプルまたは合成サウンドフラグメントを提供できますか?サウンドフラグメントの場合:生のサウンドデータフォーマットは正常です。オーディオカードではなくバッファにファイルを書き込んでください。たとえば、16ビット/符号付き/モノ/ 44100 Hzです。 –

+0

_freqを変更するには、onSeekBarChangeListenerを使用して、onProgressChanged()を使用して新しいint値を_freqに書き込みます。しかし、私はまた、周波数を1Hz増加させるボタン(上下)を使用しようとしました。そして、多くの増分の後に音の違いがあります(音が約3分音符または4分音符、1Hzではなく)。ここでは何が起こっているのかの例です。これは、スライダを上下にゆっくりとスライドさせ、1Hz刻みで周波数をインクリメントするときに聞こえるものです。 (〜200〜〜1000Hz):http://www.sendspace.com/file/tpxuwr – Tsherr

+0

私は同様の問題をオンラインで発見しましたが、提案された解決策は役に立たなかった - 上記のアップデートをご覧ください。 – Tsherr

答えて

4

私はそれを見つけました!

バッファーやスレッディングとは関係がないことが判明しました。

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

したがって、Math.sin()FloatMath.sin()に置き換えました。

私も

_currentAngle = ((_currentAngle + _angleIncrement) % (2.0f * (float) Math.PI));_currentAngle = _currentAngle + _angleIncrement;

を交換し、その角度は常に< 2 * PIです。

魅力的な作品です!おかげさまでお世話になりました。

+0

くそー、私は約2 * Piの正規化を忘れてしまった!私は自分の古いコードでそれを見てきましたが、あなたの中にそれがないことを逃しました。しかし、私は角度変数のオーバーフローがクリックとサンプル音の効果を与えるはずだと思った。とにかく、あなたの問題を解決してうれしいです。 –