Python 2.7 492 байтів (лише beats.mp3)
Ця відповідь може ідентифікувати удари beats.mp3
, але не визначатиме всі нотатки на beats2.mp3
або noisy-beats.mp3
. Після опису мого коду я детально розкажу про те, чому.
Для читання у форматі MP3 використовується PyDub ( https://github.com/jiaaro/pydub ). Вся інша обробка - NumPy.
Кодекс для гольфу
Бере єдиний аргумент командного рядка з ім'ям файлу. Він виводить кожен такт у мс в окремому рядку.
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.mp3
поки ви стежите за спектрограмою, щоб побачити, як вона працює.
Далі я перейду до цього noisy-beats.mp3
(адже це насправді простіше, ніж beats2.mp3
.
Ще раз перегляньте, чи можете ви продовжувати запис. Більшість рядків слабкіші, але все ж є. Однак у деяких місцях нижня рядок усе ще дзвонить, коли починаються тихі ноти. Це робить їх особливо важкими, тому що тепер ви повинні знайти їх за зміною частоти (вісь y), а не просто амплітудою.
beats2.mp3
неймовірно складно. Ось спектрограма
У першому біті є деякі лінії, але деякі примітки дійсно кровоточать по лініях. Щоб надійно визначити ноти, вам слід почати відстежувати крок нот (фундаментальні та гармонічні) та бачити, де вони змінюються. Як тільки перший біт працює, другий біт удвічі важчий, ніж темп подвоюється!
По суті, для надійної ідентифікації всього цього, я думаю, що потрібен якийсь код виявлення примхливих приміток. Схоже, це було б гарним заключним проектом для когось із класу DSP.