Swift 中严格安排的循环时间
Strictly scheduled loop timing in Swift
以非常严格的时间安排重复任务的最佳方法是什么(对于音乐排序来说足够准确可靠)?从 Apple 文档中可以清楚地看出 NSTimer 在这个意义上是不可靠的(即 "A timer is not a real-time mechanism")。我从 AudioKit 的 AKPlaygroundLoop 借用的一种方法似乎在大约 4 毫秒内保持一致(如果不是很准确的话),并且可能是可行的:
class JHLoop: NSObject{
var trigger: Int {
return Int(60 * duration) // 60fps * t in seconds
}
var counter: Int = 0
var duration: Double = 1.0 // in seconds, but actual loop is ~1.017s
var displayLink: CADisplayLink?
weak var delegate: JHLoopDelegate?
init(dur: Double) {
duration = dur
}
func stopLoop() {
displayLink?.invalidate()
}
func startLoop() {
counter = 0
displayLink = CADisplayLink(target: self, selector: "update")
displayLink?.frameInterval = 1
displayLink?.addToRunLoop(NSRunLoop.currentRunLoop(), forMode: NSRunLoopCommonModes)
}
func update() {
if counter < trigger {
counter++
} else {
counter = 0
// execute loop here
NSLog("loop executed")
delegate!.loopBody()
}
}
}
protocol JHLoopDelegate: class {
func loopBody()
}
↑ 代码替换成实际的class我暂时会尝试使用
作为参考,我希望制作一个复节奏鼓音序器,所以一致性是最重要的。我还需要能够流畅地实时修改循环,最好是循环周期。
有更好的方法吗?
你可以尝试使用mach_wait_until() api。这对于高精度计时器来说非常好。我从 here a little. It works fine in mine command line tool project. In below code snippet I changed main()
method from my project to startLoop()
. Also you can see this 更改了苹果示例。
希望对你有帮助。
static const uint64_t NANOS_PER_USEC = 1000ULL;
static const uint64_t NANOS_PER_MILLISEC = 1000ULL * NANOS_PER_USEC;
static const uint64_t NANOS_PER_SEC = 1000ULL * NANOS_PER_MILLISEC;
static mach_timebase_info_data_t timebase_info;
static uint64_t nanos_to_abs(uint64_t nanos) {
return nanos * timebase_info.denom / timebase_info.numer;
}
func startLoop() {
while(true) { //
int64_t nanosec = waitSomeTime(1000); // each second
NSLog(@"%lld", nanosec);
update() // call needed update here
}
}
uint64_t waitSomeTime(int64_t eachMillisec) {
uint64_t start;
uint64_t end;
uint64_t elapsed;
uint64_t elapsedNano;
if ( timebase_info.denom == 0 ) {
(void) mach_timebase_info(&timebase_info);
}
// Start the clock.
start = mach_absolute_time();
mach_wait_until(start + nanos_to_abs(eachMillisec * NANOS_PER_MILLISEC));
// Stop the clock.
end = mach_absolute_time();
// Calculate the duration.
elapsed = end - start;
elapsedNano = elapsed * timebase_info.numer / timebase_info.denom;
return elapsedNano;
}
以非常严格的时间安排重复任务的最佳方法是什么(对于音乐排序来说足够准确可靠)?从 Apple 文档中可以清楚地看出 NSTimer 在这个意义上是不可靠的(即 "A timer is not a real-time mechanism")。我从 AudioKit 的 AKPlaygroundLoop 借用的一种方法似乎在大约 4 毫秒内保持一致(如果不是很准确的话),并且可能是可行的:
class JHLoop: NSObject{
var trigger: Int {
return Int(60 * duration) // 60fps * t in seconds
}
var counter: Int = 0
var duration: Double = 1.0 // in seconds, but actual loop is ~1.017s
var displayLink: CADisplayLink?
weak var delegate: JHLoopDelegate?
init(dur: Double) {
duration = dur
}
func stopLoop() {
displayLink?.invalidate()
}
func startLoop() {
counter = 0
displayLink = CADisplayLink(target: self, selector: "update")
displayLink?.frameInterval = 1
displayLink?.addToRunLoop(NSRunLoop.currentRunLoop(), forMode: NSRunLoopCommonModes)
}
func update() {
if counter < trigger {
counter++
} else {
counter = 0
// execute loop here
NSLog("loop executed")
delegate!.loopBody()
}
}
}
protocol JHLoopDelegate: class {
func loopBody()
}
↑ 代码替换成实际的class我暂时会尝试使用
作为参考,我希望制作一个复节奏鼓音序器,所以一致性是最重要的。我还需要能够流畅地实时修改循环,最好是循环周期。
有更好的方法吗?
你可以尝试使用mach_wait_until() api。这对于高精度计时器来说非常好。我从 here a little. It works fine in mine command line tool project. In below code snippet I changed main()
method from my project to startLoop()
. Also you can see this 更改了苹果示例。
希望对你有帮助。
static const uint64_t NANOS_PER_USEC = 1000ULL;
static const uint64_t NANOS_PER_MILLISEC = 1000ULL * NANOS_PER_USEC;
static const uint64_t NANOS_PER_SEC = 1000ULL * NANOS_PER_MILLISEC;
static mach_timebase_info_data_t timebase_info;
static uint64_t nanos_to_abs(uint64_t nanos) {
return nanos * timebase_info.denom / timebase_info.numer;
}
func startLoop() {
while(true) { //
int64_t nanosec = waitSomeTime(1000); // each second
NSLog(@"%lld", nanosec);
update() // call needed update here
}
}
uint64_t waitSomeTime(int64_t eachMillisec) {
uint64_t start;
uint64_t end;
uint64_t elapsed;
uint64_t elapsedNano;
if ( timebase_info.denom == 0 ) {
(void) mach_timebase_info(&timebase_info);
}
// Start the clock.
start = mach_absolute_time();
mach_wait_until(start + nanos_to_abs(eachMillisec * NANOS_PER_MILLISEC));
// Stop the clock.
end = mach_absolute_time();
// Calculate the duration.
elapsed = end - start;
elapsedNano = elapsed * timebase_info.numer / timebase_info.denom;
return elapsedNano;
}