我正在开发一款具有琶音/排序功能的音乐应用,需要很高的定时精度。目前,使用“Timer”我已经达到了平均抖动约为5ms的精度,但最大抖动约为11ms,这对于8号,16号音符的快速琶音来说是不可接受的。特别是第32条。
我已经读过'CADisplayLink'比'Timer'更准确,但由于它的准确度(~16-17ms)限制在1/60秒,所以它看起来不太准确比我Timer
所取得的成就。
潜入CoreAudio是实现我想要的唯一途径吗?还有其他方法可以实现更准确的计时吗?
答案 0 :(得分:5)
我在iPhone 7上对Timer
和DispatchSourceTimer
(又名GCD计时器)进行了一些测试,其中包含1000个数据点,间隔为0.05秒。我期待GCD计时器明显更准确(假设它有一个专用队列),但我发现它们是可比较的,我的各种试验的标准偏差范围从0.2-0.8毫秒,最大偏差平均值约为2 -8毫秒。
按照Technical Note TN2169: High Precision Timers in iOS / OS X中的说明尝试mach_wait_until
时,我获得的计时器大约是我使用Timer
或GCD计时器获得的计时器的4倍。
话虽如此,我并不完全相信mach_wait_until
是最好的方法,因为thread_policy_set
的具体政策价值的确定似乎记录不清。但下面的代码反映了我在测试中使用的值,使用的代码改编自How to set realtime thread in Swift?和TN2169:
var timebaseInfo = mach_timebase_info_data_t()
func configureThread() {
mach_timebase_info(&timebaseInfo)
let clock2abs = Double(timebaseInfo.denom) / Double(timebaseInfo.numer) * Double(NSEC_PER_SEC)
let period = UInt32(0.00 * clock2abs)
let computation = UInt32(0.03 * clock2abs) // 30 ms of work
let constraint = UInt32(0.05 * clock2abs)
let THREAD_TIME_CONSTRAINT_POLICY_COUNT = mach_msg_type_number_t(MemoryLayout<thread_time_constraint_policy>.size / MemoryLayout<integer_t>.size)
var policy = thread_time_constraint_policy()
var ret: Int32
let thread: thread_port_t = pthread_mach_thread_np(pthread_self())
policy.period = period
policy.computation = computation
policy.constraint = constraint
policy.preemptible = 0
ret = withUnsafeMutablePointer(to: &policy) {
$0.withMemoryRebound(to: integer_t.self, capacity: Int(THREAD_TIME_CONSTRAINT_POLICY_COUNT)) {
thread_policy_set(thread, UInt32(THREAD_TIME_CONSTRAINT_POLICY), $0, THREAD_TIME_CONSTRAINT_POLICY_COUNT)
}
}
if ret != KERN_SUCCESS {
mach_error("thread_policy_set:", ret)
exit(1)
}
}
然后我可以这样做:
private func nanosToAbs(_ nanos: UInt64) -> UInt64 {
return nanos * UInt64(timebaseInfo.denom) / UInt64(timebaseInfo.numer)
}
private func startMachTimer() {
Thread.detachNewThread {
autoreleasepool {
self.configureThread()
var when = mach_absolute_time()
for _ in 0 ..< maxCount {
when += self.nanosToAbs(UInt64(0.05 * Double(NSEC_PER_SEC)))
mach_wait_until(when)
// do something
}
}
}
}
注意,您可能希望查看when
是否尚未通过(如果您的处理无法在指定的时间内完成,您希望确保您的计时器不会被积压),但是希望这说明了这个想法。
无论如何,使用mach_wait_until
,我获得了比Timer
或GCD计时器更高的保真度,代价为What are the do's and dont's of code running with high precision timers?
我很欣赏你对这一点的怀疑,但我怀疑潜入CoreAudio是否明智,看看它是否能提供更强大的解决方案。
答案 1 :(得分:3)
对于可接受的音乐准确节奏,唯一合适的定时源是使用Core Audio或AVFoundation。
答案 2 :(得分:0)
我自己正在开发音序器应用程序,因此我强烈建议将AudioKit用于这些目的。 它具有自己的音序器类。 https://audiokit.io/