Znajdź rytmy w pliku MP3

27

W tym wyzwaniu Twoim zadaniem jest wykonanie prostego nagrania w formacie mp3 i znalezienie przesunięć czasowych uderzeń w pliku. Oto dwa przykładowe nagrania:

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

Oto trzecie nagranie z dużo większym szumem niż dwa poprzednie:

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

Na przykład pierwsze nagranie ma 65 sekund długości i zawiera dokładnie (chyba że pomyliłem!) 76 uderzeń. Twoim zadaniem jest opracowanie programu, który pobiera taki plik mp3 jako dane wejściowe i wyświetla sekwencję przesunięć czasowych w milisekundach uderzeń w pliku. Uderzenie jest zdefiniowane, oczywiście, gdy gitarzysta grający wyrywa jeden lub więcej strun.

Twoje rozwiązanie musi:

  • Pracuj nad dowolnym plikiem mp3 o podobnej „złożoności”. Może zawieść przy głośnych nagraniach lub szybko odtwarzanych melodiach - nie obchodzi mnie to.
  • Bądź dość precyzyjny. Tolerancja wynosi +/- 50 ms. Jeśli więc rytm występuje po 1500 ms, a twoje rozwiązanie zgłasza 1400, to jest to niedopuszczalne.
  • Używaj tylko darmowego oprogramowania. Dzwonienie do ffmpeg jest dozwolone, podobnie jak w przypadku korzystania z dowolnego darmowego oprogramowania strony trzeciej dla wybranego języka.

Zwycięskim kryterium jest zdolność do skutecznego wykrywania uderzeń pomimo szumu w dostarczonych plikach. W przypadku remisu wygrywa najkrótsze rozwiązanie (długość kodu innej firmy nie jest dodawana do liczby).

Björn Lindqvist
źródło
1
Mimo, że wygląda to interesująco, jest to konkurs, powinieneś zdefiniować kryteria zwycięstwa bardziej precyzyjnie niż „Poprawność”.
Fabinout,
ok lepiej teraz?
Björn Lindqvist,
18
Dobry konkurs izoluje część zainteresowania. Wygląda na to, że interesuje Cię identyfikacja beatów, co jest z pewnością interesującym problemem DSP. Dlaczego więc zmusić programy do obsługi (lub outsourcingu) złożoności formatu plików MP3? Pytanie to można poprawić, przyjmując RAW (z dozwolonymi założeniami dotyczącymi częstotliwości próbkowania, głębokości bitów i endianowości) lub WAV (podobnie).
Peter Taylor
3
Celem konkursu jest obsłużenie wszystkich tych elementów. Być może sprawia to, że trudno jest go rozwiązać w skrypcie golfowym, jeśli ma trudności z połączeniem się z plikami mp3. Niemniej jednak wyzwanie jest dobrze określone i (afaict) całkowicie tematyczne, więc negatywność jest bardzo przerażająca.
Björn Lindqvist,
8
@ BjörnLindqvist Nie należy przyjmować sugestii dotyczących poprawy w sercu. O ile wcześniejszy komentarz nie został usunięty, nie widzę tu żadnych negatywnych komentarzy, tylko sugestie ulepszeń.
Gareth

Odpowiedzi:

6

Python 2.7 492 bajtów (tylko beats.mp3)

Ta odpowiedź może zidentyfikować uderzenia beats.mp3, ale nie zidentyfikuje wszystkich notatek na beats2.mp3lub noisy-beats.mp3. Po opisie mojego kodu omówię szczegółowo, dlaczego.

To używa PyDub ( https://github.com/jiaaro/pydub ) do odczytu w MP3. Wszelkie inne przetwarzanie odbywa się za pomocą NumPy.

Kod do gry w golfa

Bierze pojedynczy argument wiersza poleceń o nazwie pliku. Będzie generować każdy beat w ms w osobnej linii.

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

Kod niepoznany

# 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

Dlaczego brakuje mi notatek na temat innych plików (i dlaczego są one niewiarygodnie trudne)

Mój kod sprawdza zmiany mocy sygnału w celu znalezienia notatek. Bo beats.mp3to działa naprawdę dobrze. Ten spektrogram pokazuje rozkład mocy w czasie (oś x) i częstotliwości (oś y). Mój kod w zasadzie zwija oś y do pojedynczej linii. beats.jpeg Wizualnie bardzo łatwo jest zobaczyć, gdzie są uderzenia. Jest żółta linia, która zwęża się raz za razem. Gorąco zachęcam do słuchania beats.mp3podczas śledzenia spektrogramu, aby zobaczyć, jak to działa.

Następnie przejdę do noisy-beats.mp3(bo to w rzeczywistości jest łatwiejsze niż beats2.mp3.. noisy-beats.pngJeszcze raz sprawdź, czy możesz śledzić wraz z nagrywaniem. Większość linii jest słabsza, ale nadal tam jest. Jednak w niektórych miejscach dolna struna nadal dzwoni, gdy zaczynają się ciche dźwięki. To sprawia, że ​​znalezienie ich jest szczególnie trudne, ponieważ teraz musisz je znaleźć na podstawie zmian częstotliwości (oś y), a nie tylko amplitudy.

beats2.mp3jest niezwykle trudny. Oto spektrogram beats2.jpeg W pierwszym bicie są pewne linie, ale niektóre nuty naprawdę krwawią nad liniami. Aby niezawodnie zidentyfikować nuty, musisz zacząć śledzić wysokość tonów (podstawowe i harmoniczne) i sprawdzać, gdzie się one zmieniają. Gdy pierwszy bit działa, drugi bit jest dwa razy trudniejszy niż tempo podwojenia!

Zasadniczo, aby niezawodnie zidentyfikować je wszystkie, myślę, że wymaga trochę wymyślnego kodu wykrywania notatek. Wydaje się, że byłby to dobry końcowy projekt dla kogoś z klasy DSP.

Dominic A.
źródło
Myślę, że to niedozwolone, ponieważ nie spełnia wszystkich wymagań. Dobra odpowiedź, ale wymaga trochę pracy.
Rɪᴋᴇʀ
Tak, jestem trochę rozczarowany, że ta metoda nie zadziałała, jak się spodziewałem. Sądzę, że może to pomóc komuś innemu, kto chce w to wbić nóż. Jeśli dostanę trochę wolnego czasu w tym tygodniu, mam nadzieję wypróbować nowe podejście oparte na FFT, które powinno dać lepsze wyniki.
Dominic A.
Ok spoko. Dobra robota.
Rɪᴋᴇʀ