如何从PyAudio读取的音频数据中提取低音(低频)信号?
实时音频低频(低音)提取实现方案
前置说明
你当前使用的是固定1024长度的音频块处理,低音通常对应20~200Hz的频率范围,可根据需求调整截止频率。你的原代码中存在两个重复的while循环,逻辑冗余,执行完第一个循环后data已为空,第二个循环不会触发,可直接删除。
实现方案
方案1:FFT频域截取实现(逻辑直观,适合固定块处理场景)
通过短时FFT将音频转换到频域,保留低频分量后逆变换回时域即可得到低音数据:
- 提前定义参数和加窗函数,减少频谱泄露
- 对每个音频块做FFT变换,高频分量幅值置0
- 逆FFT转回时域,格式转换后得到低音数组
代码示例
import numpy as np import pyaudio import wave # 可调整参数 CHUNK = 1024 CUTOFF_FREQ = 200 # 低音截止频率,单位Hz # 初始化音频读取 wf = wave.open('song.wav', 'rb') SAMPLE_RATE = wf.getframerate() CHANNELS = wf.getnchannels() p = pyaudio.PyAudio() stream = p.open(format=p.get_format_from_width(wf.getsampwidth()), channels=CHANNELS, rate=SAMPLE_RATE, output=True) # 计算FFT对应截止频率的索引 cutoff_idx = int(CUTOFF_FREQ * CHUNK / SAMPLE_RATE) # 生成汉明窗减少频谱泄露 window = np.hamming(CHUNK) data = wf.readframes(CHUNK) while len(data) > 0: stream.write(data) data = wf.readframes(CHUNK) if len(data) == 0: break decoded = np.frombuffer(data, dtype=np.int16) # 低音提取逻辑 if CHANNELS == 2: # 双声道拆分处理 decoded = decoded.reshape(-1, 2) def extract_bass(sig): sig_windowed = sig * window fft_sig = np.fft.fft(sig_windowed) # 高频分量置0 fft_sig[cutoff_idx:-cutoff_idx] = 0 bass_sig = np.fft.ifft(fft_sig).real return bass_sig.astype(np.int16) left_bass = extract_bass(decoded[:,0]) right_bass = extract_bass(decoded[:,1]) bass_decoded = np.column_stack((left_bass, right_bass)).flatten() else: # 单声道处理 sig_windowed = decoded * window fft_sig = np.fft.fft(sig_windowed) fft_sig[cutoff_idx:-cutoff_idx] = 0 bass_sig = np.fft.ifft(fft_sig).real bass_decoded = bass_sig.astype(np.int16) # bass_decoded 即为提取后的低音数组 print(bass_decoded) # 资源释放 stream.stop_stream() stream.close() p.terminate()
方案2:IIR低通滤波实现(延迟更低,适合实时流场景)
提前生成巴特沃斯低通滤波器系数,每个音频块直接滤波即可,无块效应,实时性更好:
代码示例(需安装scipy库)
from scipy.signal import butter, lfilter # 生成低通滤波器系数 def butter_lowpass(cutoff, fs, order=3): nyq = 0.5 * fs normal_cutoff = cutoff / nyq b, a = butter(order, normal_cutoff, btype='low', analog=False) return b, a b, a = butter_lowpass(CUTOFF_FREQ, SAMPLE_RATE, order=3) # 循环内替换提取逻辑即可 # 拿到decoded数组后直接调用: bass_decoded = lfilter(b, a, decoded).astype(np.int16)
注意事项
- 若需要单独播放低音,直接将
bass_decoded调用.tobytes()方法转换为字节流后写入stream即可 - 截止频率可根据需求调整,需要更低的低音可降低
CUTOFF_FREQ到150Hz,需要保留更多中低频可提高到300Hz - 如果是实时麦克风输入流,将wav读取逻辑替换为PyAudio的输入流读取逻辑即可复用上述代码
内容的提问来源于stack exchange,提问作者tubez
相关产品推荐
相关产品推荐

