对于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,但这个问题一般都有效。
答案 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)