PyAudio: Какой наиболее эффективный метод форматирования и упаковки / распаковки использовать в режиме обратного вызова?

Я использую Pyaudio в режиме обратного вызова с paFloat32 формат, 2 канала, 1024 кадра на буфер, и я заинтересован в более эффективном обмене данными аудиобуфера ввода / вывода.

Чтобы распаковать входной аудио-буфер и получить список сэмплов, я использую:

fmt       = str( N_CHANNELS * BUFFER_SIZE ) + 'f'
in_floats = struct.unpack( fmt, in_data )

С помощью struct.pack() а также struct.unpack() является довольно неэффективным и требует значительных ресурсов процессора, почти так же, как сама обработка аудиосигнала. Поскольку большинство звуковых карт являются 16-битными, я также попытался использовать paInt16 формат, но результаты практически идентичны.

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

Редактировать: PyAudio обменивается данными, используя двоичные потоки или буферы, аналогичные структурам данных C, используемым в Portaudio. Мне нужно распаковать in_data входной буфер, чтобы получить образцы с плавающей точкой и проанализировать их. Все в порядке, кроме распаковки немного медленно.

1 ответ

Решение

Используя NumPy или stdlib array модуль будет намного быстрее, потому что большая часть затрат на struct.unpack это не распаковка, это упаковка каждого значения с плавающей точкой в ​​Python float объект.

Например:

In [1177]: f = [random.random() for _ in range(65536)]

In [1178]: b = struct.pack('65536f', *f)

In [1179]: %timeit struct.unpack('65536f', b)
1000 loops, best of 3: 1.61 ms per loop

In [1180]: %timeit array.array('f', b)
100000 loops, best of 3: 17.7 µs per loop

Это в 100 раз быстрее. И у вас есть итерируемый поплавок в любом случае, просто это array вместо tuple,

Однако, если вы планируете на самом деле выполнить арифметику с этими значениями, вам все равно придется выполнить итерацию этих значений - и array придется распаковывать каждый из них, как вы это делаете, что собирается добавить обратно в большую часть времени, которое вы сохранили.

Вот где приходит NumPy; Я сомневаюсь np.frombuffer(b, dtype=np.float32) будет намного быстрее, чем array.array('f', b) для создания, но это позволит вам делать векторизованную арифметику непосредственно для распакованных значений. Например:

In [1186]: a1 = array.array('f', b)

In [1187]: a2 = np.frombuffer(b, dtype=np.float32)

In [1189]: %timeit sum(f)
1000 loops, best of 3: 586 µs per loop

In [1189]: %timeit sum(a1)
1000 loops, best of 3: 907 µs per loop

In [1190]: %timeit a2.sum()
10000 loops, best of 3: 80.3 µs per loop

Как вы можете видеть, используя array.array делает это в два раза медленнее (я использовал sum потому что фактическая итерация и арифметика выполняются в C), но с использованием np.array вместо этого делает это в 5 раз быстрее.

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