如何获取1Hz至32kHz对数频率间隔的频谱图数值数组?
获取对数频率缩放的STFT幅度数组解决方案
核心思路
scipy.signal.stft生成的是线性间隔的频率轴,要得到1Hz~32kHz的对数间隔幅度数组,需先计算线性STFT的幅度谱,再通过插值将线性频率上的幅度值映射到自定义的对数频率轴上。
实现步骤与代码示例
1. 基础STFT计算(保留原有代码)
import librosa import scipy import numpy as np from scipy.interpolate import interp1d # 加载音频 sample, samplerate = librosa.load('sound.wav', sr=64000) # 计算线性频率的STFT f, t, Zxx = scipy.signal.stft( sample, fs=samplerate, window='hamming', nperseg=512, noverlap=256 ) # 提取幅度谱 mag = np.abs(Zxx)
2. 生成对数频率轴
用np.logspace生成1Hz到32kHz的对数间隔频率点,可指定点数(建议和原线性频率点数一致,方便后续对比):
# 生成对数频率轴:1Hz到32kHz,点数与原线性频率轴相同 log_f = np.logspace( np.log10(1), # 起始频率的对数(1Hz) np.log10(32000), # 结束频率的对数(奈奎斯特频率) num=len(f) # 对数频率点的数量 )
3. 插值映射幅度到对数频率轴
有两种常用插值方式,按需选择:
方式一:线性插值(快速高效)
用np.interp对每个时间帧的幅度做线性插值:
log_mag = np.zeros((len(log_f), len(t))) for idx in range(len(t)): # 将当前时间帧的线性幅度插值到对数频率轴上 log_mag[:, idx] = np.interp(log_f, f, mag[:, idx])
方式二:高阶插值(结果更平滑)
用scipy.interpolate.interp1d实现三次插值,无需循环,效率更高:
# 创建插值函数,指定对频率轴(axis=0)做三次插值 interp_func = interp1d(f, mag, axis=0, kind='cubic', fill_value="extrapolate") # 得到对数频率对应的幅度数组 log_mag = interp_func(log_f)
4. 可选优化:过滤无效频率范围
原STFT的频率轴包含0Hz,而我们的目标是1Hz起,可先过滤掉1Hz以下的频率点,避免外推误差:
# 过滤出1Hz及以上的频率点 valid_mask = f >= 1 f_valid = f[valid_mask] mag_valid = mag[valid_mask, :] # 重新生成对应点数的对数频率轴 log_f = np.logspace(np.log10(1), np.log10(32000), num=len(f_valid)) # 插值得到优化后的对数幅度数组 interp_func = interp1d(f_valid, mag_valid, axis=0, kind='cubic') log_mag = interp_func(log_f)
最终结果说明
log_f:1Hz到32kHz的对数间隔频率数组log_mag:对应对数频率轴的二维幅度数组,维度为(对数频率点数, 时间帧数量),可直接用于后续分析或可视化
内容的提问来源于stack exchange,提问作者Pouple
相关产品推荐
相关产品推荐

