Как обрабатывать аудиопоток в реальном времени

У меня есть установка с raspberry pi 3, на которой работает последняя версия jessie со всеми установленными обновлениями, в которой я предоставляю Bluetooth-приемник A2DP, где я подключаюсь к телефону, чтобы воспроизвести музыку.

Через pulseaudio источник (телефон) направляется на выход alsa (приемник). Это работает достаточно хорошо.

Теперь я хочу проанализировать аудиопоток, используя python3.4 с librosa, и я нашел многообещающий пример с использованием < href="https://people.csail.mit.edu/hubert/pyaudio/docs/#example-callback-mode-audio-io" rel="nofollow noreferrer">pyaudio, который был адаптирован для использования ввод pulseaudio (который волшебным образом работает, потому что он используется по умолчанию) вместо wavfile:

"""PyAudio Example: Play a wave file (callback version)."""

import pyaudio
import wave
import time
import sys
import numpy

# instantiate PyAudio (1)
p = pyaudio.PyAudio()

# define callback (2)
def callback(in_data, frame_count, time_info, status):
    # convert data to array
    data = numpy.fromstring(data, dtype=numpy.float32)
    # process data array using librosa
    # ...
    return (None, pyaudio.paContinue)

# open stream using callback (3)
stream = p.open(format=p.paFloat32,
                channels=1,
                rate=44100,
                input=True,
                output=False,
                frames_per_buffer=int(44100*10),
                stream_callback=callback)

# start the stream (4)
stream.start_stream()

# wait for stream to finish (5)
while stream.is_active():
    time.sleep(0.1)

# stop stream (6)
stream.stop_stream()
stream.close()
wf.close()

# close PyAudio (7)
p.terminate()

Теперь, когда поток данных в принципе работает, есть задержка (длина буфера), с которой вызывается stream_callback. Поскольку в документах указано

Обратите внимание, что PyAudio вызывает функцию обратного вызова в отдельном потоке.

я бы предположил, что пока работает обратный вызов, буфер продолжает заполнять основной поток. Конечно, была бы начальная задержка для заполнения буфера, после чего я ожидал получить синхронный поток.

Мне нужна более длинная часть в буфере (см. frame_in_buffer), чтобы librosa могла правильно выполнять анализ.

Как такое возможно? Это ограничение софт-портов для raspberry ARM?

Я нашел другие ответы, но они используют блокировка ввода/вывода. Как бы я обернул это в поток, чтобы анализ librosa (который может занять некоторое время) не блокировал заполнение буфера?

Этот блог, похоже, снижает производительность проблемы с cython, но я не думаю, что задержка связана с производительностью. Или может это? Другим, кажется, нужны некоторые настройки ALSA, но поможет ли это при использовании pulseaudio?

Спасибо, любой вклад приветствуется!


person x29a    schedule 06.03.2017    source источник
comment
Вы можете использовать queue.Queue для передачи аудиоданных из обратного вызова. функция. С другой стороны, вы можете просто подождать, пока будет доступно достаточно данных, и начать обработку...   -  person Matthias    schedule 07.03.2017
comment
однако кажется, что когда обратный вызов обрабатывает текущий буфер (который составляет несколько секунд), поток не продолжает буферизоваться, поэтому между вызовами обратного вызова есть задержка. в лучшем случае: обработка в обратном вызове занимает столько же времени, сколько занимает заполнение буфера из потока, и они выполняются всегда по одному   -  person x29a    schedule 07.03.2017
comment
Довольно редко используется такой большой размер блока, поэтому я действительно не знаю, как это должно себя вести. Обычно вы бы использовали что-то вроде 1024 кадров.   -  person Matthias    schedule 07.03.2017
comment
Почему и stream.stop_stream(), и stream.close()? Какая разница?   -  person Matthew D. Scholefield    schedule 29.06.2017