切片音频信号以检测音高

时间:2017-05-12 20:37:16

标签: python signals signal-processing pitch-tracking librosa

我正在使用Librosa来录制单声道吉他音频信号。

我认为,根据开始时间“切片”信号是一个良好的开端,以便在正确的时间检测音符变化。

Librosa提供function,可在发病时间之前检测局部最小值。我检查了那些时间,他们是正确的。

这是原始信号的波形和最小值的时间。

[ 266240  552960  840704 1161728 1427968 1735680 1994752]

waveform

播放的旋律是E4,F4,F#4 ......,B4。

因此理想结果应为:330Hz,350Hz,......,493Hz(约)。

如您所见,minima数组中的时间代表音符播放前的时间。

然而,在切片信号上(10-12秒,每片只有一个音符),我的频率检测方法效果非常差。我很困惑因为我的代码中看不到任何错误:

  y, sr = librosa.load(filename, sr=40000)

  onset_frames = librosa.onset.onset_detect(y=y, sr=sr)
  oenv = librosa.onset.onset_strength(y=y, sr=sr)

  onset_bt = librosa.onset.onset_backtrack(onset_frames, oenv)

  # Converting those times from frames to samples.
  new_onset_bt = librosa.frames_to_samples(onset_bt)

  slices = np.split(y, new_onset_bt[1:])
  for i in range(0, len(slices)):
    print freq_from_hps(slices[i], 40000)
    print freq_from_autocorr(slices[i], 40000)
    print freq_from_fft(slices[i], 40000)

freq_from函数直接来自here

我认为这只是方法的精确度差,但我得到了一些疯狂的结果。具体来说,freq_from_hps返回:

1.33818658287
1.2078047577
0.802142642257
0.531096911977
0.987532329094
0.559638134414
0.953497587952
0.628980979055

这些值应该是8个相应切片的8个节距(以Hz为单位!)。

freq_from_fft返回类似的值,而freq_from_autocorr返回一些更“正常”的值,但也会返回10000Hz附近的一些随机值:

242.748000585
10650.0394232
275.25299319
145.552578747
154.725859019
7828.70876515
174.180627765
183.731497068

这是整个信号的频谱图:

wholespectrogram

这就是,例如,切片1的谱图(E4音符): spectrograme4

如您所见,切片已正确完成。但是有几个问题。首先,频谱图中存在一个八度音程问题。我期待着一些问题。但是,我从上面提到的3种方法得到的结果非常奇怪。

这是我的信号处理理解或我的代码的问题吗?

1 个答案:

答案 0 :(得分:3)

  

这是我的信号处理理解或我的代码的问题吗?

您的代码对我来说很好。

您想要检测的频率是您的音高的基本频率(问题也称为" f0估计")。

所以在使用像freq_from_fft这样的东西之前我会对信号进行带通滤波,以消除垃圾瞬态和低频噪声 - 信号中的东西,但与你的问题无关。

考虑一下你的基本频率将在哪个范围内。对于一个具有E2(82 Hz)到F6(1,397 Hz)的原声吉他。这意味着您可以摆脱低于~80 Hz且高于~1,400 Hz的任何物体(对于带通示例,请参阅here)。过滤后,进行峰值检测以找到音高(假设基波实际上具有最大能量)。

另一种策略可能是忽略每个切片的第一个X样本,因为它们往往是打击乐而不是谐波,并且无论如何都不会给你提供太多信息。因此,在您的切片中,只需查看最后~90%的样本。

众所周知,f0或基频估计有很多工作要做。一个很好的起点是ISMIR篇论文。

最后,但并非最不重要的是,Librosa的piptrack功能可以满足您的需求。