В одном из моих приложений есть простая функция в стиле метронома, которая воспроизводит звук щелчка указанное количество раз в минуту (уд / мин). Я делаю это, запуская NSTimer с интервалом, вычисленным из указанного числа ударов в минуту, который вызывает метод, воспроизводящий звук.
Если я добавлю строку NSLog в метод воспроизведения, я вижу, что NSTimer срабатывает с точностью до 1 миллисекунды. Однако, если я записываю вывод звука в аудиоредактор, а затем измеряю интервал между щелчками, я вижу, что они не расположены равномерно. Например, при скорости 150 ударов в минуту таймер срабатывает каждые 400 миллисекунд. Но большинство звуков воспроизводится через 395 миллисекунд, а каждый третий или четвертый звук воспроизводится через 418 миллисекунд.
Таким образом, звуки не имеют одинаковой задержки, а наоборот, они следуют схеме с более короткими и длинными интервалами. Похоже, что iOS имеет более низкое разрешение для синхронизации звуков и округляет каждое звуковое событие до ближайшей доступной точки, округляя вверх или вниз по мере необходимости, чтобы не отставать от общего пути.
Я пробовал это с системными звуками, AVAudioPlayer и OpenAL и получил те же самые результаты со всеми тремя методами. При каждом методе я выполняю все настройки при загрузке представления, поэтому каждый раз, когда я воспроизводю звук, все, что мне нужно сделать, это воспроизвести его. С AVAudioPlayer я пытался вызывать prepareToPlay, используя второй таймер после каждого воспроизведения звука, поэтому он инициализируется и готов к работе в следующий раз, но получил те же результаты.
Вот код для настройки звука OpenAL в viewDidLoad (адаптировано из этого урока ):
// 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);
А потом в методе воспроизведения я просто вызываю:
alSourcePlay(self.tempoSoundSource);
Может кто-нибудь объяснить, что здесь происходит, и как я могу обойти это?
ОБНОВЛЕНИЕ 1:
У меня есть другой проект, который воспроизводит короткие звуки с аудиоустройствами, поэтому в качестве быстрого теста я добавил в этот проект таймер для воспроизведения звука щелчка каждые 400 миллисекунд. В этом случае время почти идеально. Итак, кажется, что NSTimer в порядке, но системные звуки, AVAudioPlayer и OpenAL менее точны в своем воспроизведении, чем аудиоустройства.
ОБНОВЛЕНИЕ 2:
Я только что переработал свой проект, чтобы использовать аудиоустройства, и теперь звук воспроизводится гораздо точнее. Это все еще иногда дрейфует до четырех миллисекунд в любом направлении, но это лучше, чем другие аудио методы. Мне все еще любопытно, почему все другие методы показывают паттерн коротких, коротких, коротких, длинных интервалов - это как время воспроизведения звука округляется в большую или меньшую сторону, чтобы отобразить какую-то частоту кадров - поэтому я оставьте этот вопрос открытым для тех, кто может объяснить это и / или предложить обходной путь для других аудио методов.