【问题标题】:Speeding up the sound processing algorithm加快声音处理算法
【发布时间】:2016-05-03 14:28:16
【问题描述】:

我使用以下代码进行一些即时声音处理/分析。它有效,但真的很慢(与计划的速度相比)。我添加了一些时间标记来找出问题出在哪里,据他们说不应该有任何问题。所有三个计算时间的典型持续时间(见下文)

编辑:请注意,时间测量在这里并不是真正的问题。为了证明这一点: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()
    

【问题讨论】:

  • 使用 C 加速你的 python! (或 cython)
  • 你怎么知道时间是处理时间而不是master.after()?当我尝试对原始 C 进行 numpy 时,它似乎要快得多。 (对于不足为奇的点)
  • @paddyg 我没有,但这不是问题(见编辑)。
  • 如果您每毫秒运行一个函数,并且执行 1000 次,则整个处理必须至少需要一秒钟,加上执行实际工作所需的时间。

标签: python performance python-2.7 pyaudio


【解决方案1】:

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

也许这就是循环仍然需要大约一秒钟才能完成的方式

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

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

【讨论】:

  • 谢谢,但是 - 遗憾的是 - 它不起作用。 after_idle 需要大约相同的时间,当使用 0 ms 超时时,它不会更新 Tkinter 中的字段 (freq_out.configure(text=str(pks)))
  • 所以也许最好在 20 或 50 个的包中进行,我认为您不需要 1 毫秒的 UI 刷新粒度...
  • @VictorPira 请注意:如果您没有 UI 的完整周期需要(例如)200 毫秒,那么您要求的 UI 刷新率为每秒 5000 帧:也许 TclTk 不是为此类任务设计的。
  • 200 毫秒是 5 帧/秒。这应该是可行的。但你是对的,在 1 毫秒后强制重绘可能太多了。
  • @RolandSmith 在 200 毫秒内发送 1000 次更新 -> 0.2 毫秒内 1 帧 -> 每秒 5000 帧。
【解决方案2】:

像您所做的那样将 I/O 和计算压缩到主循环中是经典的解决方案。但是还有其他选择。

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

  2. 在不同的multiprocessing.Process 中进行音频收集和计算。这使它与您的 GUI 完全分离。您将不得不建立一个沟通渠道,例如Queuepks 发送回主进程。您应该使用after 函数来检查Queue 是否有可用数据,如果有则更新显示。

【讨论】:

    【解决方案3】:

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-07-04
      • 1970-01-01
      • 1970-01-01
      • 2019-08-17
      • 2010-12-10
      • 2014-06-10
      相关资源
      最近更新 更多