Finde die Beats in einer MP3-Datei

27

In dieser Herausforderung besteht Ihre Aufgabe darin, eine einfache Aufnahme im MP3-Format zu erstellen und die Zeitversätze der Beats in der Datei zu ermitteln. Zwei Beispielaufnahmen sind hier:

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

Hier ist die dritte Aufnahme mit viel mehr Rauschen als die beiden vorherigen:

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

Zum Beispiel ist die erste Aufnahme 65 Sekunden lang und enthält genau 76 Beats (es sei denn, ich habe falsch gezählt!). Ihre Aufgabe ist es, ein Programm zu entwickeln, das eine solche MP3-Datei als Eingabe verwendet und eine Folge der Zeitversätze in Millisekunden der Beats in der Datei ausgibt. Ein Beat wird natürlich definiert, wenn der Gitarrist eine oder mehrere Saiten zupft.

Ihre Lösung muss:

  • Arbeiten Sie an jeder MP3-Datei mit ähnlicher "Komplexität". Es kann zu lauten Aufnahmen oder zu schnell gespielten Melodien kommen - das ist mir egal.
  • Sei ziemlich genau. Die Toleranz beträgt +/- 50 ms. Wenn der Beat also bei 1500 ms auftritt und Ihre Lösung 1400 meldet, ist dies nicht akzeptabel.
  • Verwenden Sie nur freie Software. Das Aufrufen von ffmpeg ist zulässig, da jede frei verfügbare Software von Drittanbietern für die Sprache Ihrer Wahl verwendet wird.

Das Gewinnkriterium ist die Fähigkeit, Beats trotz Rauschen in den gelieferten Dateien erfolgreich zu erkennen. Im Falle eines Gleichstands gewinnt die kürzeste Lösung (die Länge des Drittanbietercodes wird nicht zur Zählung hinzugefügt).

Björn Lindqvist
quelle
1
Auch wenn dies interessant aussieht, ist dies ein Wettbewerb. Sie sollten die Gewinnkriterien genauer definieren als "Korrektheit".
Fabinout
ok jetzt besser ??
Björn Lindqvist
18
Ein guter Wettbewerb isoliert den Teil des Interesses. Hier scheinen Sie an der Beat-Identifikation interessiert zu sein, was sicherlich ein interessantes DSP-Problem ist. Warum sollten die Programme die Komplexität des MP3-Dateiformats bewältigen (oder auslagern)? Die Frage könnte verbessert werden, indem entweder RAW (mit zulässigen Annahmen zu Abtastrate, Bittiefe und Endianze) oder WAV (ähnlich) herangezogen wird.
Peter Taylor
3
Der Punkt des Wettbewerbs ist es, alle diese Stücke zu behandeln. Vielleicht macht es das schwierig, es in Golfscript zu lösen, wenn es schwierig ist, mit MP3s zu kommunizieren. Nichtsdestotrotz ist die Herausforderung gut spezifiziert und (afaict) vollständig thematisch, sodass die Negativität sehr bestürzend ist.
Björn Lindqvist
8
@ BjörnLindqvist Du solltest dir keine Verbesserungsvorschläge zu Herzen nehmen. Sofern ein vorheriger Kommentar nicht gelöscht wurde, werden hier keine negativen Kommentare angezeigt, sondern lediglich Verbesserungsvorschläge.
Gareth

Antworten:

6

Python 2.7 492 Bytes (nur beats.mp3)

Diese Antwort kann die Beats in identifizieren beats.mp3, identifiziert jedoch nicht alle Noten auf beats2.mp3oder noisy-beats.mp3. Nach der Beschreibung meines Codes werde ich näher darauf eingehen, warum.

Dies verwendet PyDub ( https://github.com/jiaaro/pydub ), um die MP3 einzulesen. Alle anderen Verarbeitungen sind NumPy.

Golf Code

Nimmt ein einzelnes Befehlszeilenargument mit dem Dateinamen an. Es gibt jeden Schlag in ms in einer separaten Zeile aus.

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

Ungolfed Code

# 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

Warum ich Notizen zu den anderen Dateien vermisse (und warum sie unglaublich herausfordernd sind)

Mein Code untersucht Änderungen in der Signalstärke, um die Noten zu finden. Denn beats.mp3das funktioniert wirklich gut. Dieses Spektrogramm zeigt, wie sich die Leistung über die Zeit (x-Achse) und die Frequenz (y-Achse) verteilt. Mein Code reduziert die y-Achse im Grunde genommen auf eine einzelne Zeile. beats.jpeg Visuell ist es sehr einfach zu erkennen, wo die Beats sind. Es gibt eine gelbe Linie, die sich immer wieder verjüngt. Ich ermutige Sie sehr, zuzuhörenbeats.mp3 während Sie dem Spektrogramm folgen, um zu sehen, wie es funktioniert.

Als nächstes gehe ich zu noisy-beats.mp3(weil das eigentlich einfacher ist als ...) beats2.mp3. noisy-beats.pngNoch einmal, sehen Sie, ob Sie die Aufnahme mitverfolgen können. Die meisten Zeilen sind schwächer, aber immer noch vorhanden Die leisen Noten fangen an, was es besonders schwierig macht, sie zu finden.

beats2.mp3ist unglaublich herausfordernd. Hier ist das Spektrogramm. beats2.jpeg Im ersten Bit gibt es einige Linien, aber einige Noten bluten wirklich über die Linien. Um Noten zuverlässig zu identifizieren, müssen Sie zunächst die Tonhöhe der Noten (Grund- und Obertöne) nachverfolgen und feststellen, wo sich diese ändern. Sobald das erste Bit funktioniert, ist das zweite Bit doppelt so hart wie das doppelte Tempo!

Grundsätzlich, um all dies zuverlässig zu identifizieren, ist meiner Meinung nach ein ausgefallener Notenerkennungscode erforderlich. Scheint, als wäre dies ein gutes Abschlussprojekt für jemanden in einer DSP-Klasse.

Dominic A.
quelle
Ich denke, das ist nicht erlaubt, weil es nicht alle Anforderungen erfüllt. Gute Antwort, aber es braucht etwas Arbeit.
28.
Ja, ich bin ein bisschen enttäuscht, dass diese Methode nicht so funktioniert hat, wie ich es mir erhofft hatte. Ich denke, das könnte jemand anderem helfen, der es versuchen möchte. Wenn ich diese Woche etwas Freizeit habe, hoffe ich, einen neuen FFT-basierten Ansatz auszuprobieren, der zu besseren Ergebnissen führen sollte.
Dominic A.
Okay, cool. Gute Arbeit.
1.