I am looking at the 'Metronome' sample code from the iOS SDK (http://developer.apple.com/library/ios/#samplecode/Metronome/Introduction/Intro.html). I am running the metronome at 60 BPM, which means a tick every second. When I look at an external watch (the PC's watch), I see the metronome is running too slow - it misses about one beat each minute, which is app. 15msec of consistent error. The relevant code piece is:
- (void)startDriverTimer:(id)info {
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
// Give the sound thread high priority to keep the timing steady.
[NSThread setThreadPriority:1.0];
BOOL continuePlaying = YES;
while (continuePlaying) { // Loop until cancelled.
// An autorelease pool to prevent the build-up of temporary objects.
NSAutoreleasePool *loopPool = [[NSAutoreleasePool alloc] init];
[self playSound];
[self performSelectorOnMainThread:@selector(animateArmToOppositeExtreme) withObject:nil waitUntilDone:NO];
NSDate *curtainTime = [[NSDate alloc] initWithTimeIntervalSinceNow:self.duration];
NSDate *currentTime = [[NSDate alloc] init];
// Wake up periodically to see if we've been cancelled.
while (continuePlaying && ([currentTime compare:curtainTime] != NSOrderedDescending)) {
if ([soundPlayerThread isCancelled] == YES) {
continuePlaying = NO;
}
[NSThread sleepForTimeInterval:0.01];
[currentTime release];
currentTime = [[NSDate alloc] init];
}
[curtainTime release];
[currentTime release];
[loopPool drain];
}
[pool drain];
}
Where
self.duration
is 1.0 second in the case of 60 BPM. I wonder where this error comes from, and how can I make a more accurate timer/interval counter.
EDIT: The problem exists as well when I change the sleep time to smaller values, e.g .001.
EDIT2 (update): The problem exists as well when I use the CFAbsoluteTimeGetCurrent()
method for timing. When I use the same method to measure timing between a button tap events, the timing seems accurate - I tap once a second (while watching a watch), and the measured rate is 60 BPM (on average). So I guess it must be some issue with the NSThread
(?). Another thing is that on the device (iPod) the problem seems more severe then on the simulator.
Did you consider using a NSTimer instead of this "strange" while-loop-solution? It seems for me, that you are - not only a bit - overcomplicating very very simple things here ...