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

使用linux或python查找mp3声音样本的时间戳,python,linux,audio,signal-processing,mp3,Python,Linux,Audio,Signal Processing,Mp3,我正在慢慢地做一个项目,如果计算机能在mp3文件中找到某个样本出现的位置,这将非常有用。我会把这个问题限制在相当精确的音频片段上,而不仅仅是同一个乐队录制的不同歌曲中的合唱,因为这更像是某种机器学习问题。我在想,如果它没有添加任何噪声,并且来自同一个文件,那么它应该可以在没有机器学习的情况下找到它出现的时间,就像grep可以在文本文件中找到单词出现的行一样 如果你没有mp3,你可以通过网络上的一些公共音乐设置问题,因此没有人抱怨: curl https://web.archive.org/web

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

如果你没有mp3,你可以通过网络上的一些公共音乐设置问题,因此没有人抱怨:

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秒之间剪下一个位子(这个位子是拉拉…):

文件夹中的两个文件:

$ 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,我还没有考虑性能,只是想把它完成,但希望有一些可扩展性,如在半小时内找到10秒的某处

在我试图自己解决这一问题时,我一直在研究以下资源:

他是个很好的候选人


MP3是一种有趣的格式。基础数据存储在“帧”中,每帧长0.026秒。每一帧都是声波的快速傅里叶变换,根据大小和比特率等,以不同程度的质量进行编码。。在您的情况下,您确定MP3具有匹配的比特率吗?如果是这样的话,一个相对简单的grep风格的方法应该是可能的,因为您选择了帧边界。然而,情况完全可能并非如此

对于真正的解决方案,您需要在某种程度上处理mp3文件,以抽象出编码。但是,由于比特率和可能的帧对齐可能不同,因此无法保证即使对于匹配的声音,结果波也会匹配。这小小的机会让事情变得更加困难

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

首先,将MP3转换为Wave。我知道让它保持压缩状态会很好,但我认为波浪导向是我们唯一的希望。然后,使用高通滤波器尝试去除音频压缩的任何伪影,这些伪影在样本之间会有所不同。一旦你有两个波形,它应该是相对直接的找到波中的子波。您可以在可能的起始位置迭代并减去波浪。当你接近零时,你就知道你接近了。

正如在的中所建议的那样,一旦文件转换为.wav格式,处理音频就会容易得多

您可以使用的:

然后找到样本的位置主要是获取源(
godsavethequeen.wav
,在本例中)和要查找的样本(
gstq\u sample.wav
)之间的函数峰值。本质上,这将找到样本看起来最像源中相应部分的偏移。这可以通过使用python来完成

抛出一个小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文件具有相同的采样率(并根据需要重新采样),但是您也可以确保它们在使用
-ar 44100
参数从.mp3格式转换为
ffmpeg
时始终相同,谢谢,很高兴看到有人知道这一点。我在终端上轻松地从.mp3转换成.wav,文件至少是原来的两倍大。当你说“迭代通过可能的起始位置并减去波,当你接近零时,你知道你已经接近了。”时,直觉上也有这样的想法。获取样本并在更长的mp3/wav时间内将其迭代到不同的位置。我们再次读取。麻烦你多说一点关于如何在波中找到子波的问题,只要说出技术或技术选择就行了。听起来你现在正忙着做这类事情..天哪,你已经做了,太棒了<代码>开始30.0结束32.99997324263035非常有趣。。可以在您的个人资料中看到您对信号处理和fft的了解。在我的机器上花了0.7秒,快。。我们将在每行代码中查看中间结果。这太棒了。如果音频文件中有重复的声音,是否有办法获得多个开始-结束时间对?
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
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))