Python da aniq aylanish vaqti

ushbu loyiha uchun men navbati bilan MIDI eslatmalarini aniq temp bilan yuborish imkoniga ega sequencer/drummachine loyihasini tayyorlayapman. Misol: 2 soniya davomida 16 ta qayd (ya'ni musiqiy terminologiyada BPM 120da barda o'n olti16/16-eslatma), ya'ni har bir 125 milisaniyadagi bir eslatma.

Men o'ylayman:

import time

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

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

Agar shunday qilsam, bu to'liq 125 millisekundga teng bo'lishiga qanday ishonch hosil qilish kerak? Ushbu loopning 1000 ta o'qilishi 125 sekund o'rniga 126 soniyani ishlatish xavfi mavjud emasmi? Agar shunday bo'lsa, qanday aniqroq loop bo'lishi mumkin?

Final note: a good drummachine should be able to keep a 120 BPM rythm during 1 hour, with a precision error < 1 second.
Used platform: Linux + RaspberryPi but this question is valid in general.

1
Yomon yangiliklar: vaqt hujjatlari asosan qanday qilib zero uzoq vaqt uyqu oladi. So'ralgan vaqtdan kam yoki undan ko'p bo'lishi mumkin, bu o'zboshimchalik bilan. Bilasizmi, uyqu (0,125) 0,0001 soniya yoki million yil olishi mumkin. (berilgan, ularning ikkalasi ham ehtimol emas, lekin ...)
qo'shib qo'ydi muallif Kevin, manba
@Kevin Vau, oldin time.sleep uchun docstringni hech qachon o'qimaganman. 3.5 da da uxlash uchun o'zgartirilgan. kamida belgilangan vaqt miqdori.
qo'shib qo'ydi muallif Chris Mueller, manba

6 javoblar

As I showed 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)

Ushbu taymer har bir zarbani to'g'rilaydi va to'g'rilaydi.

Va tuzatish deyarli vaqtni talab qilmaydi:

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

Bu esa har gal taxminan 0.25 mikrosaniyani oladi degan ma'noni anglatadi

va aniqlik uchun:

1488490895.000160
1488490895.125177
1488490895.250167
1488490895.375151
1488490895.500166
1488490895.625179
1488490895.750178
1488490895.875153

Eslatmalar orasida ~ 28 mikrosaniyadan iborat. Mahalliy sifatida uzoq vaqt davomida ishlating ~ ~ 130sm umumiy drift (+ - 65sks) hosil qiladi, lekin soatni har bir urish bilan sinxronlashtirsa, u vaqt o'tishi bilan farq qilmaydi.

2
qo'shib qo'ydi

As I showed 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)

Ushbu taymer har bir zarbani to'g'rilaydi va to'g'rilaydi.

Va tuzatish deyarli vaqtni talab qilmaydi:

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

Bu esa har gal taxminan 0.25 mikrosaniyani oladi degan ma'noni anglatadi

va aniqlik uchun:

1488490895.000160
1488490895.125177
1488490895.250167
1488490895.375151
1488490895.500166
1488490895.625179
1488490895.750178
1488490895.875153

Eslatmalar orasida ~ 28 mikrosaniyadan iborat. Mahalliy sifatida uzoq vaqt davomida ishlating ~ ~ 130sm umumiy drift (+ - 65sks) hosil qiladi, lekin soatni har bir urish bilan sinxronlashtirsa, u vaqt o'tishi bilan farq qilmaydi.

2
qo'shib qo'ydi

Kutish vaqtini hisoblash uchun mutloq vaqtni ( time.time() dan foydalanishingiz mumkin).

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
qo'shib qo'ydi

At the very least, you should account for the computation time of 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())
1
qo'shib qo'ydi

Sizningcha, xohlagan vaqtingizda, chastotani chastotaga mos ravishda saqlab turishingiz kerak. Bu 1ms ga qadar bo'lgan shaxsiy jarohatlardan kelib chiqadi, bu ehtimol yomonroq, lekin bu faqat alternativni ko'rsatishdir:

# 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

Vaqtni tez-tez so'roq qilishingiz bilan CPU foydalanish hisobiga uyqu vaqtini o'zgartirish (masalan, 0.1 ms aniqlik uchun <00001/ <00001 ) ni o'zgartirib, individual kaltakning aniqligini oshirishingiz mumkin.

0
qo'shib qo'ydi

O'zingizning eskizingizni qayta ishlash muddatini inobatga olgan holda boshqa barcha javoblarni kengaytirish, bu erda sinfga osongina o'rnatilishi mumkin bo'lgan tezlikni sınırlayıcıdır. Bundan tashqari, talab qilingan ishlov berish funktsiyasi o'tgan vaqtga juda yaqin bo'lsa, uxlashga harakat qiladi.

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)
0
qo'shib qo'ydi
Python
Python
372 ishtirokchilar

Bu guruh python dasturlash tilini muhokama qilish uchun. Iltimos, o'zingizni hurmat qiling va faqat dasturlash bo'yicha yozing. Botlar mavzusini @botlarhaqida guruhida muhokama qling! FAQ: @PyFAQ Offtopic: @python_uz_offtopic

Python offtopic group !
Python offtopic group !
150 ishtirokchilar

@python_uz gruppasining offtop gruppasi. offtop bo'lsa ham reklama mumkin emas ) Boshqa dasturlash tiliga oid gruppalar @languages_programming