Trouvez les rythmes dans un fichier MP3

27

Dans ce défi, votre tâche consiste à effectuer un simple enregistrement au format mp3 et à trouver les décalages temporels des temps dans le fichier. Deux exemples d'enregistrements sont ici:

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

Voici le troisième enregistrement avec beaucoup plus de bruit que les deux précédents:

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

Par exemple, le premier enregistrement dure 65 secondes et contient exactement (à moins que j'aie mal compté!) 76 temps. Votre travail consiste à concevoir un programme qui prend un tel fichier mp3 en entrée et génère une séquence des décalages horaires en millisecondes des battements du fichier. Un battement est défini pour se produire, bien sûr, lorsque le guitariste jouant joue une ou plusieurs cordes.

Votre solution doit:

  • Travaillez sur n'importe quel fichier mp3 de "complexité" similaire. Cela peut échouer sur des enregistrements bruyants ou sur des mélodies jouées rapidement - je m'en fiche.
  • Soyez assez précis. La tolérance est de +/- 50 ms. Donc, si le battement se produit à 1500 ms et que votre solution signale 1400, c'est inacceptable.
  • Utilisez uniquement des logiciels gratuits. L'appel à ffmpeg est autorisé, tout comme l'utilisation de tout logiciel tiers disponible gratuitement pour la langue de votre choix.

Le critère gagnant est la capacité à détecter avec succès les battements malgré le bruit dans les fichiers fournis. En cas d'égalité, la solution la plus courte l'emporte (la longueur du code tiers n'est pas ajoutée au décompte).

Björn Lindqvist
la source
1
Même si cela semble intéressant, il s'agit d'un concours, vous devez définir les critères de gain plus précisément que "Exactitude".
Fabinout
ok mieux maintenant ??
Björn Lindqvist
18
Un bon concours isole la partie d'intérêt. Ici, vous semblez intéressé par l'identification des temps, qui est certainement un problème DSP intéressant. Alors pourquoi faire en sorte que les programmes gèrent (ou externalisent) la complexité du format de fichier MP3? La question serait améliorée en prenant soit RAW (avec des hypothèses autorisées sur la fréquence d'échantillonnage, la profondeur de bits et l'endianité) ou WAV (similaire).
Peter Taylor
3
Le but du concours est de gérer toutes ces pièces. Peut-être qu'il est difficile de le résoudre dans golfscript s'il a du mal à s'interfacer avec les mp3. Néanmoins, le défi est bien spécifié et (afaict) complètement sur le sujet, donc la négativité est très consternante.
Björn Lindqvist
8
@ BjörnLindqvist Vous ne devriez pas prendre à cœur les suggestions d'amélioration. À moins qu'un commentaire précédent n'ait été supprimé, je ne vois aucun commentaire négatif ici, juste des suggestions d'amélioration.
Gareth

Réponses:

6

Python 2,7 492 octets (beats.mp3 uniquement)

Cette réponse peut identifier les battements dans beats.mp3, mais n'identifiera pas toutes les notes sur beats2.mp3ou noisy-beats.mp3. Après la description de mon code, je vais expliquer en détail pourquoi.

Cela utilise PyDub ( https://github.com/jiaaro/pydub ) pour lire dans le MP3. Tous les autres traitements sont NumPy.

Golfed Code

Prend un seul argument de ligne de commande avec le nom de fichier. Il sortira chaque battement en ms sur une ligne distincte.

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

Code non golfé

# 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

Pourquoi je manque des notes sur les autres fichiers (et pourquoi ils sont incroyablement difficiles)

Mon code examine les changements de puissance du signal afin de trouver les notes. Pour beats.mp3, cela fonctionne vraiment bien. Ce spectrogramme montre comment la puissance est répartie dans le temps (axe x) et la fréquence (axe y). Mon code réduit essentiellement l'axe y en une seule ligne. beats.jpeg Visuellement, il est vraiment facile de voir où sont les battements. Il y a une ligne jaune qui s'efface encore et encore. Je vous encourage fortement à écouter beats.mp3pendant que vous suivez le spectrogramme pour voir comment cela fonctionne.

Ensuite, je vais aller à noisy-beats.mp3(parce que c'est en fait plus facile que beats2.mp3. noisy-beats.png Encore une fois, voyez si vous pouvez suivre l'enregistrement. La plupart des lignes sont plus faibles, mais toujours là. Cependant, à certains endroits, la corde du bas sonne toujours quand les notes calmes commencent. Cela rend leur recherche particulièrement difficile, car maintenant, vous devez les trouver par des changements de fréquence (l'axe des y) plutôt que par une simple amplitude.

beats2.mp3est incroyablement difficile. Voici le spectrogramme beats2.jpeg Dans le premier bit, il y a quelques lignes, mais certaines notes saignent vraiment sur les lignes. Pour identifier de manière fiable les notes, vous devez commencer à suivre la hauteur des notes (fondamentales et harmoniques) et voir où celles-ci changent. Une fois que le premier bit fonctionne, le deuxième bit est deux fois plus dur que le tempo double!

Fondamentalement, pour identifier de manière fiable tous ces éléments, je pense qu'il faut un code de détection de note de fantaisie. Il semble que ce serait un bon projet final pour quelqu'un dans une classe DSP.

Dominic A.
la source
Je pense que ce n'est pas autorisé, car il ne remplit pas toutes les exigences. Bonne réponse, mais il faut du travail.
Rɪᴋᴇʀ
Oui, je suis un peu déçu que cette méthode n'ait pas fonctionné comme je l'espérais. Je pense que cela pourrait aider quelqu'un d'autre qui veut essayer. Si j'ai du temps libre cette semaine, j'espère essayer une nouvelle approche basée sur la FFT qui devrait donner de meilleurs résultats.
Dominic A.
D'accord, cool. Beau travail cependant.
Rɪᴋᴇʀ