2016-03-02 3 views
3

Каков наилучший способ запланировать повторную задачу с очень строгим сроком (точный и надежный для музыкального секвенирования)? Из документов Apple ясно, что NSTimer не является надежным в этом смысле (т. Е. «Таймер не является механизмом реального времени»). Подход, который я позаимствовал из 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() 
}  

(Для справки, я надеюсь сделать многоритмичные барабан секвенсор таким образом, согласованность является самым важным , Я также должен иметь возможность плавно модифицировать цикл, а в идеале - период цикла, в реальном времени)

Есть ли лучший способ сделать это? Любое руководство будет высоко оценено.

(Edit: Заменены код с фактическим классом я буду стараться использовать в настоящее время)

ответ

2

Вы можете попробовать использовать mach_wait_until (API). Это очень хорошо для таймера высокой точности. Я немного изменил пример яблока от here. Он отлично работает в моем инструменте командной строки. В приведенном ниже фрагменте кода я изменил метод main() из моего проекта на startLoop(). Также вы можете увидеть 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; 
} 
+0

Это очень полезно, как код, так и особенно ссылка mach_absoloute_time. –