0

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

我认为,根据开始时间“分割”信号,以在正确的时间检测音符变化,这将是一个好的开始。

Librosa 提供了一个功能,可以在开始时间之前检测局部最小值。我检查了这些时间,它们是正确的。

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

[ 266240  552960  840704 1161728 1427968 1735680 1994752]

波形

演奏的旋律是 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函数直接取自这里

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

1.33818658287
1.2078047577
0.802142642257
0.531096911977
0.987532329094
0.559638134414
0.953497587952
0.628980979055

这些值应该是 8 个相应切片的 8 个音高(以赫兹为单位!)。

freq_from_fft返回相似的值,同时freq_from_autocorr返回一些更“正常”的值,但也返回一些接近 10000Hz 的随机值:

242.748000585
10650.0394232
275.25299319
145.552578747
154.725859019
7828.70876515
174.180627765
183.731497068

这是整个信号的频谱图:

全谱图

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

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

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

4

1 回答 1

3

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

你的代码对我来说看起来不错。

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

因此,在使用诸如带通滤波器之类的东西之前,freq_from_fft我会对信号进行带通滤波,以消除垃圾瞬态和低频噪声——信号中存在但与您的问题无关的东西。

想一想,您的基本频率将在哪个范围内。对于 E2 (82 Hz) 到 F6 (1,397 Hz) 的原声吉他。这意味着您可以摆脱低于 ~80 Hz 和高于 ~1,400 Hz 的任何东西(有关带通示例,请参见此处)。过滤后,进行峰值检测以找到音高(假设基波实际上具有最多的能量)。

另一种策略可能是,忽略X每个切片的第一个样本,因为它们往往是打击乐的,本质上不是和声的,无论如何也不会给你太多信息。因此,在您的切片中,只需查看最后约 90% 的样本。

话虽如此,f0 或基频估计还有大量工作要做。一个很好的起点是ISMIR论文。

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

于 2017-05-15T09:26:42.390 回答