在MP3文件中查找节拍


27

在此挑战中,您的任务是以mp3格式进行简单录音,并在文件中找到节拍的时间偏移。这里有两个示例录音:

https://dl.dropboxusercontent.com/u/24197429/beats.mp3 https://dl.dropboxusercontent.com/u/24197429/beats2.mp3

这是第三张录音,噪音比前两张大得多:

https://dl.dropboxusercontent.com/u/24197429/noisy-beats.mp3

例如,第一张录音的时长为65秒,其中包含准确的(除非我记错了!)76拍。您的工作是设计一个程序,该程序将一个mp3文件作为输入,并输出以毫秒为单位的文件中拍子的时间偏移序列。当然,当吉他演奏者弹奏一根或多根琴弦时,就会发生拍子。

您的解决方案必须:

  • 处理具有类似“复杂性”的任何mp3文件。它可能无法发出嘈杂的录音或无法快速播放旋律-我不在乎。
  • 相当精确。公差为+/- 50毫秒。因此,如果心跳发生在1500毫秒并且您的解决方案报告为1400,那么这是不可接受的。
  • 仅使用免费软件。允许调用ffmpeg,就像使用任何免费的第三方软件来选择您的语言一样。

获胜标准是能够成功检测出节拍,尽管随附文件中有噪音。如果出现平局,则以最短的解决方案为准(不计算第3方代码的长度)。


1
尽管这看起来很有趣,但这是一场比赛,您应该比“正确”更准确地定义获胜标准。
Fabinout 2014年


18
一场好的比赛会把兴趣部分隔离开。在这里,您似乎对节拍识别感兴趣,这肯定是一个有趣的DSP问题。那么,为什么要让程序处理(或外包)MP3文件格式的复杂性呢?通过采用RAW(允许的有关采样率,位深和字节序的假设)或WAV(类似),可以改善该问题。
彼得·泰勒

3
比赛的重点是处理所有这些部分。如果难以与mp3交互,则可能难以在golfscript中解决该问题。仍然要对挑战进行充分说明,并且(确实)完全是对主题的挑战,因此消极情绪令人沮丧。
比约恩·林奎斯特

8
@BjörnLindqvist您不应该建议改善自己的内心。除非以前的一些评论已删除,否则我在这里看不到任何负面评论,而只是提出改进建议。
Gareth 2014年

Answers:


6

Python 2.7 492字节(仅beats.mp3)

这个答案可以识别出节拍 beats.mp3,但不能识别beats2.mp3或的所有音符noisy-beats.mp3。描述代码后,我将详细说明原因。

这使用PyDub( https://github.com/jiaaro/pydub)读取MP3。所有其他处理都是NumPy。

高尔夫守则

采用带有文件名的单个命令行参数。它将以毫秒为单位输出每个拍子。

import sys
from math import *
from numpy import *
from pydub import AudioSegment
p=square(AudioSegment.from_mp3(sys.argv[1]).set_channels(1).get_array_of_samples())
n=len(p)
t=arange(n)/44.1
h=array([.54-.46*cos(i/477) for i in range(3001)])
p=convolve(p,h, 'same')
d=[p[i]-p[max(0,i-500)] for i in xrange(n)]
e=sort(d)
e=d>e[int(.94*n)]
i=0
while i<n:
 if e[i]:
  u=o=0
  j=i
  while u<2e3:
   u=0 if e[j] else u+1
   #u=(0,u+1)[e[j]]
   o+=e[j]
   j+=1
  if o>500:
   print "%g"%t[argmax(d[i:j])+i]
  i=j
 i+=1

非高尔夫代码

# Import stuff
import sys
from math import *
from numpy import *
from pydub import AudioSegment

# Read in the audio file, convert from stereo to mono
song = AudioSegment.from_mp3(sys.argv[1]).set_channels(1).get_array_of_samples()

# Convert to power by squaring it
signal = square(song)
numSamples = len(signal)

# Create an array with the times stored in ms, instead of samples
times = arange(numSamples)/44.1

# Create a Hamming Window and filter the data with it. This gets rid of a lot of
# high frequency stuff.
h = array([.54-.46*cos(i/477) for i in range(3001)])
signal = convolve(signal,h, 'same') #The same flag gets rid of the time shift from this

# Differentiate the filtered signal to find where the power jumps up.
# To reduce noise from the operation, instead of using the previous sample,
# use the sample 500 samples ago.
diff = [signal[i] - signal[max(0,i-500)] for i in xrange(numSamples)]

# Identify the top 6% of the derivative values as possible beats
ecdf = sort(diff)
exceedsThresh = diff > ecdf[int(.94*numSamples)]

# Actually identify possible peaks
i = 0
while i < numSamples:
 if exceedsThresh[i]:
  underThresh = overThresh = 0
  j=i
  # Keep saving values until 2000 consecutive ones are under the threshold (~50ms)
  while underThresh < 2000:
   underThresh =0 if exceedsThresh[j] else underThresh+1
   overThresh += exceedsThresh[j]
   j += 1
  # If at least 500 of those samples were over the threshold, take the maximum one
  # to be the beat definition
  if overThresh > 500:
   print "%g"%times[argmax(diff[i:j])+i]
  i=j
 i+=1

为什么我错过其他文件上的笔记(以及为什么它们难以置信的挑战)

我的代码着眼于信号功率的变化,以便找到注释。对于beats.mp3,这确实很好。该频谱图显示了功率如何随时间(x轴)和频率(y轴)分配。我的代码基本上将y轴折叠为单行。 beats.jpeg 视觉上,很容易看出节拍在哪里。有一条黄线不断缩小。我强烈建议您在听beats.mp3频谱图的同时聆听它的工作原理。

接下来,我将转到noisy-beats.mp3(因为实际上比beats2.mp3.. 容易。 noisy-beats.png请再次查看是否可以继续录音。大多数行都比较暗淡,但是仍然存在。但是,在某些地方,当安静的音符开始,这使找到它们变得特别困难,因为现在,您必须通过频率(y轴)的变化而不是幅度的变化来找到它们。

beats2.mp3极具挑战性。这是声谱图 beats2.jpeg 在第一行中,有几行,但有些音符确实在这些行上流血。为了可靠地识别音符,您必须开始跟踪音符的音高(基本音和和声),并查看音符的变化位置。一旦第一位开始工作,第二位的速度将是节奏的两倍!

基本上,为了可靠地识别所有这些,我认为需要花哨的检测代码。看来对于DSP班的某个人来说,这将是一个不错的最终项目。


我认为这是不允许的,因为它不能满足所有要求。好的答案,但是需要一些工作。
Rɪᴋᴇʀ

是的,我有点失望,这种方法未能如我所愿。我认为这可能会帮助想要刺中它的其他人。如果我本周有空闲时间,我希望尝试一种基于FFT的新方法,该方法应能提供更好的结果。
Dominic A.

好的,很酷。干得好。
Rɪᴋᴇʀ
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.