参考 pytorch 音频特征提取教程:
https://docs.pytorch.org/audio/stable/tutorials/audio_feature_extractions_tutorial.html
准备
from IPython.display import Audio from torchaudio.utils import download_asset from librosa import power_to_db import matplotlib.pyplot as plt import numpy as np import torchaudio # 基本参数 n_fft = 512 hop_length = 512 # 示例音频 sample_speech = download_asset( "tutorial-assets/Lab41-SRI-VOiCES-src-sp0307-ch127535-sg0042.wav" ) # 提取频谱 waveform, sample_rate = torchaudio.load(sample_speech) print(waveform.shape) print(sample_rate)
torchaudio实现
spectrogram = torchaudio.transforms.Spectrogram( n_fft=n_fft, hop_length=hop_length ) spec = spectrogram(waveform) spec.shape
torch.Size([1, 257, 107])
频谱有三个维度:
通道域: 单声道即为1,双声道即为2
频率域: n_fft // 2 + 1
时间域: (总采样数 / 窗口长度(win_length)) + 1 (默认每次移动半个窗口)
# 绘制波形和频谱图 _, axs = plt.subplots(2, 1) axs[0].plot(waveform[0]) axs[1].imshow( power_to_db(spec[0]), origin='lower', aspect='auto', interpolation='nearest' ) plt.tight_layout() plt.show()
自实现
实现频谱图计算一般包括一下步骤:
- 1、计算帧数。时间维度的长度。
- 2、扩展信号。如果不满足整数帧的长度,需要扩展原始信号满足长度。
- 3、分帧。
- 4、快速傅里叶变换。
# 手动实现频谱计算 def my_spectrogram(signal, n_fft=512, hop_length=512): # 数组转换 signal = np.asarray(signal) # 计算帧数 signal_length = len(signal) num_frames = int(np.ceil(signal_length / hop_length)) # 扩展信号 pad_length = (num_frames - 1) * hop_length + n_fft - signal_length signal = np.pad( signal, (0, pad_length), mode='constant' ) # 分帧 frames = np.lib.stride_tricks.sliding_window_view( signal, window_shape=n_fft )[::hop_length] # 加窗 window = np.hanning(n_fft) frames = frames * window # 快速傅里叶变换 fft_result = np.fft.fft(frames, axis=1) magnitude_spectrum = np.abs(fft_result) ** 2 # 对称处理 magnitude_spectrum = magnitude_spectrum[:, : n_fft//2 + 1] return magnitude_spectrum
# 计算频谱 my_spec = my_spectrogram(waveform[0].numpy()) my_spec.shape
(107, 257)
对比两个频谱
# 绘制波形和频谱图 _, axs = plt.subplots(2, 1) axs[0].imshow( power_to_db(spec[0]), origin='lower', aspect='auto', interpolation='nearest' ) axs[1].imshow( power_to_db(my_spec.T), origin='lower', aspect='auto', interpolation='nearest' ) plt.tight_layout() plt.show()
如上图,频谱图,肉眼看不出什么较大差别。
但是mse差异较大:
# 计算两个频谱差距 import torch mse = torch.square(spec[0] - my_spec.T).mean().item() mse
604.3883183638077