Точный таймер хотел! - PullRequest
0 голосов
/ 08 февраля 2010

Я ищу это уже несколько недель: Моему приложению нужна функция, похожая на метроном, и я сделал это с помощью потока (скопировано и адаптировано из примера Apples Metronom), но время выполнения жалкое. Я знаю, что ресурсы таймера не точны, но я не могу найти другую выполнимую технику. И мне нужны только короткие звуковые сигналы, чтобы не требовалось полное воспроизведение звука. Я делю BPM (ударов в минуту) на 12, чтобы иметь возможность играть также восьмые, шестнадцатые и т. Д. Ноты. Таким образом, проблема не в воспроизведении аудио, а в проблеме точного временного расстояния между двумя «галочками».

Вот код для расчета расстояния тика:

- (double)Be_get12TickDistance:(NSInteger)bpm{
double result = 60.0 / (bpm*12);
return result;

} Вот нить:

- (void)startDriverTimer:(id)info {
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];

// Give sound thread high priority to keep the timing steady
[NSThread setThreadPriority:1.0];
BOOL continuePlaying = YES;

while (continuePlaying) {  // loop until cancelled
    // Use an autorelease pool to prevent the build-up of temporary date objects
    NSAutoreleasePool *loopPool = [[NSAutoreleasePool alloc] init]; 

     SoundEffect *currentSoundEffect= [[self beepAndLEDKeeper] Be_GetBeepSound4CurrentTick:beatNumber theCurrentGroove:grooveNumber];
     [currentSoundEffect play];

     if ([[self beepAndLEDKeeper] Be_isLEDFlashPoint:beatNumber]){
         [self performSelectorOnMainThread:@ selector(setLEDtoHidden:) withObject:[NSNumber numberWithInt:LEDoldBeatNumber+oldGrooveNumber*9] waitUntilDone:NO];
         [self performSelectorOnMainThread:@ selector(setLEDtoUnHidden:) withObject:[NSNumber numberWithInt:LEDbeatNumber+grooveNumber*9] waitUntilDone:NO];
         LEDoldBeatNumber = LEDbeatNumber++;
     }

     oldBeatNumber = beatNumber++;
     oldGrooveNumber = grooveNumber;

     if (beatNumber >= grooveEnd[grooveNumber]) 
     {
         beatNumber=0;
         LEDbeatNumber=0;
         if (++grooveNumber > 1)
             grooveNumber=0;
         if (grooveEnd[grooveNumber] == 0)
             grooveNumber=0;
     }

     if ([soundPlayerThread isCancelled] == YES)
         continuePlaying = NO;
     else
         [NSThread sleepForTimeInterval:self.duration];

     [loopPool release];
}
[pool release];

}

Вопрос: У кого-нибудь есть подсказка, как это сделать легко? Мне просто нужна лучшая возможность синхронизации, игра с SoundEffect достаточно хороша.

Спасибо за помощь, Andreas

Ответы [ 3 ]

1 голос
/ 08 февраля 2010

Самое простое и достаточно точное решение - просто использовать NSTimer. Я создал простое демонстрационное приложение, чтобы протестировать его, и оно было с точностью до тысячной доли миллисекунды (я просто NSLog гед каждый раз, когда тикал таймер).

2010-02-07 17:11:07.548 TimerTest[12775:207] Ticked
2010-02-07 17:11:07.648 TimerTest[12775:207] Ticked
2010-02-07 17:11:07.748 TimerTest[12775:207] Ticked
2010-02-07 17:11:07.848 TimerTest[12775:207] Ticked
2010-02-07 17:11:07.948 TimerTest[12775:207] Ticked
2010-02-07 17:11:08.048 TimerTest[12775:207] Ticked
2010-02-07 17:11:08.148 TimerTest[12775:207] Ticked

Это вывод с устройства iPod Touch 2nd Gen. (Обратите внимание, что точность будет снижаться, если вы тем временем выполняете сложную обработку, но я не думаю, что у метронома будет так много всего происходящего). Таймер пришел из очень простого кода:

[NSTimer scheduledTimerWithTimeInterval:.1 target:self selector:@selector(tick) userInfo:nil repeats:YES];

Но я полагаю, что именно звуковой код вызывает проблему. Что вы используете для воспроизведения звуков? Если это что-то кроме OpenAL, вы не можете ожидать, что он будет очень точным - он может приостановиться перед воспроизведением, после воспроизведения и т. Д. Попробуйте найти тестовое приложение и посмотрите, работает ли таймер с правильной скоростью без звука и со звуком, чтобы определить причину проблемы.

0 голосов
/ 08 февраля 2010
NSTimer timer = [NSTimer scheduledTimerWithTimeInterval:0.1 target:self selector:@selector(YOUR_METHOD:) userInfo:nil repeats:YES];

Замените scheduleTimerWithTimeInterval: 0.1 с любым интервалом (в секундах), который вы хотите, и замените YOUR_METHOD тем, какой у вас метод.

0 голосов
/ 08 февраля 2010

Одна стратегия, которая будет работать, - это иметь фоновый поток, который спит в течение фиксированного промежутка времени (скажем, каждую 1/16 секунды).При этом используются более точные тактовые частоты процессора, поэтому при возврате из вызова вы будете намного точнее.

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