2014-06-12 25 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。

相关问题