Python 2.7 492 bytes (solo beats.mp3)
Esta respuesta puede identificar los ritmos beats.mp3
, pero no identificará todas las notas en beats2.mp3
o noisy-beats.mp3
. Después de la descripción de mi código, entraré en detalles sobre por qué.
Esto usa PyDub ( https://github.com/jiaaro/pydub ) para leer en el MP3. El resto del procesamiento es NumPy.
Código de golf
Toma un argumento de línea de comando único con el nombre del archivo. Producirá cada latido en ms en una línea separada.
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
Código sin 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
Por qué echo de menos las notas en los otros archivos (y por qué son increíblemente desafiantes)
Mi código analiza los cambios en la potencia de la señal para encontrar las notas. Pues beats.mp3
esto funciona muy bien. Este espectrograma muestra cómo se distribuye la potencia en el tiempo (eje x) y la frecuencia (eje y). Mi código básicamente colapsa el eje y en una sola línea.
Visualmente, es realmente fácil ver dónde están los latidos. Hay una línea amarilla que se reduce una y otra vez. Le recomiendo que escuche beats.mp3
mientras sigue el espectrograma para ver cómo funciona.
A continuación, iré a noisy-beats.mp3
(porque eso es realmente más fácil que beats2.mp3
...
Una vez más, vea si puede seguir junto con la grabación. La mayoría de las líneas son más débiles, pero aún están allí. Sin embargo, en algunos puntos, la cadena inferior sigue sonando cuando comienzan las notas silenciosas, lo que hace que encontrarlas sea especialmente difícil, porque ahora, debe encontrarlas mediante cambios en la frecuencia (el eje y) en lugar de solo la amplitud.
beats2.mp3
Es increíblemente desafiante. Aquí está el espectrograma
En el primer bit, hay algunas líneas, pero algunas notas realmente sangran sobre las líneas. Para identificar notas de manera confiable, tendría que comenzar a rastrear el tono de las notas (fundamental y armónicas) y ver dónde cambian esas notas. Una vez que el primer bit está funcionando, el segundo bit es el doble de duro que el tempo se duplica.
Básicamente, para identificar de manera confiable todo esto, creo que se necesita un código de detección de notas elegante. Parece que este sería un buen proyecto final para alguien en una clase DSP.