2017-03-02 61 views
0

對於this project我設計了一個音序器/ drummachine,它應該能夠以精確的速度發送MIDI音符。示例:每2秒鐘16個音符(即,在音樂術語中,在BPM 120處每十個音符16個1/16音符),即每125毫秒一個音符。Python中的精確循環時序

我在想:

import time 

def midi_note_send(...): 
    .... 

while True: 
    midi_note_send(...) 
    time.sleep(0.125) 

如果我不喜歡這個,怎麼肯定這將是完全125毫秒? 這個循環的1000次迭代是否會有126秒而不是125秒的風險?如果是這樣,如何有一個更精確的循環?

最後注意:一個好的鼓機應該能夠在1小時內保持120 BPM的節奏,精度誤差爲< 1秒。
使用平臺:Linux + RaspberryPi但這個問題一般是有效的。

+1

壞消息:[時間文檔](https://docs.python.org/2/library/time.html#time。睡眠)基本上不保證睡眠實際需要多長時間。它可以小於或大於要求的時間,任意數量。就你所知,睡眠(0.125)可能需要0.0001秒或一百萬年。 (授予,這些都不太可能,但...) – Kevin

+1

你可能想看看[這裏](http://stackoverflow.com/questions/40496780/how-to-make-while-loops-take-a- set-amount-of-time/40496844#40496844),在那裏我給了一種方法來阻止漂流。 – TemporalWolf

+0

@Kevin哇,我從來沒有讀過'time.sleep'的文檔字符串。 [在3.5中修改](https://docs.python.org/3.6/library/time.html#time.sleep)至少睡眠指定的時間*。 –

回答

1

您可以使用絕對時間(從time.time())來計算您的睡眠時間。

starttime = time.time() 
for i in range(100): 
    midi_note_send(...) 
    sleep_duration = (i + 1) * 0.125 - time.time() + starttime 
    time.sleep(sleep_duration) 
1

最起碼,你應該考慮的midi_note_send

import time 

# Define a generator for timing 
def next_time(t0, dt): 
    while 1: 
     t0 += dt 
     yield t0 

# Initialize timer and start loop 
timer = next_time(time.time(), 0.125) 
while True: 
    midi_note_send(...) 
    time.sleep(next(timer) - time.time()) 
0

這裏的計算時間是應儘可能準確保持跳動頻率爲您的計算機的時鐘允許,只要你替代想。它是在個別節拍是可達1ms關閉,這可能是更壞的費用,但是這僅僅是證明可供選擇:

# Time between beats 
beat_length = 0.125 

# Send first beat and initialize next beat time 
midi_note_send() 
next_beat = time.time() + beat_length 

while True: 
    # Poll the time in 1ms increments until enough time has elapsed 
    while time.time() < next_beat: 
     time.sleep(0.001) 
    # Send the next note 
    midi_note_send() 
    # Increment the next beat time 
    next_beat += beat_length 

您可以通過改變睡眠時間增加獨立節拍的準確度(以0.0001,例如精確度爲0.1ms),但是會在更頻繁地輪詢時間的情況下以CPU使用爲代價。

0

擴大其他答案,所有建議考慮到處理您的筆記所需的時間,這裏是一個速率限制器,可以很容易地建成一個班級。 如果所需的速率非常接近處理函數的運行時間,它也可以避免嘗試睡眠。

def rate_limit(rate, previous=0): 
    duration = time.time() - previous 
    if duration > 0: 
     sleep_time = 1.0/rate - duration 
     if sleep_time > 0.0: 
     time.sleep(sleep_time) 
    return time.time() 

previous = 0 
for i in range(120): 
    midi_note_send(...) 
    previous = rate_limit(120, previous) 
2

正如我表明here

import time 
def drummer(): 
    counter = 0 
    # time.sleep(time.time() * 8 % 1/8) # enable to sync clock for demo 
    while counter < 60 * 8: 
     counter += 1 
     print time.time() 
     time.sleep(.125 - time.time() * 8 % 1/8) 

此計時器調整每一個節拍和重新調整。

和調節需要幾乎沒有時間:

timeit.timeit("time.time() * 8 % 1/8", number=1000000) 
0.2493131160736084 

這意味着每它,它需要約0.25微秒

和精度時間:

1488490895.000160 
1488490895.125177 
1488490895.250167 
1488490895.375151 
1488490895.500166 
1488490895.625179 
1488490895.750178 
1488490895.875153 

〜28微秒筆記之間的漂移。長時間在本地運行會產生〜130μs的總漂移(+ - 65μs),但是,由於它每次同步到時鐘,它不會隨時間而偏離。