Обнаружение удара и воспроизведения (WAV) файл в синхронизированном порядке
Я пробую свои силы в обработке звука в 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()
Какие изменения / дополнения я могу внести в сценарий, чтобы получить вывод звука и вывод алгоритма (консоли) синхронизированным по времени образом? То есть, когда результаты консоли выводятся для определенного кадра, этот кадр должен воспроизводиться на колонках.
3 ответа
Рабочий код обнаружения ударов (NumPy / PyAudio)
Если вы используете NumPy, этот код может помочь. Предполагается, что сигнал (считывается с PyAudio) имеет 16-битную ширину Int. Если это не так, измените или удалите signal.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
Примеры PyAudio для чтения в wav или записи с микрофона предоставят вам необходимые данные сигнала. Создайте массив NumPy эффективно с frombuffer()
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()
Избавление от цикла for Python и откладывание этих вычислений в код на C должно дать вам повышение скорости.
Кроме того, взгляните на этот вопрос или этот вопрос, чтобы получить представление о том, как можно векторизовать локальные и мгновенные сравнения энергии в while
цикл, используя numpy.lib.stride_tricks
метод.
Хорошей идеей было бы попробовать portaudio (pyaudio), чтобы получить данные в реальном времени, тогда вы сможете увидеть, соответствуют ли они.
Вот хороший пример использования fft из микрофона с pyaudio: