2017-05-12 169 views
0

我使用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种方法得到的结果只是非常奇怪。

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

回答

3

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

你的代码对我来说看起来很好。

你想要检测的频率是你的音调的基本频率(这个问题也被称为“f0估计”)。

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

想一想,您的基本频率将会在哪个范围内。对于声学吉他,即E2(82 Hz)到F6(1,397 Hz)。这意味着您可以摆脱低于〜80 Hz和高于〜1400 Hz的任何信号(对于带通示例,请参阅here)。过滤后,做你的峰值检测来找到音高(假设基本实际上有最多的能量)。

另一种策略可能是忽略每个切片的第一个X样本,因为它们往往是撞击式的,本质上不是谐波,无论如何也不会给您太多的信息。所以,在你的切片中,只要看看你样品的最后~90%。

也就是说,f0或基频估计有大量工作。一个好的起点是ISMIR论文。

最后但并非最不重要的,Librosa的piptrack函数可能只是你想要的。