我曾经遇到类似的问题,尝试使用PyAudio回调模式进行编程,但我的要求是:
- 使用立体声输出(2个通道)。
- 实时处理。
- 使用任意脉冲响应处理输入信号,该响应可能在处理过程中发生变化。
经过几次尝试,我成功了,以下是我的代码片段(基于在这里找到的PyAudio示例):
import pyaudio
import scipy.signal as ss
import numpy as np
import librosa
track1_data, track1_rate = librosa.load('path/to/wav/track1', sr=44.1e3, dtype=np.float64)
track2_data, track2_rate = librosa.load('path/to/wav/track2', sr=44.1e3, dtype=np.float64)
track3_data, track3_rate = librosa.load('path/to/wav/track3', sr=44.1e3, dtype=np.float64)
p = pyaudio.PyAudio()
count = 0
IR_left = first_IR_left
IR_right = first_IR_right
def callback(in_data, frame_count, time_info, status):
global count
track1_frame = track1_data[frame_count*count : frame_count*(count+1)]
track2_frame = track2_data[frame_count*count : frame_count*(count+1)]
track3_frame = track3_data[frame_count*count : frame_count*(count+1)]
track1_left = ss.fftconvolve(track1_frame, IR_left)
track1_right = ss.fftconvolve(track1_frame, IR_right)
track2_left = ss.fftconvolve(track2_frame, IR_left)
track2_right = ss.fftconvolve(track2_frame, IR_right)
track3_left = ss.fftconvolve(track3_frame, IR_left)
track3_right = ss.fftconvolve(track3_frame, IR_right)
track_left = 1/3 * track1_left + 1/3 * track2_left + 1/3 * track3_left
track_right = 1/3 * track1_right + 1/3 * track2_right + 1/3 * track3_right
ret_data = np.empty((track_left.size + track_right.size), dtype=track1_left.dtype)
ret_data[1::2] = br_left
ret_data[0::2] = br_right
ret_data = ret_data.astype(np.float32).tostring()
count += 1
return (ret_data, pyaudio.paContinue)
stream = p.open(format=pyaudio.paFloat32,
channels=2,
rate=int(track1_rate),
output=True,
stream_callback=callback,
frames_per_buffer=2**16)
stream.start_stream()
while_count = 0
while stream.is_active():
while_count += 1
if while_count % 3 == 0:
IR_left = first_IR_left
IR_right = first_IR_right
elif while_count % 3 == 1:
IR_left = second_IR_left
IR_right = second_IR_right
elif while_count % 3 == 2:
IR_left = third_IR_left
IR_right = third_IR_right
time.sleep(10)
stream.stop_stream()
stream.close()
p.terminate()
以下是关于上述代码的一些重要反思:
- 使用
librosa
而不是wave允许我使用numpy数组进行处理,这比wave.readframes
中的数据块要好得多。
p.open(format=
中设置的数据类型必须与ret_data
字节的格式匹配。PyAudio最多使用float32
。
ret_data
中的偶数索引字节进入右耳机,奇数索引字节进入左耳机。
只是为了澄清,此代码将三个轨道的混合发送到立体声输出音频中,并且每10秒更改脉冲响应和因此应用的滤波器。
我使用它来测试我正在开发的3D音频应用程序,因此脉冲响应是头部相关脉冲响应(HRIR),每10秒更改一次声音位置。
编辑:
这段代码存在问题:输出带有噪声,频率与帧大小相对应(帧大小越小,频率越高)。我通过手动重叠和添加帧来解决了这个问题。基本上,ss.oaconvolve
返回一个大小为 track_frame.size + IR.size - 1
的数组,所以我将该数组分成了前 track_frame.size
个元素(然后用于 ret_data
),然后最后 IR.size - 1
个元素被保存以备后用。这些保存的元素将会被添加到下一帧的前 IR.size - 1
个元素中。第一帧添加零。
CHANNELS = 2
。这如何处理立体声输入? - endolith