Точные повторяющиеся звуки iPhone - PullRequest
1 голос
/ 22 августа 2011

Я работаю с приложением, которое должно воспроизводить звук «тап» с интервалом 60 мс (что составляет 1000 ударов в минуту). Когда я преодолеваю интервалы примерно в 120 мс (500 ударов в минуту), звуки становятся очень хаотичными и накапливаются друг с другом.

Используемый мной звуковой байт имеет длину 10 мс, и у меня есть таймер потока, который работает с интервалами в 20 мс. Я использую AVAudioPlayer для воспроизведения звука и пробовал форматы wav, caf и m4a. Я проверил с выходами NSLog, чтобы увидеть, когда запускается AVAudioPlayer, и когда цикл таймера опрашивает ... и все время кажется достаточно точным.

Я добавил обработку аудио-сеанса в метод viewDidLoad () следующим образом:

 [[AVAudioSession sharedInstance]
 setCategory: AVAudioSessionCategoryPlayback
 error: &setCategoryError];

Вот мой цикл таймера, который работает в своем собственном потоке:

NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; 

// Give the sound thread high priority to keep the timing steady.
[NSThread setThreadPriority:1.0];

while (running) {     // loop until stopped. 
    [self performSelectorOnMainThread:@selector(playOutput) withObject:nil waitUntilDone:NO];

    // An autorelease pool to prevent the build-up of temporary objects.
    NSAutoreleasePool *loopPool = [[NSAutoreleasePool alloc] init];

    NSDate *curtainTime = [[NSDate alloc] initWithTimeIntervalSinceNow:sleepValue];
    NSDate *currentTime = [[NSDate alloc] init];
    // Wake up periodically to see if we've been cancelled. 
    while (running && ([currentTime compare:curtainTime] == NSOrderedAscending)) { 
        [NSThread sleepForTimeInterval:0.015];
        [currentTime release];
        currentTime = [[NSDate alloc] init];
    }

    [curtainTime release]; 
    [currentTime release]; 
    [loopPool drain];
}
[pool drain];

Вот как я инициализирую мой AVAudioPlayer:

NSString *beatOne = @"3h";
NSString *beatOneSoundFile = [[NSBundle mainBundle]
                              pathForResource:beatOne ofType:@"caf"];
NSURL *beatOneURL = [[NSURL alloc] initFileURLWithPath:beatOneSoundFile];
beatPlayerOne = [[AVAudioPlayer alloc]initWithContentsOfURL:beatOneURL error:nil];
beatPlayerOne.delegate = self;
beatPlayerOne.currentTime = 0;
[beatPlayerOne prepareToPlay];
[beatOneURL release];

А вот где воспроизводится аудиоплеер:

[beatPlayerOne pause];
beatPlayerOne.currentTime = 0;
[beatPlayerOne play];

Заранее спасибо за любую помощь ...

Ответы [ 3 ]

2 голосов
/ 24 августа 2011

NSTimer (и, соответственно, NSThread sleep) не являются надежными механизмами «синхронизации по времени». Если что-то блокирует основной поток, цикл выполнения не завершится, пока не разблокируется, и таймеры и спящие потоки не будут вызваны вовремя.

Кроме того, разрешение по NSTimer составляет ЛУЧШЕЕ около 1/30 секунды, хотя фреймворк не дает никаких обещаний относительно регулярности его запуска. Как вы видели, это не регулярно. Если ваше почтовое приложение запускается в фоновом режиме, оно, скорее всего, выбросит все ваши модные гудящие аудиосигналы из строя.

Думаю, вам лучше всего создавать более длинные звуковые файлы, содержащие различные скорости щелчков, и запускать их реже.

0 голосов
/ 24 августа 2011

Как сказал Дэн Рэй, NSTimer и NSThread не надежны для точного определения времени.На вашем месте я бы использовал аудио-очередь - она ​​будет запрашивать буферы аудио через обратный вызов ввода, и вы будете заполнять их.Если вы ответите быстро, он запросит и воспроизведет эти буферы на точно аппаратной частоте дискретизации, поэтому вы можете сделать несколько простых вычислений, чтобы выяснить время ваших нажатий и заполнить каждый буфер из вашего файла или из выборок.хранится в массиве или векторе.Вы бы заполнили его, используя AudioFileReadPackets, если решите перейти прямо из своего файла.

Это будет немного больше работы, но гораздо более универсальным и мощным.Вы можете использовать аудио-очереди для живого синтеза и обработки аудио, поэтому их время будет абсолютно точным, если вы соблюдаете их сроки.Если вы внимательно относитесь к использованию процессора, вы можете прослушивать звук каждые несколько миллисекунд без проблем.

Посмотрите пример приложения SpeakHere , чтобы начать работу с аудио-очередями. Аудиоустройства также полезны для такого рода вещей, и вы можете предоставить им данные с помощью обратного вызова, но они требуют немного больше настроек и имеют более крутой курс обучения.

Удачи!

0 голосов
/ 23 августа 2011

Вы всегда должны избегать [NSThread Sleep], насколько это возможно. Вместо этого используйте NSTimer:

[NSTimer scheduledTimerWithTimeInterval:(0.015) target:self selector:@selector(timerfn) userInfo:nil repeats:NO];
//the @selector(timerfn) is which function it calls

вызовите этот таймер в функции, которую он вызывает, если вы хотите продолжить воспроизведение звука

OR

сохранить таймер в переменную следующим образом:

 mytimer=[NSTimer scheduledTimerWithTimeInterval:(0.015) target:self selector:@selector(timerfn) userInfo:nil repeats:YES];

и использование:

 [mytimer invalidate];
    mytimer=nil;

чтобы убить его.

Тогда просто играйте звук в функции. Вы также можете использовать [NSTimer alloc] и [mytimer release], я думаю ...

Кроме того, 66,6 вызовов в секунду, кажется, несколько излишним.

...