加快声音处理算法

时间:2016-05-03 14:28:16

标签: python performance python-2.7 pyaudio

我使用以下代码进行一些即时声音处理/分析。它工作,但真的很慢(与计划的速度相比)。我添加了一些时间标记来找出问题所在,根据它们不应该有任何问题。对于所有三个计算时间,典型的持续时间(见下文)<0.01秒,但是仍然需要大约一秒来完成循环。问题在哪里?

修改:请注意,时间测量不是真正的问题。为了证明:MyPeaks基本上只能找到相当短的FFT的最大值 - 没什么代价。即使这些例程被注释掉,问题仍然存在。

  • 我应该使用与lambda函数不同的东西来进行循环吗?
  • 启动和录制流时是否犯了一些错误?
  • import pyaudio
    import struct
    import mute_alsa
    import time
    import numpy as np
    from Tkinter import *
    
    
    def snd_process(k=0):
     if k<1000:
      t0=time.clock()
    
      data = stream.read(CHUNK)
    
      t1=time.clock()
    
      fl=CHUNK
      int_data = struct.unpack("%sh" %str(fl),data)
      ft=np.fft.fft(int_data)
      ft=np.fft.fftshift(ft)
      ft=np.abs(ft)     
    
      t2=time.clock()
    
      pks=MyPeaks(np.log(ft))
    
      freq_out.configure(text=str(pks))
    
      t3=time.clock()
    
      print t1-t0, t2-t1, t3-t2     
    
      master.after(1, lambda: snd_process(k+1))
    
    CHUNK = 8000
    FORMAT = pyaudio.paInt16
    CHANNELS = 1
    RATE = 4000
    
    p = pyaudio.PyAudio()
    
    stream = p.open(format=FORMAT,
                channels=CHANNELS,
                rate=RATE,
                input=True,
                frames_per_buffer=CHUNK)
    
    #Tkinter stuff
    master=Tk()
    button_play=Button(master, command=snd_process, bg="yellow", text="Analyze")
    button_play.grid(row=0, column=0)
    freq_out = Label(master)
    freq_out.grid(row=0, column=1)
    freq_out.configure(text='base')
    mainloop()
    

3 个答案:

答案 0 :(得分:4)

您正在tk主线程中安排1000个回调;对于每个回调,您使用1 ms延迟(after()的第一个参数)。这意味着最后一个循环将在第一个循环1000毫秒(1秒)后开始。

也许循环仍然需要大约一秒才能完成

因此,请尝试使用after_idle()。我认为你真的不需要加速声音处理算法因为np已经非常有效了。

<强> [编辑] 惊喜!你是在每次迭代时从音频通道读取1秒8000字节,16位格式,4000帧速率。你需要一秒钟才能拥有它。

答案 1 :(得分:2)

将I / O和计算压缩到主循环就像是经典的解决方案。但也有其他选择。

  1. 在第二个帖子中进行音频采集和计算。由于I / O和numpy都应该释放GIL,因此它可能是一个很好的选择。这里有一个警告。由于像TKinter这样的GUI工具包通常不是多线程安全的,因此你应该从第二个线程进行Tkinter调用。但您可以设置一个使用after调用的函数来检查计算的进度,并每隔100毫秒更新一次UI。

  2. 在另一个multiprocessing.Process中进行音频采集和计算。这使它与GUI完全分离。您必须设置一个通信渠道,例如: Queuepks发送回主进程。您应该使用after函数检查Queue是否有可用数据,如果是,则更新显示。

答案 2 :(得分:-1)

根据您运行的操作系统,您可能会测量实际的“挂钟”时间。有关详细信息,请参阅此处http://pythoncentral.io/measure-time-in-python-time-time-vs-time-clock/。请注意,对于python 3.3,不推荐使用time.clock,建议使用time.process_time()或time.perf_counter()。