本文对 Librosa、Madmom 和 Torchaudio 三个库中获取频谱图的方法细节进行对比。

分帧细节对比

librosa

参数设置举例

1
2
3
4
5
6
7
8
sample_rate = 22050
n_fft = 1764 # 441 * 4
win_length = n_fft # window_size
hop_length = 441 # hop_size
center = True
duration = 1
num_samples = sample_rate * duration
random_audio = np.random.randn(num_samples)

短时傅里叶变换

librosa.stft

1
2
3
4
5
6
7
D = librosa.stft(
random_audio,
n_fft=n_fft,
hop_length=hop_length,
win_length=win_length,
center=center
)

mel频谱图

librosa.feature.melspectrogram

1
2
3
4
5
6
7
8
mel_spectrogram = librosa.feature.melspectrogram(
y=random_audio,
sr=sample_rate,
n_fft=n_fft,
hop_length=hop_length,
win_length=win_length,
center=center
)

分帧实验

输出 D 或 mel_spectrogram 的形状我们可以得到变换后的帧数,以下是使用不同参数得到的帧数,该部分数据可用于验证帧数计算公式是否正确。

实验数据
center win_length hop_length num_sampless num_frames
true 1764 441 22050 51
true 1764 441 22049 50
true 1764 441 22051 51
true 1763 441 22050 50
true 1763 441 22049 50
true 1763 441 22051 51
true 1765 441 22050 50
true 1765 441 22049 50
true 1765 441 22051 51
true 1764 441 44100 101
true 1764 441 44099 100
true 1764 441 44101 101
true 1763 441 44100 100
true 1763 441 44099 100
true 1763 441 44101 101
true 1765 441 44100 100
true 1765 441 44099 100
true 1765 441 44101 101
center win_length hop_length num_sampless num_frames
false 1764 441 22050 47
false 1764 441 22049 46
false 1764 441 22051 47
false 1763 441 22050 47
false 1763 441 22049 47
false 1763 441 22051 47
false 1765 441 22050 46
false 1765 441 22049 46
false 1765 441 22051 47
false 1764 441 44100 97
false 1764 441 44099 96
false 1764 441 44101 97
false 1763 441 44100 97
false 1763 441 44099 97
false 1763 441 44101 97
false 1765 441 44100 96
false 1765 441 44099 96
false 1765 441 44101 97

madmom

对信号分帧

madmom.audio.signal.FramedSignal

1
2
3
4
5
6
7
8
frames = madmom.audio.signal.FramedSignal(
random_audio,
frame_size=win_length,
hop_size=hop_length,
end='normal',
sample_rate=sample_rate,
origin=center
)

频谱图

madmom.audio.spectrogram.Spectrogram

1
2
3
4
5
6
7
8
9
spectrogram = madmom.audio.spectrogram.Spectrogram(
random_audio,
n_fft=n_fft,
frame_size=win_length,
hop_size=hop_length,
end='normal',
sample_rate=sample_rate,
origin=center
)

分帧实验

输出 frames 或 spectrogram 的形状我们可以得到变换后的帧数,以下是使用不同参数得到的帧数。

实验数据
origin win_length hop_length num_sampless num_frames
center 1764 441 22050 50
center 1764 441 22049 50
center 1764 441 22051 51
center 1763 441 22050 50
center 1763 441 22049 50
center 1763 441 22051 51
center 1765 441 22050 50
center 1765 441 22049 50
center 1765 441 22051 51
center 1764 441 44100 100
center 1764 441 44099 100
center 1764 441 44101 101
center 1763 441 44100 100
center 1763 441 44099 100
center 1763 441 44101 101
center 1765 441 44100 100
center 1765 441 44099 100
center 1765 441 44101 101
origin win_length hop_length num_sampless num_frames
future 1764 441 22050 50
future 1764 441 22049 50
future 1764 441 22051 51
future 1763 441 22050 50
future 1763 441 22049 50
future 1763 441 22051 51
future 1765 441 22050 50
future 1765 441 22049 50
future 1765 441 22051 51
future 1764 441 44100 100
future 1764 441 44099 100
future 1764 441 44101 101
future 1763 441 44100 100
future 1763 441 44099 100
future 1763 441 44101 101
future 1765 441 44100 100
future 1765 441 44099 100
future 1765 441 44101 101

结论

由 madmom.audio.signal.FramedSignal 中的代码

1
num_frames = np.ceil(len(self.signal) / float(self.hop_size))

以及实验数据可得帧数计算公式:

Nframes=Nsampleshop_sizeN_{frames}=\left\lceil\frac{N_{samples}}{hop\_size}\right\rceil

由 madmom.audio.signal.FramedSignal 中的代码

1
2
self.hop_size = self.signal.sample_rate / float(fps)
# 若给定fps,madmom会根据fps计算hop_size

可得帧率计算公式:

fps=sample_ratehop_sizefps = \frac{sample\_rate} {hop\_size}

torchaudio

频谱图

torchaudio.transforms.Spectrogram

1
2
3
4
5
6
7
8
random_audio = torch.randn(1, num_samples)
trans_spectrogram = torchaudio.transforms.Spectrogram(
n_fft=n_fft,
win_length=win_length,
hop_length=hop_length,
center=center
)
spectrogram = trans_spectrogram(random_audio)

mel频谱图

torchaudio.transforms.MelSpectrogram

1
2
3
4
5
6
7
8
trans_mel_spectrogram = torchaudio.transforms.MelSpectrogram(
sample_rate=sample_rate,
n_fft=n_fft,
win_length=win_length,
hop_length=hop_length,
center=center
)
mel_spectrogram = trans_mel_spectrogram(random_audio)

分帧实验

输出 spectrogram 或 mel_spectrogram 的形状我们可以得到变换后的帧数,以下是使用不同参数得到的帧数。

实验数据
center win_length hop_length num_sampless num_frames
true 1764 441 22050 51
true 1764 441 22049 50
true 1764 441 22051 51
true 1763 441 22050 50
true 1763 441 22049 50
true 1763 441 22051 51
true 1765 441 22050 50
true 1765 441 22049 50
true 1765 441 22051 51
true 1764 441 44100 101
true 1764 441 44099 100
true 1764 441 44101 101
true 1763 441 44100 100
true 1763 441 44099 100
true 1763 441 44101 101
true 1765 441 44100 100
true 1765 441 44099 100
true 1765 441 44101 101
center win_length hop_length num_sampless num_frames
false 1764 441 22050 47
false 1764 441 22049 46
false 1764 441 22051 47
false 1763 441 22050 47
false 1763 441 22049 47
false 1763 441 22051 47
false 1765 441 22050 46
false 1765 441 22049 46
false 1765 441 22051 47
false 1764 441 44100 97
false 1764 441 44099 96
false 1764 441 44101 97
false 1763 441 44100 97
false 1763 441 44099 97
false 1763 441 44101 97
false 1765 441 44100 96
false 1765 441 44099 96
false 1765 441 44101 97

在本实验所使用的参数设置下,torchaudio 与 librosa 分帧帧数一致。

时间与帧的对应关系

librosa

根据 librosa.time_to_frames 可知对应关系为:

1
frames[i] = floor( times[i] * sr / hop_length )

根据源代码,实现 time_to_frames 时,使用 time_to_samples 和 samples_to_frames 进行转换。

根据 librosa.frames_to_time 可知对应关系为:

1
times[i] = frames[i] * hop_length / sr

根据源代码,实现 frames_to_time 时,使用 frames_to_samples 和 samples_to_time 进行转换。

以上函数中还有n_fft参数,librosa.frames_to_time 中的解释为“If given, time conversion will include an offset of n_fft // 2 to counteract windowing effects when using a non-centered STFT.”, librosa.time_to_frames 中的解释为“If given, time conversion will include an offset of - n_fft // 2 to counteract windowing effects in STFT.”。 根据以上解释,可推测在使用“center”的时频变换的情况下,调用 frames_to_time 和 time_to_frames 函数不需要 n_fft 参数。

根据以上两段代码可以推测,虽然 librosa 没有对帧率(fps)进行定义,但实现时是隐式使用 sr / hop_length 作为 fps 的。

madmom

根据madmom.utils.quantize_events的代码

1
2
3
4
5
# quantize
events *= fps
# indices to be set in the quantized array
idx = np.unique(np.round(events).astype(np.int))
quantized[idx] = 1

可知对应关系为:

1
frames[i] = round(times[i] * fps)