从唱歌到乐谱:使用 SPICE 和 TensorFlow Hub 估算音高
2020 年 6 月 17 日
发布者 Luiz Gustavo Martins,Beat Gfeller 和 Christian Frank

音高是音乐音调的属性之一(还有持续时间、强度和音色),它可以让你描述一个音符是“高”还是“低”。音高由频率量化,以赫兹 (Hz) 为单位,1 赫兹对应每秒一个周期。频率越高,音符就越高。

音高检测是一个有趣的挑战。从历史上看,要让机器理解音高,它需要依赖复杂的、手工制作的信号处理算法来测量音符的频率,尤其是要将相关频率与背景噪声和伴奏乐器分离。如今,我们可以使用机器学习来做到这一点,更具体地说,可以使用 SPICE 模型(SPICE:自监督音高估计)。

SPICE 是一种经过预训练的模型,可以从混合音频录制(包括噪声和伴奏乐器)中识别出基频。该模型也可以在 Web 上使用 TensorFlow.js,以及在移动设备上使用 TensorFlow Lite

在本教程中,我们将引导你使用 SPICE 从短小的音乐片段中提取音高。首先,我们将加载音频文件并对其进行处理。然后,我们将使用机器学习来解决这个问题(你会发现,使用 TensorFlow Hub 是多么容易)。最后,我们将进行一些后处理和一些很酷的可视化操作。你可以按照此 Colab 笔记本 的步骤进行操作。

加载音频文件

该模型期望以原始音频样本作为输入。为了帮助你做到这一点,我们展示了四种方法,你可以使用这些方法将输入 wav 文件导入 Colab
  1. 在 Colab 中直接录制一段简短的你唱歌的片段
  2. 从你的计算机上传录制文件
  3. 从你的 Google Drive 下载文件
  4. 从 URL 下载文件
你可以选择其中任何一种方法。在 Colab 中直接录制自己唱歌是最容易尝试的方法,也是最有趣的方法。

音频可以用多种格式录制(例如,你可能会使用 Android 应用、台式电脑或浏览器录制音频),将你的音频转换为模型所需的精确格式可能很具有挑战性。为了帮助你做到这一点,我们提供了一个名为 convert_audio_for_model 的辅助函数,用于将你的 wav 文件转换为正确的格式,即单声道音频,采样率为 16khz。

在本博文的其余部分,我们将使用此文件

准备音频数据

现在我们已经加载了音频,我们可以使用频谱图对其进行可视化,频谱图显示了随时间变化的频率。在这里,我们使用对数频率刻度,以使唱歌更清晰地显示出来(请注意,此步骤不是运行模型所必需的,它只是用于可视化)。
注意:此图表是使用 Librosa 库创建的。你可以找到更多信息 此处
我们还需要进行最后一次转换。输入必须归一化为 -1 到 1 之间的浮点数。在前面的步骤中,我们使用辅助函数 convert_audio_for_model 将音频转换为 16 位格式。为了对其进行归一化,我们只需要将所有值除以 216,或者在我们的代码中,除以 MAX_ABS_INT16
audio_samples = audio_samples / float(MAX_ABS_INT16)

执行模型

从 TensorFlow Hub 加载模型很简单。你只需使用 load 方法,并提供模型的 URL
model = hub.load("https://tfhub.dev/google/spice/2")
注意:这里有一个有趣的细节是,Hub 中的所有模型 URL 都可以用于下载,也可以用于读取文档,因此,如果你将浏览器指向该链接,你就可以阅读有关如何使用该模型以及如何训练该模型的更多信息。

现在,我们可以通过传递归一化的音频样本,使用从 TensorFlow Hub 加载的模型
output = model.signatures["serving_default"](tf.constant(audio_samples, tf.float32))

pitch_outputs = output["pitch"]
uncertainty_outputs = output["uncertainty"]
在这一点上,我们有了音高估计和不确定性(每个检测到的音高)。将不确定性转换为置信度(confidence_outputs = 1.0 - uncertainty_outputs),我们可以很好地了解结果: 如图所示,对于一些预测(特别是在没有歌唱声音的地方),置信度非常低。让我们只保留高置信度的预测,删除置信度低于 0.9 的结果。 为了确认模型是否正常工作,让我们将音高从 [0.0, 1.0] 范围转换为赫兹的绝对值。要进行此转换,我们可以使用 Colab 笔记本中提供的函数
def output2hz(pitch_output):
  # Constants taken from https://tfhub.dev/google/spice/2
  PT_OFFSET = 25.58
  PT_SLOPE = 63.07
  FMIN = 10.0;
  BINS_PER_OCTAVE = 12.0;
  cqt_bin = pitch_output * PT_SLOPE + PT_OFFSET;
  return FMIN * 2.0 ** (1.0 * cqt_bin / BINS_PER_OCTAVE)

confident_pitch_values_hz = [ output2hz(p) for p in confident_pitch_outputs_y ]
如果我们将这些值绘制在频谱图上,我们可以看到预测与主要音高的匹配程度,主要音高可以在频谱图中显示为更强的线条: 成功了!我们成功地从歌手的声音中提取了相关的音高。

请注意,对于此特定示例,基于频谱图的启发式方法也可以用于提取音高。总的来说,基于 ML 的模型比手工制作的信号处理方法表现得更好,尤其是在音频中存在背景噪声和伴奏乐器的情况下。有关 SPICE 与基于频谱图的算法 (SWIPE) 的比较,请参见 此处

转换为音乐音符

为了使音高信息更有用,我们还可以找到每个音高所代表的音符。为此,我们将使用一些数学方法将频率转换为音符。一个重要的观察结果是,与推断的音高值相反,转换后的音符是量化的,因为这种转换涉及舍入(笔记本中的函数 hz2offset 使用了一些数学方法,你可以找到很好的解释 此处)。此外,我们还需要按时间将预测分组在一起,以便获得更长的持续音符,而不是一系列相等的音符。这种时间量化并不容易,我们的笔记本只是实现了一些启发式方法,这些方法通常不会产生完美的乐谱。但是,对于具有相同持续时间的音符序列,它确实有效,就像我们的示例一样。

我们首先根据置信度低的预测添加休止符(无歌唱间隔)。下一步更具挑战性。当一个人自由地唱歌时,旋律可能相对于音符可以代表的绝对音高值存在偏差。因此,要将预测转换为音符,需要校正这种可能的偏差。

在计算偏移量并尝试不同的速度(多少个预测构成一个八分音符)后,我们最终得到了这些渲染的音符: 我们还可以使用 music21 将转换后的音符导出到 MIDI 文件
converted_audio_file_as_midi = converted_audio_file[:-4] + '.mid'
fp = sc.write('midi', fp=converted_audio_file_as_midi)

下一步是什么?

使用 TensorFlow Hub,你可以轻松找到很棒的模型,例如 SPICE 和许多其他模型,以帮助你解决机器学习挑战。继续探索 模型,使用 Colab 玩一玩,也许尝试构建一些类似于 FreddieMeter 的东西,但使用你最喜欢的歌手!

我们渴望知道你能想出什么。在社交媒体上与我们分享你的想法,并在你的帖子中添加 #TFHub。

致谢

本博文基于 Beat Gfeller、Christian Frank、Dominik Roblek、Matt Sharifi、Marco Tagliasacchi 和 Mihajlo Velimirović 在 SPICE:自监督音高估计 上的工作。还要感谢 Polong Lin 对这篇博文的审阅并提出了很棒的想法,以及 Jaesung Chung 对创建该模型的 TF Lite 版本的支持。
下一篇文章
From singing to musical scores: Estimating pitch with SPICE and Tensorflow Hub

发布者 Luiz Gustavo Martins,Beat Gfeller 和 Christian Frank

音高是音乐音调的属性之一(还有持续时间、强度和音色),它可以让你描述一个音符是“高”还是“低”。音高由频率量化,以赫兹 (Hz) 为单位,1 赫兹对应每秒一个周期。频率越高,音符就越高。

音高检测是一个有趣的挑战……