Librosa音高跟踪 - 短时傅里叶变换

19
我正在使用这个算法来检测这个音频文件的音高。正如你所听到的,它是在吉他上演奏的E2音符,背景中有一些噪音。
我使用STFT生成了这个频谱图:spectrogram 而我是这样使用上面链接的算法的:
y, sr = librosa.load(filename, sr=40000)
pitches, magnitudes = librosa.core.piptrack(y=y, sr=sr, fmin=75, fmax=1600)

np.set_printoptions(threshold=np.nan)
print pitches[np.nonzero(pitches)]

因此,我得到了在fminfmax之间的几乎所有可能的频率。那么我需要用piptrack方法的输出来发现时间帧的基频是什么?
更新
尽管如此,我仍然不确定这两个二维数组代表什么。假设我想找出第5帧中82Hz的强度。我可以使用STFT函数来完成这个任务,该函数只返回一个二维矩阵(用于绘制频谱图)。
但是,piptrack还做了一些额外的工作,这可能很有用,但我真的不明白是什么。 pitches [f,t]包含bin f,time t的瞬时频率。这是什么意思?如果我想在时间帧t中找到最大频率,我需要:
1. 转到magnitudes[] [t]数组,找到具有最大幅度的bin。 2. 将bin分配给变量f。 3. 找到pitches [b] [t]以查找属于该bin的频率?

你可能没有正确地看待结果。根据文档,pitches 包含 fminfmax 之间每个 FFT bin 的频率。尝试检查 magnitudes 中的非零元素,并查看它们对应的音高。 - Linuxios
好的,我有点困惑。bin 究竟代表什么?如果 pitches 是一个二维数组,那么例如 f = 3,t = 5 代表什么? - pavlos163
在“时间”t处,bin#f 表示的频率,就是文档所说的。bins 只是 FFT 将频率谱分成的小段。例如,100Hz 和 200Hz 之间的区域可以分成10个 bins,这样,bin#2 就表示110Hz 到 120Hz 的频率。 - Linuxios
好的,那么(n,m)元素的值代表什么?我理解矩阵的一个维度是存储区间,另一个维度是时间。但是这个值代表什么,为什么它会随着时间而改变呢? - pavlos163
实际上,频谱图看起来并不那么糟糕。对于音符E,你应该在82.4赫兹、165(2 x 84.4)、247(3 x 84)和329(4 x 84)等频率找到谐波(他错误地称之为“音高”)。出现的最大值——主导水平线——似乎大致与这些频率相一致。 - James Paul Millard
显示剩余4条评论
3个回答

15
音高检测是一个棘手的问题,通常并不直观。我对于这个特定函数的源代码文档的记录方式并不满意——开发者似乎将'谐波'与'音高'混淆了。
当吉他或钢琴演奏出一个音符(一个“音高”)时,我们听到的不仅仅是一种声音振动频率,而是多个以数学相关频率发生的声音振动构成的复合音。典型的音高跟踪技术包括在FFT的结果中搜索对应于预期谐波频率的某些频率值的幅度。例如,如果我们按下钢琴上的中央C键,则复合体的谐波的单独频率将从261.6 Hz作为基本频率开始,523 Hz将是第二谐波,785 Hz将是第三谐波,1046 Hz将是第四谐波等。后面的谐波是基本频率261.6 Hz的整数倍(例如:2 x 261.6 = 523,3 x 261.6 = 785,4 x 261.6 = 1046)。然而,谐波所在的频率是对数间隔的,但FFT使用线性间隔。通常情况下,FFT的垂直间距在低频处不能得到足够的分辨率。
由于这个原因,当我写一个音高检测应用程序(PitchScope Player)时,我选择创建一个对数间隔的DFT,而不是FFT,这样我可以专注于音乐感兴趣的精确频率(请参见附加的吉他独奏3秒钟的自定义DFT图表)。如果你认真考虑追求音高检测,应该考虑更多地阅读这个主题,查看其他示例代码(我的链接如下),并考虑编写自己的函数来测量频率。

https://en.wikipedia.org/wiki/Transcription_(music)#Pitch_detection

https://github.com/CreativeDetectors/PitchScope_Player

enter image description here


你好,如果我想从librosa计算F0,应该使用哪些参数呢?我知道在PRAAT中很容易实现,但是在librosa中我不知道该怎么做。 - abdoulsn

11
原来在某个帧 t 选择音高的方法很简单:
def detect_pitch(y, sr, t):
  index = magnitudes[:, t].argmax()
  pitch = pitches[index, t]

  return pitch

首先通过查看 magnitudes 数组获取最强频率的二进制,然后在 pitches[index, t] 处找到音高。


我在 https://github.com/miromasat/pitch-detection-librosa-python/blob/master/script_final.py 找到了有关音高跟踪的代码。在这段代码中,pitch_track = np.max(pitches[:,t])。我认为你是正确的,但我找不到支持它的材料。你能给我一些关于你的代码的材料吗? - Rosand Liu
你好,如果我想要使用librosa计算F0,我应该使用哪些参数呢?我知道在PRAAT中很容易实现,但是我不知道如何在librosa中实现。 - abdoulsn

1
找到整个音频段的音调:
def detect_pitch(y, sr):
    pitches, magnitudes = librosa.core.piptrack(y=y, sr=sr, fmin=75, fmax=1600)
    # get indexes of the maximum value in each time slice
    max_indexes = np.argmax(magnitudes, axis=0)
    # get the pitches of the max indexes per time slice
    pitches = pitches[max_indexes, range(magnitudes.shape[1])]
    return pitches

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接