借助 SPICE 模型解决音高识别难题,让机器学会 “听歌识谱”

文 / Luiz Gustavo Martins、Beat Gfeller 和 Christian Frank

音高 (Pitch) 是音调中的一个属性(其他属性包括音长、音强和音色),您可以通过音高将音符描述为 “高” 或 “低”。音高由频率量化,单位为赫兹 (Hz),其中 1 Hz 相当于每秒一个周期。频率越高,音调越高。

音高检测是一个有趣的挑战 。在过去,一台机器要想识别音高,需要依靠复杂的手工信号处理算法来测量音符的频率,特别是要将相关频率与背景噪音和伴奏音乐分开。

而现在,我们可以通过机器学习来实现这一目标,也就是 SPICE 模型(SPICE:新一代自监督式基音检测模型)。SPICE 是一种预训练模型,可以从混合录音(包括噪音和伴奏音乐)中识别出基本音高。该模型可以在网络上 与 TensorFlow.js 一起使用,也可以在移动设备上 与 TensorFlow Lite 一起使用

在本教程中,我们将引导您使用 SPICE 从简短的音乐片段中提取音高。首先,我们将加载音频文件并进行处理。随后,我们将使用机器学习来解决这一问题(您会发现使用 TensorFlow Hub 进行处理是如此简单)。最后,我们将做一些后期处理和酷炫的可视化。您可以使用此 Colab notebook 来执行这些操作。

加载音频文件

该模型以原始音频样本作为输入。为便于操作,我们介绍了四种方法,您可以任选其一将 wav 输入文件导入到 Colab:

  1. 直接在 Colab 中录制自己唱歌的短片段
  2. 从您的计算机上传录音
  3. 从您的 Google 云端硬盘下载文件
  4. 从网址下载文件

您可以从这些方法中任选其一。当然在 Colab 中直接录制自己的歌声是最简单(这也最有趣且我们最推荐的方法哦~)。

模型支持多种格式的音频录音(如:您可以使用 Android 应用、台式电脑或浏览器进行录制)。不过,将您的音频转换成模型所要求的格式可能并非易事。为了帮助您执行此操作,模型提供了一个辅助函数 convert_audio_for_model 来将您的 wav 文件转换为一个音频通道的正确格式,采样率为 16 KHz。

在本文的剩余部分,我们将使用此文件进行实验:
https://tfugcs.andfun.cn/download.tensorflow.org/data/c-scale-metronome.wav

准备音频数据

音频加载完毕后,接下来可以使用声谱图将其可视化,这样更直观的看到频率随时间的变化情况。我们此处使用对数频响反应表 (Logarithmic Frequency Scale),以更清晰地呈现歌声的属性(请注意:此步骤仅用于可视化,运行模型时非必须执行项)。


注:此图使用 Librosa 库创建而成。更多信息,请访问 Github

之后我们还需要进行一次转换操作,即:必须将输入标准化为 -1 和 1 之间的浮点数。在上一步中,我们已将音频转换为 16 位格式(使用辅助函数 convert_audio_for_model )。要对其进行标准化,我们只需要将所有值除以 216 。具体的来说,在我们的代码中,将所有值除以 MAX_ABS_INT16:

audio_samples = audio_samples / float (MAX_ABS_INT16)

执行模型

从 TensorFlow Hub 加载模型很简单。您只需将加载模型时指定 模型地址 即可。

model = hub.load ("https://hub.tensorflow.google.cn/google/spice/2")

注: 这里有一处小彩蛋,TensorFlow Hub 中的所有模型地址都可下载与阅读文档。因此,如果通过浏览器访问该链接,您可以阅读有关如何使用模型的文档,并详细了解模型的训练过程。

现在,我们可以通过使用使用从 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] 范围转换为以 Hz 为单位的绝对值。我们可以使用 Colab notebook 中提供的函数来完成该转换操作:

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 类似的工具,但一定要选择自己最喜欢的歌手!

致谢

本文由以下人员共同完成:Beat Gfeller、Christian Frank、Dominik Roblek、Matt Sharifi、Marco Tagliasacchi 和 Mihajlo Velimirović( SPICE:新一代自监督式基音检测模型 )。以及 Polong Lin 帮忙审阅并提出了很棒的想法,Jaesung Chung 为创建 TF Lite 版模型提供了支持,在此一并表示感谢。

image
原文:From singing to musical scores: Estimating pitch with SPICE and Tensorflow Hub
中:TensorFlow 公众号