iOS中的准确计时 [英] Accurate timing in iOS

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

问题描述

我正在查看 iOS SDK 中的节拍器"示例代码 (http://developer.apple.com/library/ios/#samplecode/Metronome/Introduction/Intro.html).我正在以 60 BPM 运行节拍器,这意味着每秒一个滴答声.当我查看外部手表(PC 的手表)时,我发现节拍器运行速度太慢 - 每分钟错过大约一个节拍,这就是 app.15 毫秒的一致错误.相关的代码是:

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.

推荐答案

好的,我做了一些测试后有了一些答案,所以我和有兴趣的人分享.

Ok, I have some answers after doing some more tests, so I am sharing it with anyone who is interested.

我在 play 方法(实际将 play 消息发送到 AVAudioPlayer 对象),正如我简单的与外部观察的比较实验所显示的,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 秒间隔后会消耗一些开销时间,并且在几十秒后,额外的时间(大约 10 毫秒)累积到相当可观的数量——这对节拍器来说非常糟糕.因此,我决定测量从第一次调用开始的 间隔,而不是测量 调用之间的间隔,这样错误就不会累积.换句话说,我已经替换了这个条件:

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).

我很高兴听到更多关于是什么导致额外时间流逝的见解——对于现代 CPU 来说,10 毫秒听起来像是永恒——尽管我不熟悉 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天全站免登陆