Python中的精确循环时序

时间:2017-03-02 20:27:27

标签: python time timer

对于this project我正在设计一个音序器/鼓机,它应该能够以精确的速度发送MIDI音符。示例:每2秒16个音符(即在音乐术语中,BPM 120每条16个1/16音符),即每125毫秒一个音符。

我正在考虑:

import time

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

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

如果我喜欢这样,那么如何确定它将是125毫秒? 是否存在1000次循环使用126秒而不是125秒的风险?如果是这样,如何有一个更精确的循环?

最后注意事项:一台好的鼓机应该能够在1小时内保持120 BPM的节奏,精度误差<1。 1秒。
二手平台:Linux + RaspberryPi,但这个问题一般都有效。

5 个答案:

答案 0 :(得分: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),但是,由于它与每个节拍的时钟同步,因此不会随时间偏离。

答案 1 :(得分: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)

答案 2 :(得分: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())

答案 3 :(得分:0)

这是一种替代方案,可以像您的计算机时钟一样准确地保持拍频,只要您愿意。它的代价是单个节拍最多可达1毫秒,这可能更糟糕,但这仅仅是为了证明这一选择:

# 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.1毫秒),但会更频繁地轮询时间,这会牺牲CPU使用率。

答案 4 :(得分: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)