Python 2.7 492 byte (solo beats.mp3)
Questa risposta può identificare i battiti in beats.mp3
, ma non identificherà tutte le note su beats2.mp3
o noisy-beats.mp3
. Dopo la descrizione del mio codice, entrerò nei dettagli sul perché.
Questo utilizza PyDub ( https://github.com/jiaaro/pydub ) per leggere in MP3. Tutte le altre elaborazioni sono NumPy.
Codice Golfed
Accetta un singolo argomento della riga di comando con il nome del file. Emetterà ogni battito in ms su una riga separata.
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
Codice Ungolfed
# 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
Perché mi mancano le note sugli altri file (e perché sono incredibilmente difficili)
Il mio codice esamina i cambiamenti nella potenza del segnale al fine di trovare le note. Per beats.mp3
questo funziona davvero bene. Questo spettrogramma mostra come viene distribuita la potenza nel tempo (asse x) e frequenza (asse y). Il mio codice sostanzialmente comprime l'asse y su una sola riga.
Visivamente, è davvero facile vedere dove sono i battiti. C'è una linea gialla che si assottiglia ancora e ancora. Ti incoraggio vivamente ad ascoltare beats.mp3
mentre segui lo spettrogramma per vedere come funziona.
Poi andrò a noisy-beats.mp3
(perché in realtà è più facile di beats2.mp3
...
Ancora una volta, vedi se riesci a seguire insieme alla registrazione. La maggior parte delle linee sono più deboli, ma sono ancora lì. Tuttavia, in alcuni punti, la stringa inferiore continua a squillare quando iniziano le note tranquille, il che rende particolarmente difficile trovarle, perché ora le devi trovare in base ai cambiamenti di frequenza (l'asse y) piuttosto che all'ampiezza.
beats2.mp3
è incredibilmente stimolante. Ecco lo spettrogramma
Nel primo bit, ci sono alcune linee, ma alcune note sanguinano davvero sopra le linee. Per identificare le note in modo affidabile, dovresti iniziare a tracciare il tono delle note (fondamentali e armoniche) e vedere dove cambiano. Una volta che il primo bit funziona, il secondo bit è due volte più duro del doppio del tempo!
Fondamentalmente, per identificare tutti questi in modo affidabile, penso che ci voglia un po 'di codice di rilevamento delle note fantasiose. Sembra che questo sarebbe un buon progetto finale per qualcuno in una classe DSP.