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.mp3
ou 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.
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.mp3
pendant 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
.
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.mp3
est incroyablement difficile. Voici le spectrogramme
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.