使用 linux 或 python 查找 mp3 声音样本的时间戳

find the timestamp of a sound sample of an mp3 with linux or python

我正在慢慢地进行一个项目,如果计算机可以找到 where 在 mp3 文件中出现某个样本,那将非常有用。我会将这个问题限制为相当精确的音频片段,而不仅仅是例如同一乐队在不同录音中的歌曲中的合唱,这将成为某种机器学习问题。我在想,如果它没有添加噪音并且来自同一个文件,那么它应该可以在没有机器学习的情况下找到它发生的时间,就像 grep 可以在文本文件中找到单词出现的行一样。

如果您身边没有 mp3,可以使用 public 域中网络上的一些音乐来设置问题,这样就没有人抱怨了:

curl https://web.archive.org/web/20041019004300/http://www.navyband.navy.mil/anthems/ANTHEMS/United%20Kingdom.mp3 --output godsavethequeen.mp3

一分钟长:

exiftool godsavethequeen.mp3 | grep Duration
Duration                        : 0:01:03 (approx)

现在剪掉 30 到 33 秒之间的一段(la la la la..):

ffmpeg -ss 30 -to 33 -i godsavethequeen.mp3 gstq_sample.mp3

文件夹中的两个文件:

$ ls -la
-rw-r--r-- 1 cardamom cardamom   48736 Jun 23 00:08 gstq_sample.mp3
-rw-r--r-- 1 cardamom cardamom 1007055 Jun 22 23:57 godsavethequeen.mp3

出于某种原因,exiftool 似乎高估了样本的持续时间:

$ exiftool gstq_sample.mp3 | grep Duration
Duration                        : 6.09 s (approx)

..但我想它只是近似值,就像它告诉你的那样。

这就是我想要的:

$ findsoundsample gstq_sample.mp3 godsavethequeen.mp3
start 30 end 33

我很高兴它是一个 bash 脚本或 python 解决方案,甚至使用某种 python 库。有时如果你使用了错误的工具,解决方案可能有效但看起来很糟糕,所以无论哪个工具更合适。这是一个一分钟的 mp3,还没有考虑性能只是为了完成它,但想要一些可扩展性,例如在半小时内找到十秒。

在尝试自己解决这个问题时,我一直在查看以下资源:

https://github.com/craigfrancis/audio-detect

https://madmom.readthedocs.io/en/latest/introduction.html

Reading *.wav files in Python

https://github.com/aubio/aubio

aubionset是一个很好的候选人

https://willdrevo.com/fingerprinting-and-audio-recognition-with-python/

MP3 是一种有趣的格式。底层数据存储在 'Frames' 中,每 0.026 秒长。每一帧都是声波的快速傅里叶变换,根据大小和比特率等以不同程度的质量编码。在你的情况下,你确定 mp3 具有匹配的比特率吗?如果他们这样做了,一个相对简单的 grep 风格的方法应该是可能的,因为你 select 在框架边界上。然而,情况完全有可能并非如此。

对于真正的解决方案,您需要在某种程度上处理 mp3 文件,以抽象出编码。但是,即使对于匹配的声音,也不能保证生成的波形匹配,因为比特率和可能的帧对齐可能不同。如此小的几率使它 变得更难

我会告诉你我解决这个问题的方法,但值得注意的是,这不是做事的完美方法,只是我最好的方法。即使是同一个文件,也不能保证帧边界对齐,所以我认为你需要采取一种非常面向波的方法,而不是面向数据的方法。

首先,将 mp3 转换为 wave。我知道让它保持压缩状态会很棒,但我再次认为面向波浪是我们唯一的希望。然后,使用高通滤波器尝试去除样本之间可能存在差异的任何音频压缩伪影。一旦有了两个波形,就可以相对直接地在波形中找到小波。您可以遍历可能的起始位置并减去波浪。当您接近零时,您就知道自己接近了。

Carson's 中所建议,一旦文件转换为 .wav 格式,处理音频就会变得容易得多。

您可以使用 Wernight's answer on reading mp3 in python:

ffmpeg -i godsavethequeen.mp3 -vn -acodec pcm_s16le -ac 1 -ar 44100 -f wav godsavethequeen.wav
ffmpeg -i gstq_sample.mp3 -vn -acodec pcm_s16le -ac 1 -ar 44100 -f wav gstq_sample.wav

然后找到样本的位置主要是获得cross-correlation function between the source (godsavethequeen.wav in this case) and the sample to look for (gstq_sample.wav). In essence, this will find the shift at which the sample looks the most like the corresponding portion in the source. This can be done with python using scipy.signal.correlate的峰值。

抛出一个小 python 脚本来做到这一点看起来像:

import numpy as np
import sys
from scipy.io import wavfile
from scipy import signal

snippet = sys.argv[1]
source  = sys.argv[2]

# read the sample to look for
rate_snippet, snippet = wavfile.read(snippet);
snippet = np.array(snippet, dtype='float')

# read the source
rate, source = wavfile.read(source);
source = np.array(source, dtype='float')

# resample such that both signals are at the same sampling rate (if required)
if rate != rate_snippet:
  num = int(np.round(rate*len(snippet)/rate_snippet))
  snippet = signal.resample(snippet, num)

# compute the cross-correlation
z = signal.correlate(source, snippet);

peak = np.argmax(np.abs(z))
start = (peak-len(snippet)+1)/rate
end   = peak/rate

print("start {} end {}".format(start, end))

请注意,为了采取有效措施,我进行了检查以确保两个 .wav 文件具有相同的采样率(并根据需要重新采样),但您也可以确保它们在转换时始终相同从 .mp3 格式使用 -ar 44100 参数到 ffmpeg.