ค้นหาจังหวะในไฟล์ MP3


27

ในความท้าทายนี้งานของคุณคือการบันทึกอย่างง่าย ๆ ในรูปแบบ mp3 และค้นหาการชดเชยเวลาของการเต้นในไฟล์ ตัวอย่างการบันทึกสองตัวอย่างอยู่ที่นี่:

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

นี่คือการบันทึกครั้งที่สามที่มีเสียงรบกวนมากกว่าสองครั้งก่อนหน้า:

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

ตัวอย่างเช่นการบันทึกครั้งแรกมีความยาว 65 วินาทีและมีการบันทึก (ยกเว้นว่าฉันผิด!) 76 ครั้ง งานของคุณคือการออกแบบโปรแกรมที่ใช้ไฟล์ mp3 เป็นอินพุทและเอาท์พุทตามลำดับของเวลาออฟเซ็ตในหน่วยมิลลิวินาทีของการเต้นในไฟล์ จังหวะถูกกำหนดให้เกิดขึ้นแน่นอนเมื่อมือกีต้าร์เล่น plucks หนึ่งหรือมากกว่าหนึ่งสาย

โซลูชันของคุณต้อง:

  • ทำงานกับไฟล์ mp3 ใด ๆ ที่มี "ความซับซ้อน" ที่คล้ายกัน มันสามารถล้มเหลวในการบันทึกที่มีเสียงดังหรือเล่นท่วงทำนองที่รวดเร็ว - ฉันไม่สนใจ
  • มีความแม่นยำพอสมควร ความอดทนคือ +/- 50 ms ดังนั้นหากจังหวะการเต้นเกิดขึ้นที่ 1500 ms และวิธีการแก้ปัญหาของคุณรายงานที่ 1,400 นั่นเป็นสิ่งที่ยอมรับไม่ได้
  • ใช้ซอฟต์แวร์ฟรีเท่านั้น อนุญาตให้ใช้ ffmpeg ได้เช่นเดียวกับการใช้ซอฟต์แวร์ของบุคคลที่สามที่มีให้บริการฟรีสำหรับภาษาที่คุณเลือก

เกณฑ์การชนะคือความสามารถในการตรวจจับการเต้นได้สำเร็จแม้จะมีเสียงดังในไฟล์ที่ให้ ในกรณีที่เสมอกันทางออกที่สั้นที่สุดจะชนะ (ความยาวของรหัสของบุคคลที่สามจะไม่ถูกเพิ่มเข้าไปในการนับ)


1
แม้ว่านี่จะดูน่าสนใจ แต่นี่เป็นการแข่งขันคุณควรกำหนดเกณฑ์การชนะให้แม่นยำยิ่งกว่า "ความถูกต้อง"
Fabinout

ตกลงดีกว่าตอนนี้?
Björn Lindqvist

18
การประกวดที่ดีจะแยกส่วนที่น่าสนใจ ที่นี่คุณดูเหมือนจะสนใจในการระบุจังหวะซึ่งเป็นปัญหา DSP ที่น่าสนใจอย่างแน่นอน เหตุใดจึงทำให้โปรแกรมจัดการ (หรือ outsource) ความซับซ้อนของรูปแบบไฟล์ MP3 คำถามจะได้รับการปรับปรุงโดยใช้ RAW (โดยมีสมมติฐานที่อนุญาตเกี่ยวกับอัตราตัวอย่างความลึกของบิตและ endianness) หรือ WAV (ในทำนองเดียวกัน)
Peter Taylor

3
จุดของการแข่งขันคือการจัดการกับชิ้นส่วนเหล่านั้นทั้งหมด บางทีมันอาจจะทำให้ยากที่จะแก้ไขใน golfscript ถ้ามันมีช่วงเวลาที่ยากลำบากในการเชื่อมต่อกับ mp3 ความท้าทายมีการระบุไว้เป็นอย่างดีและ (afaict) อย่างสมบูรณ์ในหัวข้อดังนั้นการปฏิเสธจะทำให้เสียขวัญมาก
Björn Lindqvist

8
@ BjörnLindqvistคุณไม่ควรรับคำแนะนำในการปรับปรุงจิตใจ หากไม่ได้ลบความคิดเห็นก่อนหน้านี้ฉันไม่เห็นความคิดเห็นเชิงลบที่นี่เพียงคำแนะนำสำหรับการปรับปรุง
Gareth

คำตอบ:


6

Python 2.7 492 ไบต์ (beats.mp3 เท่านั้น)

คำตอบนี้สามารถระบุเต้นในbeats.mp3แต่จะไม่ระบุบันทึกทั้งหมดบนหรือbeats2.mp3 noisy-beats.mp3หลังจากคำอธิบายรหัสของฉันฉันจะเข้าไปดูรายละเอียดว่าทำไม

สิ่งนี้ใช้ PyDub ( https://github.com/jiaaro/pydub ) เพื่ออ่านใน MP3 การประมวลผลอื่น ๆ ทั้งหมดคือ NumPy

รหัส Golfed

รับอาร์กิวเมนต์บรรทัดคำสั่งเดียวพร้อมชื่อไฟล์ มันจะออกแต่ละจังหวะเป็น ms ในแต่ละบรรทัด

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

รหัสที่ไม่ดี

# 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

ทำไมฉันถึงคิดถึงโน้ตของไฟล์อื่น ๆ (และทำไมมันถึงท้าทายอย่างเหลือเชื่อ)

รหัสของฉันดูที่การเปลี่ยนแปลงกำลังงานของสัญญาณเพื่อค้นหาบันทึกย่อ สำหรับbeats.mp3วิธีนี้ใช้งานได้ดีจริงๆ สเปกโตรแกรมนี้แสดงให้เห็นว่ากำลังกระจายอย่างไรเมื่อเวลาผ่านไป (แกน x) และความถี่ (แกน y) โดยทั่วไปโค้ดของฉันจะยุบแกน y ลงไปเป็นบรรทัดเดียว beats.jpeg มันง่ายมากที่จะดูว่ามันอยู่ที่ไหน มีเส้นสีเหลืองที่ลดลงอีกครั้งและอีกครั้ง ฉันขอแนะนำให้คุณฟังbeats.mp3ในขณะที่คุณติดตามบน spectrogram เพื่อดูว่ามันทำงานอย่างไร

ต่อไปฉันจะไปที่noisy-beats.mp3(เพราะมันง่ายกว่าจริงbeats2.mp3มีเสียงดัง-beats.pngอีกครั้งดูว่าคุณสามารถติดตามพร้อมกับการบันทึกได้หรือไม่ส่วนใหญ่ของบรรทัดนั้นซีดกว่า แต่ยังอยู่ที่นั่นอย่างไรก็ตามในบางจุดสตริงด้านล่างยังคงดังขึ้นเมื่อ โน้ตเงียบเริ่มต้นที่ทำให้การค้นหายากเป็นพิเศษเพราะตอนนี้คุณต้องค้นหาโดยการเปลี่ยนแปลงความถี่ (แกน y) แทนที่จะเป็นแอมพลิจูด

beats2.mp3ท้าทายอย่างไม่น่าเชื่อ นี่คือสเปกโทรแกรม beats2.jpeg ในบิตแรกมีบางบรรทัด แต่บางบันทึกย่อมีเลือดออกเหนือเส้น ในการระบุโน้ตได้อย่างน่าเชื่อถือคุณจะต้องเริ่มการติดตามระดับเสียงของโน้ต (พื้นฐานและเสียงประสาน) และดูว่าการเปลี่ยนแปลงเหล่านั้นอยู่ที่ไหน เมื่อบิตแรกทำงานบิตที่สองนั้นหนักหน่วงกว่าสองเท่าของจังหวะ!

โดยทั่วไปเพื่อระบุสิ่งเหล่านี้ได้อย่างน่าเชื่อถือฉันคิดว่ามันต้องใช้รหัสตรวจจับบันทึกย่อแฟนซี ดูเหมือนว่านี่จะเป็นโครงการสุดท้ายที่ดีสำหรับใครบางคนในคลาส DSP


ฉันคิดว่านี่ไม่ได้รับอนุญาตเพราะมันไม่ได้เป็นไปตามข้อกำหนดทั้งหมด คำตอบที่ดี แต่มันต้องมีงาน
Rɪᴋᴇʀ

ใช่ฉันผิดหวังเล็กน้อยวิธีนี้ไม่ได้ผลตามที่ฉันหวังไว้ ฉันคิดว่าสิ่งนี้อาจช่วยคนอื่นที่ต้องการแทงมัน ถ้าฉันมีเวลาว่างในสัปดาห์นี้ฉันหวังว่าจะลองใช้วิธีการแบบ FFT ใหม่ที่ควรให้ผลลัพธ์ที่ดีกว่า
Dominic A.

โอเคเยี่ยมเลย ทำได้ดีมาก
Rɪᴋᴇʀ
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.