在iOS中精确计时 [英] Accurate timing in iOS

查看:273
本文介绍了在iOS中精确计时的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我在查看iOS SDK中的Metronome示例代码( http ://developer.apple.com/library/ios/#samplecode/Metronome/Introduction/Intro.html )。我以60 BPM运行节拍器,这意味着每秒钟一次。当我看外部手表(PC的手表),我看到节拍器运行太慢 - 它错过了每分钟一个节拍,这是应用程序。 15msec的一致性误差。相关代码片段是:

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];    
}

其中


self.duration

self.duration

在60 BPM的情况下为1.0秒。我不知道这个错误是从哪里来的,如何使一个更精确的计时器/间隔计数器。

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.

编辑:问题存在,当我改变睡眠时间例如.001。

The problem exists as well when I change the sleep time to smaller values, e.g .001.

EDIT2(更新):当我使用 CFAbsoluteTimeGetCurrent()定时方法。当我使用相同的方法来测量按钮点击事件之间的时序时,看起来准确 - 我每秒一次(观看手表),并且测量的速率是60 BPM(平均)。所以我想这一定是一些问题与 NSThread (?)。另一件事是,在设备(iPod)上的问题似乎比模拟器上更严重。

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.

推荐答案

我已经放置了一个变量来测量刻度之间的时间间隔,在 play 方法(将 play 消息发送到 AVAudioPlayer object),而且我的简单的compare-to-external-watch实验显示,60 BPM太慢 - 我有这些时间间隔(以秒为单位):

I've placed a variable to measure time intervals between ticks, inside the play method (the method that actually sends the play message to the AVAudioPlayer object), and as my simple compare-to-external-watch experiment showed, the 60 BPM was too slow - I got these time intervals (in seconds):

1.004915
1.009982
1.010014
1.010013
1.010028
1.010105
1.010095
1.010105

我的结论是每隔1秒间隔计算一些开销时间,在几十秒后累积到一个明显的量 - 对于节拍器是相当糟糕的。因此,不是测量之间的间隔,我决定测量第一次调用的间隔,以便不会累积错误。换句话说,我替换了这个条件:

My conclusion was that some overhead time elapses after each 1-second-interval is counted, and that extra time (about 10msec) is accumulated to a noticeable amount after a few tens of seconds --- quite bad for a metronome. So instead of measuring the interval between calls, I decided to measure the total interval from the first call, so that the error won't be accumulated. In other words I've replaced this condition:

while (continuePlaying && ((currentTime0 + [duration doubleValue]) >= currentTime1)

while (continuePlaying && ((_currentTime0 + _cnt * [duration doubleValue]) >= currentTime1 ))

其中 _currentTime0 _cnt 对不起,如果它是一个c + +术语,我是相当新的Obj-C),前者保存的第一次调用的方法的时间戳,后者是一个 int 计数嘀嗒数(==函数调用)。这导致以下测量的时间间隔:

where now _currentTime0 and _cnt are class members (sorry if it's a c++ jargon, I am quite new to Obj-C), the former holds the time stamp of the first call to the method, and the latter is an int counting number of ticks (==function calls). This resulted in the following measured time intervals:

1.003942
0.999754
0.999959
1.000213
0.999974
0.999451
1.000581
0.999470
1.000370
0.999723
1.000244
1.000222
0.999869

即使没有计算平均值,这些值在1.0第二(平均值接近1.0,至少有一毫秒的精度)。

and it is evident even without calculating the average, that these values fluctuate around 1.0 second (and the average is close to 1.0 with at least a millisecond of accuracy).

我很乐意听到更多关于什么导致额外时间流逝的见解 - 10msec听起来像现代CPU的永恒 - 虽然我不熟悉iPod CPU的规格(它的iPod 4G,维基百科说CUP是PowerVR SGX GPU 535 @ 200 MHz)

I will be happy to hear more insights regarding what causes the extra time to elapse - 10msec sounds as eternity for a modern CPU - though I am not familiar with the specs of the iPod CPU (it's iPod 4G, and Wikipedia says the CUP is PowerVR SGX GPU 535 @ 200 MHz)

这篇关于在iOS中精确计时的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆