Библиотеки aubio обернуты SWIG и поэтому могут использоваться Python. Среди их многочисленных особенностей - несколько методов обнаружения / оценки основного тона, в том числе алгоритм YIN и некоторые алгоритмы гармонической гребенки.
Однако, если вы хотите что-то более простое, я написал некоторый код для оценки высоты тона некоторое время назад, и вы можете взять его или оставить. Это не так точно, как использование алгоритмов в aubio, но может оказаться достаточно для ваших нужд. Я в основном просто взял FFT данных, умноженных на окно (в данном случае это окно Blackman), возведя в квадрат значения FFT, нашел корзину с самым высоким значением и использовал квадратичную интерполяцию вокруг пика, используя журнал максимального значения и два соседних значения, чтобы найти основную частоту. Квадратичная интерполяция я взял из какой-то бумаги, которую нашел.
Он довольно хорошо работает на тестовых сигналах, но он не будет таким же надежным или точным, как другие методы, упомянутые выше. Точность может быть увеличена путем увеличения размера чанка (или уменьшения путем его уменьшения). Размер куска должен быть кратным 2, чтобы в полной мере использовать БПФ. Кроме того, я определяю только основной шаг для каждого куска без наложения. Я использовал PyAudio для воспроизведения звука во время записи приблизительной высоты звука.
Исходный код:
# Read in a WAV and find the freq's
import pyaudio
import wave
import numpy as np
chunk = 2048
# open up a wave
wf = wave.open('test-tones/440hz.wav', 'rb')
swidth = wf.getsampwidth()
RATE = wf.getframerate()
# use a Blackman window
window = np.blackman(chunk)
# open stream
p = pyaudio.PyAudio()
stream = p.open(format =
p.get_format_from_width(wf.getsampwidth()),
channels = wf.getnchannels(),
rate = RATE,
output = True)
# read some data
data = wf.readframes(chunk)
# play stream and find the frequency of each chunk
while len(data) == chunk*swidth:
# write data out to the audio stream
stream.write(data)
# unpack the data and times by the hamming window
indata = np.array(wave.struct.unpack("%dh"%(len(data)/swidth),\
data))*window
# Take the fft and square each value
fftData=abs(np.fft.rfft(indata))**2
# find the maximum
which = fftData[1:].argmax() + 1
# use quadratic interpolation around the max
if which != len(fftData)-1:
y0,y1,y2 = np.log(fftData[which-1:which+2:])
x1 = (y2 - y0) * .5 / (2 * y1 - y2 - y0)
# find the frequency and output it
thefreq = (which+x1)*RATE/chunk
print "The freq is %f Hz." % (thefreq)
else:
thefreq = which*RATE/chunk
print "The freq is %f Hz." % (thefreq)
# read some more data
data = wf.readframes(chunk)
if data:
stream.write(data)
stream.close()
p.terminate()