質問: AudioTrack と setLoopPoints() を使用して、ミリ秒あたりのサンプル/フレーム数に基づいた精度でループを構成する方法はありますか?
編集:ほとんどの Android デバイスが持つ処理能力からは完全な精度が期待できないことを理解しています。ただし、平均ループ時間をミリ秒単位のテンポの「実際の」間隔に近づけたいと思います。これは、テンポと同期する必要がある「アニメーション」に基づいているためです (アニメーションは、再描画する SurfaceView です)テンポの間隔中の線の座標)。
詳細: setLoopPoints で AudioTrack を使用して、正確なメトロノームを作成しようとしています。これを行うために、2 つの wav ファイル (Tick と Tock) を使用して byte[] 配列を埋め、AudioTrack にフィードします。4/4 時間で byte[] を埋める例を考えてみましょう。1 回は [0] から始まる Tick で、3 回は (arrayCopy() を使用して) Tock で [length/4]、[length/2]、および[3*長さ/4] とし、wav データが重ならないようにします。
私のコードが行うことの大まかな例:
// read each wav file's header into its own ByteBuffer (with LITTLE_ENDIAN order)
// ... then get the size of the audio data
tickDataSize = tickHeaderBuffer.getInt(40);
tockDataSize = tockHeaderBuffer.getInt(40);
// allocate space for one loop at the current tempo into a byte[] array (to be given to AudioTrack)
// e.g. 22050hz * 2 Bytes (1 per channel) * 2 Bytes (1 per 8 bit PCM sample) = 22050*4 Bytes/second
// If my tempo were 60 BPM I'd have 88200 Bytes/second (where 1 second is the whole loop interval);
// 110 BPM = 48109.0909091 Bytes per loop interval (where 1 loop interval is 0.54545 seconds)
int tempo = 110;
int bytesPerSecond = sampleRate * 2 * 2;
int bytesPerInterval = (int)((((float)bytesPerSecond * 60.0F)/(float)tempo) * 4);
byte[] wavData = new byte[bytesPerInterval];
// ... then fill wavData[] as mentioned above with 1*wavOne and 3*wavTwo
// Then feed to an instance of AudioTrack and set loop points
audioTrack.write(wavData, 0, bytesPerInterval);
int numSamples = bytesPerInterval/4;
audioTrack.setLoopPoints(0, numSamples, -1);
audioTrack.play();
問題が見えてきたと思います。特定のテンポでは、ループで静的な再生しか得られません(ただし、1 番目と 3 番目の Tock [ループの 2 番目と 4 番目のサンプル] の間のみ)。
次の場合、スタティックは停止します。
- byte[] に wav データを入力しないで、bytesPerInterval と numSamples を同じにします (正しい持続時間のサイレント ループ)。
- bytesPerInterval = bytesPerInterval % 4 に設定します (したがって、テンポの精度が失われます)
機能する (静的ではない) テンポと機能しない (静的) テンポの例と必要なフレーム数 (1 秒 = 88200 フレームと考えてください):
tempo: 110 (static)
wavData.length: 192436
numFrames: 48109
tempo: 120 (no static)
wavData.length: 176400
numFrames: 44100
tempo: 130 (static)
wavData.length: 162828
numFrames: 40707
tempo: 140 (no static)
wavData.length: 151200
numFrames: 37800
tempo: 150 (no static)
wavData.length: 141120
numFrames: 35280
tempo: 160 (static)
wavData.length: 132300
numFrames: 33075
tempo: 170 (static)
wavData.length: 124516
numFrames: 31129
tempo: 180 (no static)
wavData.length: 117600
numFrames: 29400
質問に対する答えが「いいえ、setLoopPoints() を使用してミリ秒単位で正確なループを構成することはできません」という場合は、他のオプションについて知りたいと思います。NDK、SoundPool、または MediaPlayer の OpenSL ES は、正確なループを生成するのに適していますか?
編集 2:静的な問題の原因となっている場所を絞り込みました:
// Assume a tempo of 160 BPM which requires byte[132300]
wavStream1 = this.context.getResources().openRawResource(R.raw.tick);
wavStream2 = this.context.getResources().openRawResource(R.raw.tock);
ByteBuffer headerBuffer1 = ByteBuffer.allocate(44);
ByteBuffer headerBuffer2 = ByteBuffer.allocate(44);
headerBuffer1.order(ByteOrder.LITTLE_ENDIAN);
headerBuffer2.order(ByteOrder.LITTLE_ENDIAN);
wavStream1.read(headerBuffer1.array(), 0, 44);
wavStream2.read(headerBuffer2.array(), 0, 44);
int tickDataSize = headerBuffer1.getInt(40);
int tockDataSize = headerBuffer2.getInt(40);
byte[] wavData = new byte[bytesPerInterval * 4];
byte[] tickWavData = new byte[bytesPerInterval];
byte[] tockWavData = new byte[bytesPerInterval];
wavStream1.read(accentWavData, 0, tickDataSize);
wavStream2.read(normalWavData, 0, tockDataSize);
System.arraycopy(tickWavData, 0, wavData, 0, bytesPerInterval);
System.arraycopy(tockWavData, 0, wavData, 33075, bytesPerInterval);
System.arraycopy(tockWavData, 0, wavData, 66150, bytesPerInterval);
System.arraycopy(tockWavData, 0, wavData, 99225, bytesPerInterval);
// bytesPerInterval of 33075 and 99225 (or any odd number) will be
// static when wavData is played
AudioTrack audioTrack = new AudioTrack(3, 22050, 12, 2, wavData.length, 0);
audioTrack.write(wavData, 0, wavData.length);
audioTrack.setLoopPoints(0, bytesPerInterval, -1);
audioTrack.play();
最も重要なことは、wavData の奇数インデックスで始まるオーディオ データが、予想されるサウンドではなくスタティックを生成する理由と、これに対する救済策があるかどうかを理解したいことです。