Temukan ketukan dalam file MP3

27

Dalam tantangan ini, tugas Anda adalah mengambil rekaman sederhana dalam format mp3 dan menemukan waktu offset ketukan dalam file. Dua contoh rekaman ada di sini:

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

Ini adalah rekaman ketiga dengan lebih banyak noise daripada dua sebelumnya:

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

Misalnya, rekaman pertama berdurasi 65 detik dan berisi persis (kecuali saya salah menghitung!) 76 ketukan. Tugas Anda adalah menyusun program yang mengambil file mp3 seperti itu sebagai input dan menghasilkan urutan offset waktu dalam milidetik dari ketukan dalam file tersebut. Ketukan didefinisikan terjadi, tentu saja, ketika gitaris bermain memetik satu atau lebih senar.

Solusi Anda harus:

  • Kerjakan semua file mp3 dengan "kompleksitas" yang serupa. Ini bisa gagal pada rekaman berisik atau melodi yang diputar dengan cepat - Saya tidak peduli.
  • Cukup tepat. Toleransi adalah +/- 50 ms. Jadi jika ketukan terjadi pada 1500 ms dan solusi Anda melaporkan 1400, maka itu tidak dapat diterima.
  • Gunakan hanya perangkat lunak gratis. Memanggil ffmpeg diizinkan sebagaimana halnya menggunakan perangkat lunak pihak ketiga yang tersedia secara bebas untuk bahasa pilihan Anda.

Kriteria yang menang adalah kemampuan untuk berhasil mendeteksi ketukan meskipun ada noise di file yang disediakan. Dalam kasus seri, solusi terpendek menang (panjang kode pihak ketiga tidak ditambahkan ke hitungan).

Björn Lindqvist
sumber
1
Meskipun ini terlihat menarik, ini adalah kontes, Anda harus menentukan kriteria kemenangan lebih tepatnya daripada "Benar".
Fabinout
ok lebih baik sekarang ??
Björn Lindqvist
18
Kontes yang bagus mengisolasi bagian yang menarik. Di sini Anda tampaknya tertarik pada identifikasi beat, yang tentunya merupakan masalah DSP yang menarik. Jadi mengapa membuat program menangani (atau mengalihdayakan) kompleksitas format file MP3? Pertanyaan akan ditingkatkan dengan mengambil RAW (dengan asumsi yang diizinkan tentang laju sampel, kedalaman bit, dan endianness) atau WAV (sama).
Peter Taylor
3
Tujuan dari kontes ini adalah untuk menangani semua bagian itu. Mungkin itu membuatnya sulit untuk menyelesaikannya dalam skrip golf jika mengalami kesulitan berinteraksi dengan mp3. Meskipun demikian, tantangannya ditentukan dengan baik dan (afaict) benar-benar sesuai topik sehingga negativitas sangat mengecewakan.
Björn Lindqvist
8
@ BjörnLindqvist Anda tidak harus menerima saran untuk perbaikan hati. Kecuali beberapa komentar sebelumnya telah dihapus, saya tidak melihat komentar negatif di sini, hanya saran untuk perbaikan.
Gareth

Jawaban:

6

Python 2.7 492 byte (hanya beats.mp3)

Jawaban ini dapat mengidentifikasi ketukan beats.mp3, tetapi tidak akan mengidentifikasi semua catatan pada beats2.mp3atau noisy-beats.mp3. Setelah uraian kode saya, saya akan menjelaskan mengapa.

Ini menggunakan PyDub ( https://github.com/jiaaro/pydub ) untuk membaca di MP3. Semua pemrosesan lainnya adalah NumPy.

Kode Golf

Membawa argumen baris perintah tunggal dengan nama file. Ini akan menampilkan setiap ketukan dalam ms pada baris terpisah.

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

Kode Tidak Terkunci

# 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

Mengapa saya ketinggalan catatan pada file lain (dan mengapa mereka sangat menantang)

Kode saya melihat perubahan daya sinyal untuk menemukan catatan. Sebab beats.mp3, ini bekerja dengan sangat baik. Spektrogram ini menunjukkan bagaimana daya didistribusikan dari waktu ke waktu (sumbu x) dan frekuensi (sumbu y). Kode saya pada dasarnya menciutkan sumbu y ke satu baris. beats.jpeg Secara visual, sangat mudah untuk melihat di mana ketukannya. Ada garis kuning yang berangsur-angsur berkurang. Saya sangat menganjurkan Anda untuk mendengarkan beats.mp3sambil mengikuti spektrogram untuk melihat cara kerjanya.

Selanjutnya saya akan pergi ke noisy-beats.mp3(karena itu sebenarnya lebih mudah daripada beats2.mp3... berisik-beats.pngSekali lagi, lihat apakah Anda dapat mengikuti dengan rekaman. Sebagian besar garis lebih redup, tetapi masih ada. Namun, di beberapa tempat, string bawah masih berdering ketika nada sunyi mulai, yang membuat mereka sangat sulit, karena sekarang, Anda harus menemukannya dengan perubahan frekuensi (sumbu y) daripada hanya amplitudo.

beats2.mp3sangat menantang. Inilah spektrogram beats2.jpeg Di bit pertama, ada beberapa garis, tetapi beberapa nada benar-benar berdarah di atas garis. Untuk mengidentifikasi catatan dengan andal, Anda harus mulai melacak nada catatan (fundamental dan harmonik) dan melihat di mana perubahan itu. Setelah bit pertama bekerja, bit kedua dua kali lebih keras dari tempo yang berlipat ganda!

Pada dasarnya, untuk mengidentifikasi semua ini dengan andal, saya pikir dibutuhkan beberapa kode deteksi catatan mewah. Sepertinya ini akan menjadi tugas akhir yang bagus untuk seseorang di kelas DSP.

Dominic A.
sumber
Saya pikir ini tidak diperbolehkan, karena tidak memenuhi semua persyaratan. Jawaban yang bagus, tetapi perlu beberapa usaha.
Rɪᴋᴇʀ
Ya, saya agak kecewa metode ini tidak berhasil seperti yang saya harapkan. Saya pikir ini dapat membantu orang lain yang ingin menikamnya. Jika saya mendapatkan waktu luang minggu ini, saya berharap dapat mencoba pendekatan berbasis FFT baru yang akan memberikan hasil yang lebih baik.
Dominic A.
Oke, keren. Pekerjaan yang bagus.
Rɪᴋᴇʀ