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 раз быстрее.