I am looking for this for weeks now: My app needs a metronome like function and I did it with a thread (copied and adapted from Apples Metronom example) but the timing is miserable. I know that the timer ressources are not accurate but I cannot find another feaseable technique. And I need just short "beep" sounds so tackling the full Audio playback stuff is not required. I divide the BPM (beats per minute) by 12 to have the possibillity to play also the eigth, sixteenth and so on notes. So the problem is not the audio play, the problem is the accurate time distance between two "ticks".
Here the Code for calculation the tick distance:
- (double)Be_get12TickDistance:(NSInteger)bpm{
double result = 60.0 / (bpm*12);
return result;
} Here the thread:
- (void)startDriverTimer:(id)info {
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
// Give sound thread 开发者_如何学JAVAhigh 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];
}
Question: Does anybody has a hint how to do that in an easy way? I just need a better timing possibillity, the playing with SoundEffect is good enough.
Thanks for all help, Andreas
Simplest solution, and one which is quite accurate, is to just use an NSTimer
. I created a simple demo app to test it, and it was accurate to a thousandth of a millisecond (I just NSLog
ged every time a timer ticked).
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
This is output from an iPod Touch 2nd Gen device. (Note that accuracy will decrease if you're doing a lot of hard processing in the meantime, but I don't imagine a metronome will have that much else going on). The timer came from the very simple code:
[NSTimer scheduledTimerWithTimeInterval:.1 target:self selector:@selector(tick) userInfo:nil repeats:YES];
But my guess is that it is in fact the sound code that's causing the problem. What are you using to play the sounds? If it's anything but OpenAL, you can't expect it to be very accurate -- it can pause before playback, after playback, etc. Try to get a test app and see if the timer is ticking at the correct rate without the sound and with the sound to identify the cause of the problem.
One strategy that will work is to have a background thread that sleeps for a fixed amount of time (say, every 1/16th of a second). This uses the more accurate CPU clock, so when it returns from the call, you'll be much more accurate.
NSTimer timer = [NSTimer scheduledTimerWithTimeInterval:0.1 target:self selector:@selector(YOUR_METHOD:) userInfo:nil repeats:YES];
Replace scheduledTimerWithTimeInterval:0.1 with whatever interval (in seconds) you want, and replace YOUR_METHOD with whatever your method is.
精彩评论