加快声音处理算法
Speeding up the sound processing algorithm
我使用下面的代码来做一些即时的声音processing/analyzing。它有效,但真的很慢(与计划速度相比)。我添加了一些时间标记来找出问题所在,根据他们的说法,不应该有任何问题。所有三个计算时间的典型持续时间(见下文)均小于 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()
根据 OS,您 运行 可能无法测量实际的 'wall-clock' 时间。有关详细信息,请参阅此处 http://pythoncentral.io/measure-time-in-python-time-time-vs-time-clock/。请注意,对于 python 3.3,time.clock 已弃用,建议使用 time.process_time() 或 time.perf_counter()。
您正在 tk 主线程中安排 1000 个回调;对于您使用 1 毫秒延迟的每个回调(after()
的第一个参数)。这意味着最后一个循环将在第一个循环 1000 毫秒(1 秒)后开始。
也许这就是循环的方式仍然需要大约一秒钟才能完成。
所以,尝试使用after_idle()
。我不认为你真的需要加速声音处理算法因为np
已经相当高效了。
[编辑]
惊喜!!您在每次迭代时从音频通道读取 1 秒 8000 字节,16 位格式,帧速率为 4000。你需要一秒钟才能拥有它。
将I/O和计算压缩到主循环中,就像你正在做的那样是经典的解决方案。但还有其他选择。
在第二个线程中进行音频收集和计算。由于 I/O 和 numpy 都应该释放 GIL,因此它可能是一个不错的选择。这里有一个警告。由于像 TKinter 这样的 GUI 工具包通常不是多线程安全的,因此您应该 而不是 从第二个线程进行 Tkinter 调用。但是你可以设置一个用 after
调用的函数来检查计算的进度并更新 UI 说每 100 毫秒。
在另一个multiprocessing.Process
中进行音频收集和计算。这使它与您的 GUI 完全分开。您将必须建立一个沟通渠道,例如a Queue
将 pks
发送回主进程。您应该使用 after
函数来检查 Queue
是否有可用数据,如果有则更新显示。
我使用下面的代码来做一些即时的声音processing/analyzing。它有效,但真的很慢(与计划速度相比)。我添加了一些时间标记来找出问题所在,根据他们的说法,不应该有任何问题。所有三个计算时间的典型持续时间(见下文)均小于 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()
根据 OS,您 运行 可能无法测量实际的 'wall-clock' 时间。有关详细信息,请参阅此处 http://pythoncentral.io/measure-time-in-python-time-time-vs-time-clock/。请注意,对于 python 3.3,time.clock 已弃用,建议使用 time.process_time() 或 time.perf_counter()。
您正在 tk 主线程中安排 1000 个回调;对于您使用 1 毫秒延迟的每个回调(after()
的第一个参数)。这意味着最后一个循环将在第一个循环 1000 毫秒(1 秒)后开始。
也许这就是循环的方式仍然需要大约一秒钟才能完成。
所以,尝试使用after_idle()
。我不认为你真的需要加速声音处理算法因为np
已经相当高效了。
[编辑] 惊喜!!您在每次迭代时从音频通道读取 1 秒 8000 字节,16 位格式,帧速率为 4000。你需要一秒钟才能拥有它。
将I/O和计算压缩到主循环中,就像你正在做的那样是经典的解决方案。但还有其他选择。
在第二个线程中进行音频收集和计算。由于 I/O 和 numpy 都应该释放 GIL,因此它可能是一个不错的选择。这里有一个警告。由于像 TKinter 这样的 GUI 工具包通常不是多线程安全的,因此您应该 而不是 从第二个线程进行 Tkinter 调用。但是你可以设置一个用
after
调用的函数来检查计算的进度并更新 UI 说每 100 毫秒。在另一个
multiprocessing.Process
中进行音频收集和计算。这使它与您的 GUI 完全分开。您将必须建立一个沟通渠道,例如aQueue
将pks
发送回主进程。您应该使用after
函数来检查Queue
是否有可用数据,如果有则更新显示。