2014-06-12 59 views
0

我有一個簡單的節拍器正在運行,並且由於某種原因,當它處於較低的bpm時很好,但在較高的bpms時它不一致並且不穩定。 我不知道發生了什麼事。 我想嘗試使用某些東西來定期運行它。有沒有辦法做到這一點?如何在python中定期運行函數

這裏是我的代碼:

class thalam(): 
    def __init__(self,root,e): 
     self.lag=0.2 
     self.root=root 
     self.count=0 
     self.thread=threading.Thread(target=self.play) 
     self.thread.daemon=True 
     self.tempo=60.0/120 
     self.e=e 
     self.pause=False 
     self.tick=open("tick.wav","rb").read() 
     self.count=0 
     self.next_call = time.time() 
    def play(self): 
     if self.pause: 
      return 
     winsound.PlaySound(self.tick,winsound.SND_MEMORY) 
     self.count+=1 
     if self.count==990: 
      self.thread=threading.Thread(target=self.play) 
      self.thread.daemon=True 
      self.thread.start() 
      return 

     self.next_call+=self.tempo 
     new=threading.Timer(self.next_call-time.time(),self.play) 
     new.daemon=True 
     new.start() 
    def stop(self): 
     self.pause=True 
     winsound.PlaySound(None,winsound.SND_ASYNC) 
    def start(self): 
     self.pause=False 
    def settempo(self,a): 
     self.tempo=a 
class Metronome(Frame): 
    def __init__(self,root): 
     Frame.__init__(self,root) 
     self.first=True 
     self.root=root 
     self.e=Entry(self) 
     self.e.grid(row=0,column=1) 
     self.e.insert(0,"120") 
     self.play=Button(self,text="Play",command=self.tick) 
     self.play.grid(row=1,column=1) 
     self.l=Button(self,text="<",command=lambda:self.inc("l")) 
     self.l.grid(row=0,column=0) 
     self.r=Button(self,text=">",command=lambda:self.inc("r")) 
     self.r.grid(row=0,column=2) 
    def tick(self): 
     self.beat=thalam(root,self.e) 
     self.beat.thread.start() 
     self.play.configure(text="Stop",command=self.notick) 
    def notick(self): 
     self.play.configure(text="Start",command=self.tick) 
     self.beat.stop() 
    def inc(self,a): 
     if a=="l": 
      try: 
       new=str(int(self.e.get())-5) 
       self.e.delete(0, END) 
       self.e.insert(0,new) 
       self.beat.settempo(60.0/(int(self.e.get()))) 
      except: 
       print "Invalid BPM" 
       return 
     elif a=="r": 
      try: 
       new=str(int(self.e.get())+5) 
       self.e.delete(0, END) 
       self.e.insert(0,new) 
       self.beat.settempo((60.0/(int(self.e.get())))) 
      except: 
       print "Invalid BPM" 
       return 
+0

相關http://stackoverflow.com/a/8600301/674039 – wim

+0

那麼我已經使用了答案的方法,如在線next_call = time.time()所以... – user2658538

+0

相關:[如何實現高速,一致的採樣?](http://stackoverflow.com/q/10717589/4279) – jfs

回答

1

做任何需要的時間精確度是非常困難的,因爲需要對處理器與其他程序共享本身。不幸的是,對於計時關鍵程序,操作系統可以隨時切換到另一個進程,只要它選擇。這可能意味着它可能會在明顯的延遲之後纔會返回到您的程序。在之後使用time.sleep導入時間是嘗試平衡嘟嘟聲之間的時間的更一致的方式,因爲處理器沒有足夠的「理由」來切換。雖然Windows上的睡眠有15.6ms的默認粒度,但我認爲你不需要在64Hz的頻率下打一個節拍。此外,似乎您正在使用多線程來嘗試解決您的問題,但是,線程的python實現有時會強制線程順序運行。這使得切換離開你的過程更加糟糕。

我覺得最佳解決方案生成包含在所希望的頻率節拍器嘟嘟聲數據。然後你可以用操作系統理解的方式播放聲音數據。由於系統知道如何以可靠的方式處理聲音,因此您的節拍器可以工作。

對不起,令人失望,但時間關鍵的應用程序是非常難除非你想弄髒你的手與你正在使用的系統。

+0

我試過time.sleep,它也不一致。有沒有另一種方法在python中創建節拍器?這是我能想到的唯一方法。 – user2658538

1

播放聲音以模擬普通節拍器不需要「實時」功能。

它看起來像你使用Tkinter框架來創建GUI。 root.after()允許你call a function with a delay。給定argsinterval毫秒

def tick(interval, function, *args): 
    root.after(interval - timer() % interval, tick, interval, function, *args) 
    function(*args) # assume it doesn't block 

tick()運行function:你可以用它來實現蜱。單個滴答的持續時間受精度影響爲root.after(),但從長遠來看,穩定性僅取決於timer()函數。

下面是打印一些統計數據,每分鐘心跳240腳本:

#!/usr/bin/env python 
from __future__ import division, print_function 
import sys 
from timeit import default_timer 
try: 
    from Tkinter import Tk 
except ImportError: # Python 3 
    from tkinter import Tk 

def timer(): 
    return int(default_timer() * 1000 + .5) 

def tick(interval, function, *args): 
    root.after(interval - timer() % interval, tick, interval, function, *args) 
    function(*args) # assume it doesn't block 

def bpm(milliseconds): 
    """Beats per minute.""" 
    return 60000/milliseconds 

def print_tempo(last=[timer()], total=[0], count=[0]): 
    now = timer() 
    elapsed = now - last[0] 
    total[0] += elapsed 
    count[0] += 1 
    average = total[0]/count[0] 
    print("{:.1f} BPM, average: {:.0f} BPM, now {}" 
      .format(bpm(elapsed), bpm(average), now), 
      end='\r', file=sys.stderr) 
    last[0] = now 

interval = 250 # milliseconds 
root = Tk() 
root.withdraw() # don't show GUI 
root.after(interval - timer() % interval, tick, interval, print_tempo) 
root.mainloop() 

節奏osculates只有一拍:我的機器上240±1。

相關問題