Encontre as batidas em um arquivo MP3

27

Nesse desafio, sua tarefa é fazer uma gravação simples em formato mp3 e encontrar as compensações de tempo das batidas no arquivo. Dois exemplos de gravações estão aqui:

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

Aqui está a terceira gravação com muito mais ruído do que as duas anteriores:

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

Por exemplo, a primeira gravação tem 65 segundos de duração e contém exatamente (a menos que eu tenha falhado errado!) 76 batidas. Seu trabalho é criar um programa que use um arquivo mp3 como entrada e produza uma sequência das compensações de tempo em milissegundos das batidas no arquivo. Uma batida é definida para ocorrer, é claro, quando o guitarrista toca uma ou mais cordas.

Sua solução deve:

  • Trabalhe em qualquer arquivo mp3 de "complexidade" semelhante. Pode falhar em gravações barulhentas ou em melodias tocadas rapidamente - não me importo.
  • Seja bastante preciso. A tolerância é de +/- 50 ms. Portanto, se o ritmo ocorrer a 1500 ms e a sua solução reportar 1400, isso é inaceitável.
  • Use apenas software livre. A chamada ffmpeg é permitida, assim como o uso de qualquer software de terceiros disponível gratuitamente no idioma de sua escolha.

O critério vencedor é a capacidade de detectar com êxito as batidas, apesar do ruído nos arquivos fornecidos. Em caso de empate, a solução mais curta vence (o comprimento do código de terceiros não é adicionado à contagem).

Björn Lindqvist
fonte
1
Mesmo que isso pareça interessante, este é um concurso, você deve definir os critérios de vitória com mais precisão do que "Correção".
Fabinout
ok melhor agora ??
Björn Lindqvist
18
Um bom concurso isola a parte de interesse. Aqui você parece estar interessado na identificação de batidas, o que certamente é um problema interessante do DSP. Então, por que os programas lidam com (ou terceirizam) as complexidades do formato de arquivo MP3? A questão seria aprimorada adotando RAW (com suposições permitidas sobre taxa de amostragem, profundidade de bits e endianness) ou WAV (da mesma forma).
Peter Taylor
3
O objetivo do concurso é lidar com todas essas peças. Talvez isso dificulte resolvê-lo no golfscript, se houver dificuldade em fazer interface com o mp3. Não obstante, o desafio é bem especificado e (completamente) on-topic, de modo que a negatividade é muito desanimadora.
Björn Lindqvist
8
@ BjörnLindqvist Você não deve receber sugestões de melhorias para o coração. A menos que algum comentário anterior tenha sido excluído, não vejo comentários negativos aqui, apenas sugestões de melhorias.
Gareth

Respostas:

6

Python 2.7 492 bytes (somente beats.mp3)

Esta resposta pode identificar as batidas beats.mp3, mas não identificará todas as notas em beats2.mp3ou noisy-beats.mp3. Após a descrição do meu código, entrarei em detalhes sobre o porquê.

Isso usa o PyDub ( https://github.com/jiaaro/pydub ) para ler o MP3. Todo o outro processamento é NumPy.

Código de golfe

Leva um único argumento de linha de comando com o nome do arquivo. Ele produzirá cada batida em ms em uma linha 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 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

Por que sinto falta de anotações nos outros arquivos (e por que eles são incrivelmente desafiadores)

Meu código analisa as alterações na potência do sinal para encontrar as notas. Pois beats.mp3, isso funciona muito bem. Este espectrograma mostra como a energia é distribuída ao longo do tempo (eixo x) e frequência (eixo y). Meu código basicamente recolhe o eixo y em uma única linha. beats.jpeg Visualmente, é realmente fácil ver onde estão as batidas. Há uma linha amarela que se afunila de novo e de novo. É altamente recomendável que você ouça beats.mp3enquanto acompanha o espectrograma para ver como ele funciona.

Em seguida, irei a noisy-beats.mp3(porque isso é realmente mais fácil do que beats2.mp3... noisy-beats.pngMais uma vez, veja se você pode acompanhar a gravação. A maioria das linhas é mais fraca, mas ainda está lá. começam as notas silenciosas, o que dificulta sua descoberta, porque agora você precisa encontrá-las com mudanças na frequência (o eixo y) e não apenas na amplitude.

beats2.mp3é incrivelmente desafiador. Aqui está o espectrograma beats2.jpeg No primeiro bit, existem algumas linhas, mas algumas notas realmente sangram sobre as linhas. Para identificar notas de maneira confiável, é necessário começar a acompanhar o tom das notas (fundamental e harmônica) e ver onde elas mudam. Uma vez que o primeiro bit está funcionando, o segundo bit é duas vezes mais difícil que o tempo dobra!

Basicamente, para identificar de forma confiável tudo isso, acho que é preciso algum código de detecção de nota sofisticado. Parece que este seria um bom projeto final para alguém da classe DSP.

Dominic A.
fonte
Eu acho que isso não é permitido, porque não atende a todos os requisitos. Boa resposta, mas precisa de algum trabalho.
Rɪᴋᴇʀ
Sim, estou um pouco decepcionado por esse método não ter funcionado como eu esperava. Eu acho que isso pode ajudar alguém que queira dar uma facada nele. Se eu tiver algum tempo livre nesta semana, espero tentar uma nova abordagem baseada na FFT que dê melhores resultados.
Domingos
OK fixe. Bom trabalho embora.
Rɪᴋᴇʀ