颤声法获取基频
我正在尝试通过倒谱方法来找出频率。为了测试,我获取了一个音频文件,链接是http://www.mediacollege.com/audio/tone/files/440Hz_44100Hz_16bit_05sec.wav,这个音频信号的频率是440Hz。
我使用了以下公式:
cepstrum = IFFT (log FFT (s))
我得到了256个数据块,但我的结果总是错误的……
from numpy.fft import fft, ifft
import math
import wave
import numpy as np
from scipy.signal import hamming
index1=15000;
frameSize=256;
spf = wave.open('440.wav','r');
fs = spf.getframerate();
signal = spf.readframes(-1);
signal = np.fromstring(signal, 'Int16');
index2=index1+frameSize-1;
frames=signal[index1:int(index2)+1]
zeroPaddedFrameSize=16*frameSize;
frames2=frames*hamming(len(frames));
frameSize=len(frames);
if (zeroPaddedFrameSize>frameSize):
zrs= np.zeros(zeroPaddedFrameSize-frameSize);
frames2=np.concatenate((frames2, zrs), axis=0)
fftResult=np.log(abs(fft(frames2)));
ceps=ifft(fftResult);
posmax = ceps.argmax();
result = fs/zeroPaddedFrameSize*(posmax-1)
print result
在这种情况下,如何得到结果为440呢?
**
更新:
**
好吧,我在matlab中重写了我的代码,现在一切似乎都正常了。我用440Hz和250Hz的频率进行了测试……
对于440Hz,我得到了441Hz,效果还不错。
对于250Hz,我得到了249.1525Hz,结果也很接近。
我找到了一种简单的方法来获取倒谱值中的峰值。
我觉得可以通过四次插值来找到更好的结果,以便找到最大值!
我正在绘制440Hz估计的结果。
分享一下用于倒谱频率估计的源代码:
%% ederwander Cepstral Frequency (Matlab)
waveFile='440.wav';
[y, fs, nbits]=wavread(waveFile);
subplot(4,2,1); plot(y); legend('Original signal');
startIndex=15000;
frameSize=4096;
endIndex=startIndex+frameSize-1;
frame = y(startIndex:endIndex);
subplot(4,2,2); plot(frame); legend('4096 CHUNK signal');
%make hamming window
win = hamming(length(frame));
%samples multplied by hamming window
windowedSignal = frame.*win;
fftResult=log(abs(fft(windowedSignal)));
subplot(4,2,3); plot(fftResult); legend('FFT signal');
ceps=ifft(fftResult);
subplot(4,2,4); plot(ceps); legend('ceps signal');
nceps=length(ceps)
%find the peaks in ceps
peaks = zeros(nceps,1);
k=3;
while(k <= nceps - 1)
y1 = ceps(k - 1);
y2 = ceps(k);
y3 = ceps(k + 1);
if (y2 > y1 && y2 >= y3)
peaks(k)=ceps(k);
end
k=k+1;
end
subplot(4,2,5); plot(peaks); legend('PEAKS');
%get the maximum ...
[maxivalue, maxi]=max(peaks)
result = fs/(maxi+1)
subplot(4,2,6); plot(result); %legend('Frequency is' result);
legend(sprintf('Final Result Frequency =====>>> (%8.3f)',result))
3 个回答
我之前也遇到过类似的问题,所以我借用了你代码的一部分,并通过对同一帧进行连续评估来提高结果的质量,然后从中选择中间值。
现在我得到了稳定的结果。
def fondamentals(frames0, samplerate):
mid = 16
sample = mid*2+1
res = []
for first in xrange(sample):
last = first-sample
frames = frames0[first:last]
res.append(_fondamentals(frames, samplerate))
res = sorted(res)
return res[mid] # We use the medium value
def _fondamentals(frames, samplerate):
frames2=frames*hamming(len(frames));
frameSize=len(frames);
ceps=ifft(np.log(np.abs(fft(frames2))))
nceps=ceps.shape[-1]*2/3
peaks = []
k=3
while(k < nceps - 1):
y1 = (ceps[k - 1])
y2 = (ceps[k])
y3 = (ceps[k + 1])
if (y2 > y1 and y2 >= y3): peaks.append([float(samplerate)/(k+2),abs(y2), k, nceps])
k=k+1
maxi=max(peaks, key=lambda x: x[1])
return maxi[0]
倒谱方法最适合处理那些含有丰富谐波成分的信号,而对于接近纯正弦波的信号效果就不太好了。
最好的测试信号可能是一些在时间上重复出现、间隔非常接近的脉冲(在FFT窗口内的脉冲越多越好),这样在频域中就会产生接近均匀间隔的重复峰值,这部分会在倒谱的激励部分显示出来。脉冲响应则会在倒谱的低共振部分表示出来。
如果你的采样频率是44.1 kHz,那么256这个数字可能太小了,没法做什么有用的事情。在这种情况下,你的快速傅里叶变换(FFT)的分辨率会是44100除以256,结果是172赫兹。如果你想要大约10赫兹的分辨率,那么你可以使用4096作为FFT的大小。