Обнаружение удара и воспроизведения (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:

http://www.swharden.com/blog/2010-03-05-realtime-fft-graph-of-audio-wav-file-or-microphone-input-with-python-scipy-and-wckgraph/

Другие вопросы по тегам