在Python中使用LPC估计共振峰

时间:2014-08-03 18:32:59

标签: python matlab numpy scipy signal-processing

我是信号处理的新手(对于那个问题,我是numpy,scipy和matlab)。我正在尝试通过调整这个matlab代码来估计Python中使用LPC的元音共振峰:

http://www.mathworks.com/help/signal/ug/formant-estimation-with-lpc-coefficients.html

到目前为止,这是我的代码:

#!/usr/bin/env python
import sys
import numpy
import wave
import math
from scipy.signal import lfilter, hamming
from scikits.talkbox import lpc

"""
Estimate formants using LPC.
"""

def get_formants(file_path):

    # Read from file.
    spf = wave.open(file_path, 'r') # http://www.linguistics.ucla.edu/people/hayes/103/Charts/VChart/ae.wav

    # Get file as numpy array.
    x = spf.readframes(-1)
    x = numpy.fromstring(x, 'Int16')

    # Get Hamming window.
    N = len(x)
    w = numpy.hamming(N)

    # Apply window and high pass filter.
    x1 = x * w
    x1 = lfilter([1., -0.63], 1, x1)

    # Get LPC.
    A, e, k = lpc(x1, 8)

    # Get roots.
    rts = numpy.roots(A)
    rts = [r for r in rts if numpy.imag(r) >= 0]

    # Get angles.
    angz = numpy.arctan2(numpy.imag(rts), numpy.real(rts))

    # Get frequencies.
    Fs = spf.getframerate()
    frqs = sorted(angz * (Fs / (2 * math.pi)))

    return frqs

print get_formants(sys.argv[1])

使用this file作为输入,我的脚本返回此列表:

[682.18960189917243, 1886.3054773107765, 3518.8326108511073, 6524.8112723782951]

我甚至没有按照带宽过滤频率的最后步骤,因为列表中的频率不正确。根据Praat的说法,我应该得到这样的东西(这是元音中间的共振峰列表):

Time_s     F1_Hz        F2_Hz         F3_Hz         F4_Hz
0.164969   731.914588   1737.980346   2115.510104   3191.775838 

我做错了什么?

非常感谢

UPDATE:

我改变了这个

x1 = lfilter([1., -0.63], 1, x1)

x1 = lfilter([1], [1., 0.63], x1)

根据Warren Weckesser的建议,我现在正在接受

[631.44354635609318, 1815.8629524985781, 3421.8288991389031, 6667.5030877036006]

我觉得我错过了一些东西,因为F3非常关闭。

更新2:

我意识到由于采样频率不同,传递给order的{​​{1}}已关闭。将其更改为:

scikits.talkbox.lpc

现在我得到了:

Fs = spf.getframerate() ncoeff = 2 + Fs / 1000 A, e, k = lpc(x1, ncoeff)

更接近普拉特的估计!

3 个答案:

答案 0 :(得分:3)

问题与传递给lpc函数的顺序有关。 2 + fs / 1000其中fs是采样频率,根据以下规则是经验法则:

http://www.phon.ucl.ac.uk/courses/spsci/matlab/lect10.html

答案 1 :(得分:1)

我无法得到您期望的结果,但我注意到两件可能会导致一些差异的事情:

  1. 您的代码使用[1, -0.63],其中您提供的链接中的MATLAB代码为[1 0.63]
  2. 您的处理一次应用于整个x向量,而不是其中的较小段(请参阅MATLAB代码执行此操作的位置:x = mtlb(I0:Iend);)。

  3. 希望有所帮助。

答案 2 :(得分:1)

至少有两个问题:

  • 根据链接,"预加重滤波器是高通全极(AR(1))滤波器"。这里给出的系数的符号是​​正确的:[1, 0.63]。如果您使用[1, -0.63],则会获得低通滤波器。

  • 您有scipy.signal.lfilter反向的前两个参数。

所以,试着改变这个:

x1 = lfilter([1., -0.63], 1, x1)

到此:

x1 = lfilter([1.], [1., 0.63], x1)

我还没有尝试过运行你的代码,所以我不知道这些是否是唯一的问题。