Encuentra los ritmos en un archivo MP3

27

En este desafío, su tarea es tomar una grabación simple en formato mp3 y encontrar las compensaciones de tiempo de los tiempos en el archivo. Aquí hay dos ejemplos de grabaciones:

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

Aquí está la tercera grabación con mucho más ruido que las dos anteriores:

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

Por ejemplo, la primera grabación dura 65 segundos y contiene exactamente (¡a menos que haya contado mal!) 76 latidos. Su trabajo es diseñar un programa que tome un archivo mp3 como entrada y genere una secuencia de las compensaciones de tiempo en milisegundos de los tiempos del archivo. Un ritmo se define, por supuesto, cuando el guitarrista tocando toca una o más cuerdas.

Su solución debe:

  • Trabaja en cualquier archivo mp3 de "complejidad" similar. Puede fallar en grabaciones ruidosas o reproducir melodías rápidamente, no me importa.
  • Sé bastante preciso. La tolerancia es de +/- 50 ms. Entonces, si el latido ocurre a 1500 ms y su solución informa 1400, entonces eso es inaceptable.
  • Use solo software libre. Se permite llamar a ffmpeg al igual que usar cualquier software de terceros disponible gratuitamente para el idioma que elija.

El criterio ganador es la capacidad de detectar con éxito los latidos a pesar del ruido en los archivos suministrados. En caso de empate, la solución más corta gana (la longitud del código de terceros no se agrega al recuento).

Björn Lindqvist
fuente
1
Aunque esto parezca interesante, este es un concurso, debes definir los criterios ganadores con más precisión que "Corrección".
Fabinout
ok mejor ahora ??
Björn Lindqvist
18
Un buen concurso aísla la parte de interés. Aquí parece estar interesado en la identificación de ritmos, que sin duda es un problema interesante de DSP. Entonces, ¿por qué los programas manejan (o subcontratan) las complejidades del formato de archivo MP3? La pregunta se mejoraría tomando RAW (con supuestos permitidos sobre la frecuencia de muestreo, profundidad de bits y endianness) o WAV (de manera similar).
Peter Taylor
3
El objetivo del concurso es manejar todas esas piezas. Quizás eso hace que sea difícil resolverlo en golfscript si le resulta difícil interactuar con mp3. Sin embargo, el desafío está bien especificado y (enfadado) completamente sobre el tema, por lo que la negatividad es muy desalentadora.
Björn Lindqvist
8
@ BjörnLindqvist No debe tomar en serio sugerencias de mejora. A menos que se haya eliminado algún comentario anterior, no veo ningún comentario negativo aquí, solo sugerencias de mejoras.
Gareth

Respuestas:

6

Python 2.7 492 bytes (solo beats.mp3)

Esta respuesta puede identificar los ritmos beats.mp3, pero no identificará todas las notas en beats2.mp3o 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.mp3esto 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. beats.jpeg 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.mp3mientras 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... ruidosos latidos.pngUna 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.mp3Es increíblemente desafiante. Aquí está el espectrograma beats2.jpeg 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.

Dominic A.
fuente
Creo que esto no está permitido, porque no cumple con todos los requisitos. Buena respuesta, pero necesita algo de trabajo.
Rɪᴋᴇʀ
Sí, estoy un poco decepcionado porque este método no funcionó como esperaba. Me imagino que esto podría ayudar a alguien más que quiera apuñalarlo. Si tengo algo de tiempo libre esta semana, espero probar un nuevo enfoque basado en FFT que debería dar mejores resultados.
Dominic A.
De acuerdo, genial. Buen trabajo sin embargo.
Rɪᴋᴇʀ