我正在尝试使用此Beat Detection算法在python中进行音频处理。我已经实现了前面文章中的第一个(非优化版本)。虽然它打印了一些结果,但我无法检测它是否能准确工作,因为我不知道如何用它播放声音
目前,在进入计算循环之前,我使用Popen
异步启动我的媒体播放器和歌曲,但我不确定这种策略是否有效,并且是否给出同步结果。
#!/usr/bin/python
import scipy.io.wavfile, numpy, sys, subprocess
# Some abstractions for computation
def sumsquared(arr):
sum = 0
for i in arr:
sum = sum + (i[0] * i[0]) + (i[1] * i[1])
return sum
if sys.argv.__len__() < 2:
print 'USAGE: wavdsp <wavfile>'
sys.exit(1)
numpy.set_printoptions(threshold='nan')
rate, data = scipy.io.wavfile.read(sys.argv[1])
# Beat detection algorithm begin
# the algorithm has been implemented as per GameDev Article
# Initialisation
data_len = data.__len__()
idx = 0
hist_last = 44032
instant_energy = 0
local_energy = 0
le_multi = 0.023219955 # Local energy multiplier ~ 1024/44100
# Play the song
p = subprocess.Popen(['audacious', sys.argv[1]])
while idx < data_len - 48000:
dat = data[idx:idx+1024]
history = data[idx:hist_last]
instant_energy = sumsquared(dat)
local_energy = le_multi * sumsquared(history)
print instant_energy, local_energy
if instant_energy > (local_energy * 1.3):
print 'Beat'
idx = idx + 1024
hist_last = hist_last + 1024 # Right shift history buffer
p.terminate()
为了以时间同步的方式获得音频输出和算法(控制台)输出,我可以对脚本进行哪些修改/添加?即当控制台输出特定帧的结果时,该帧必须在扬声器上播放。
工作节拍检测代码(NumPy/PyAudio)
如果您正在使用NumPy,此代码可能会有所帮助。它假设信号(用PyAudio读取)是16位宽Int。如果不是这种情况,请更改或删除信号.astype()并调整归一化除法器(此处为max-int16)。
class SimpleBeatDetection:
"""
Simple beat detection algorithm from
http://archive.gamedev.net/archive/reference/programming/features/beatdetection/index.html
"""
def __init__(self, history = 43):
self.local_energy = numpy.zeros(history) # a simple ring buffer
self.local_energy_index = 0 # the index of the oldest element
def detect_beat(self, signal):
samples = signal.astype(numpy.int) # make room for squares
# optimized sum of squares, i.e faster version of (samples**2).sum()
instant_energy = numpy.dot(samples, samples) / float(0xffffffff) # normalize
local_energy_average = self.local_energy.mean()
local_energy_variance = self.local_energy.var()
beat_sensibility = (-0.0025714 * local_energy_variance) + 1.15142857
beat = instant_energy > beat_sensibility * local_energy_average
self.local_energy[self.local_energy_index] = instant_energy
self.local_energy_index -= 1
if self.local_energy_index < 0:
self.local_energy_index = len(self.local_energy) - 1
return beat
wav读取或麦克风记录的PyAudio示例将为您提供所需的信号数据。使用frombuffer()
高效创建NumPy阵列
data = stream.read(CHUNK)
signal = numpy.frombuffer(data, numpy.int16)
一种更简单的非实时方法
我对将控制台输出与实时音频同步并不乐观。我的方法会简单一点。当您阅读并处理文件时,将样本写入一个新的音频文件。每当检测到节拍时,在你正在写的音频中添加一些难以错过的声音,比如响亮、简短的正弦音。这样,您就可以从听觉上评估结果的质量。
合成节拍指示音:
def testsignal(hz,seconds=5.,sr=44100.):
'''
Create a sine wave at hz for n seconds
'''
# cycles per sample
cps = hz / sr
# total samples
ts = seconds * sr
return np.sin(np.arange(0,ts*cps,cps) * (2*np.pi))
signal = testsignal(880,seconds = .02)
在while
循环中,如果检测到节拍,则将测试信号添加到输入帧,如果未检测到节拍则保持帧不变。将这些帧写入一个文件,然后听它来评估节拍检测的质量。
这是aubio库用于评估节拍检测结果的方法。请参阅此处的文档。特别令人感兴趣的是--output
命令行选项的文档:
将结果保存在此文件中。文件将在的模型上创建输入文件。结果用一个很短的木块样品来标记。
优化
由于numpy已经是一个依赖项,请使用它的功能来加快算法的速度。您可以将sumsquared
函数重写为:
def sumsquared(arr):
return (arr**2).sum()
去掉Python for循环并将这些计算向下推到C代码中应该可以提高速度。
此外,看看这个问题或这个问题,了解如何使用numpy.lib.stride_tricks
方法将while
循环中的局部到瞬时能量比较矢量化。
最好尝试portaudio(pyaudio)来实时获取数据,然后您应该能够查看它是否匹配。
下面是一个很好的例子,使用pyaudio麦克风的fft:
http://www.swharden.com/blog/2010-03-05-realtime-fft-graph-of-audio-wav-file-or-microphone-input-with-python-scipy-and-wckgraph/