私のアプリの中には、1分間に指定した回数(bpm)のクリック音を鳴らす簡単なメトロノームスタイルの機能があります。私は、指定されたbpmから計算された間隔で、サウンドを再生するメソッドを呼び出すNSTimerを起動することでこれを行います。私のオーディオ・サウンドが時間通りに再生されないのはなぜですか?
私がNSLog行をplayメソッドに入れると、NSTimerが約1ミリ秒に正確に起動していることがわかります。しかし、音声出力をオーディオエディタに記録してからクリック間隔を測定すると、それらが均等に配置されていないことがわかります。たとえば、150 bpmの場合、タイマーは400ミリ秒ごとに起動します。しかし、ほとんどのサウンドは395ミリ秒後に再生され、3番目または4番目のサウンドは418ミリ秒後に再生されます。
音が一様に遅れているわけではなく、より短く長い間隔のパターンに従います。 iOSのサウンドのタイミングの解像度が低いようで、各サウンドイベントを最も近い利用可能なポイントに丸め、トラック全体を維持するために必要に応じて切り上げまたは切り下げます。
私はこれをシステムサウンド、AVAudioPlayer、OpenALで試してみたところ、3つの方法すべてで全く同じ結果が得られました。それぞれの方法では、ビューが読み込まれるときにすべての設定を行っています。そのため、再生するたびに再生する必要があります。 AVAudioPlayerを使用して、サウンドが再生されるたびに2番目のタイマーを使用してprepareToPlayを呼び出してみました。そのため、次回には初期化され準備が整いますが、同じ結果が得られました。ここで
は、のviewDidLoad(this tutorialから適応)にOpenALの音を設定するためのコードです:
// set up the context and device
ALCcontext *context;
ALCdevice *device;
OSStatus result;
device = alcOpenDevice(NULL); // select the "preferred device"
if (device) {
context = alcCreateContext(device, NULL); // use the device to make a context
alcMakeContextCurrent(context); // set the context to the currently active one
}
// open the sound file
NSString *soundFilePath = [[NSBundle mainBundle] pathForResource:@"TempoClick" ofType:@"caf"];
NSURL *soundFileURL = [NSURL fileURLWithPath:soundFilePath];
AudioFileID fileID;
result = AudioFileOpenURL((CFURLRef)soundFileURL, kAudioFileReadPermission, 0, &fileID);
if (result != 0) DLog(@"cannot open file %@: %ld", soundFilePath, result);
// get the size of the file data
UInt32 fileSize = 0;
UInt32 propSize = sizeof(UInt64);
result = AudioFileGetProperty(fileID, kAudioFilePropertyAudioDataByteCount, &propSize, &fileSize);
if (result != 0) DLog(@"cannot find file size: %ld", result);
DLog(@"file size: %li", fileSize);
// copy the data into a buffer, then close the file
unsigned char *outData = malloc(fileSize);
AudioFileOpenURL((CFURLRef)soundFileURL, kAudioFileReadPermission, 0, &fileID); // we get a "file is not open" error on the next line if we don't open this again
result = AudioFileReadBytes(fileID, false, 0, &fileSize, outData);
if (result != 0) NSLog(@"cannot load data: %ld", result);
AudioFileClose(fileID);
alGenBuffers(1, &tempoSoundBuffer);
alBufferData(self.tempoSoundBuffer, AL_FORMAT_MONO16, outData, fileSize, 44100);
free(outData);
outData = NULL;
// connect the buffer to the source and set some preferences
alGenSources(1, &tempoSoundSource);
alSourcei(tempoSoundSource, AL_BUFFER, tempoSoundBuffer);
alSourcef(tempoSoundSource, AL_PITCH, 1.0f);
alSourcef(tempoSoundSource, AL_GAIN, 1.0f);
alSourcei(tempoSoundSource, AL_LOOPING, AL_FALSE);
そして、私はちょうど呼び出しplayメソッドで:
alSourcePlay(self.tempoSoundSource);
誰が何であるかを説明することができますここで起こって、私はそれを回避する方法は?
UPDATE 1:
私は、クリック音ごとに400ミリ秒を再生するには、そのプロジェクトにタイマーを追加した簡単なテストように私は、オーディオユニットと簡単なサウンドを再生別のプロジェクトを持っています。その場合、タイミングはほぼ完璧です。ですから、NSTimerはうまくいきますが、システムサウンド、AVAudioPlayerとOpenALはオーディオユニットよりも再生の精度が劣るようです。
UPDATE 2:
私はオーディオユニットを使用するように私のプロジェクトを再加工し、現在のオーディオは、はるかに正確に再生されます。どちらの方向にも4ミリ秒まで変動することがありますが、これは他のオーディオ方法よりも優れています。私はまだ興味があるのですが、他の方法はすべて短く、短い、短い、長い間隔のパターンを示しています - オーディオ再生時間が何らかの種類のフレームレートにマップされるように切り上げられているようですそれを説明したり、他のオーディオメソッドの回避策を提供できる人なら誰でもこの質問を開いておくことができます。
あなたのNSLogが遅れを引き起こしているかもしれませんが、それがあれば – Ahmed
@Ahmed:それは良い考えですが、私はNSLogをコメントアウトして同じ結果を得ました。 – arlomedia
[iPhoneでリアルタイムの正確なオーディオシーケンサーをプログラミングするにはどうすればいいですか?](http://stackoverflow.com/questions/907137/how-to-program-a-real-time-accurate-audio-sequencer -on-the-iphone) – benzado