-
Notifications
You must be signed in to change notification settings - Fork 3
/
Copy pathlpc.py
159 lines (129 loc) · 4.8 KB
/
lpc.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
import numpy as np
import scipy as sp
import scipy.signal as sig
def lpc_ref(signal, order):
"""Compute the Linear Prediction Coefficients.
Return the order + 1 LPC coefficients for the signal. c = lpc(x, k) will
find the k+1 coefficients of a k order linear filter:
xp[n] = -c[1] * x[n-2] - ... - c[k-1] * x[n-k-1]
Such as the sum of the squared-error e[i] = xp[i] - x[i] is minimized.
Parameters
----------
signal: array_like
input signal
order : int
LPC order (the output will have order + 1 items)
Notes
----
This is just for reference, as it is using the direct inversion of the
toeplitz matrix, which is really slow"""
if signal.ndim > 1:
raise ValueError("Array of rank > 1 not supported yet")
if order > signal.size:
raise ValueError("Input signal must have a lenght >= lpc order")
if order > 0:
p = order + 1
r = np.zeros(p, signal.dtype)
# Number of non zero values in autocorrelation one needs for p LPC
# coefficients
nx = np.min([p, signal.size])
x = np.correlate(signal, signal, 'full')
r[:nx] = x[signal.size-1:signal.size+order]
phi = np.dot(sp.linalg.inv(sp.linalg.toeplitz(r[:-1])), -r[1:])
return np.concatenate(([1.], phi))
else:
return np.ones(1, dtype = signal.dtype)
def levinson_1d(r, order):
"""Levinson-Durbin recursion, to efficiently solve symmetric linear systems
with toeplitz structure.
Parameters
---------
r : array-like
input array to invert (since the matrix is symmetric Toeplitz, the
corresponding pxp matrix is defined by p items only). Generally the
autocorrelation of the signal for linear prediction coefficients
estimation. The first item must be a non zero real.
Notes
----
This implementation is in python, hence unsuitable for any serious
computation. Use it as educational and reference purpose only.
Levinson is a well-known algorithm to solve the Hermitian toeplitz
equation:
_ _
-R[1] = R[0] R[1] ... R[p-1] a[1]
: : : : * :
: : : _ * :
-R[p] = R[p-1] R[p-2] ... R[0] a[p]
_
with respect to a ( is the complex conjugate). Using the special symmetry
in the matrix, the inversion can be done in O(p^2) instead of O(p^3).
"""
r = np.atleast_1d(r)
if r.ndim > 1:
raise ValueError("Only rank 1 are supported for now.")
n = r.size
if n < 1:
raise ValueError("Cannot operate on empty array !")
elif order > n - 1:
raise ValueError("Order should be <= size-1")
if not np.isreal(r[0]):
raise ValueError("First item of input must be real.")
elif not np.isfinite(1/r[0]):
raise ValueError("First item should be != 0")
# Estimated coefficients
a = np.empty(order+1, r.dtype)
# temporary array
t = np.empty(order+1, r.dtype)
# Reflection coefficients
k = np.empty(order, r.dtype)
a[0] = 1.
e = r[0]
for i in xrange(1, order+1):
acc = r[i]
for j in range(1, i):
acc += a[j] * r[i-j]
k[i-1] = -acc / e
a[i] = k[i-1]
for j in range(order):
t[j] = a[j]
for j in range(1, i):
a[j] += k[i-1] * np.conj(t[i-j])
e *= 1 - k[i-1] * np.conj(k[i-1])
return a, e, k
def lpcres(signal, order, usefft=True):
"""Compute the LPC residual of a signal.
The LPC residual is the 'error' signal from LPC analysis, and is defined
as:
res[n] = x[n] - xe[n] = 1 + a[1] x[n-1] + ... + a[p] x[n-p]
Where x is the input signal and xe the linear prediction of x.
Parameters
----------
signal : array-like
input signal. If rank of signal is 2, each row is assumed to be an
independant signal on which the LPC is computed. Rank > 2 is not
supported.
order : int
LPC order
Returns
-------
res : array-like
LPC residual
Note
----
The LPC residual can also be seen as the input of the LPC analysis filter.
As the LPC filter is a whitening filter, it is a whitened version of the
signal.
In AR modelling, the residual is simply the estimated excitation of the AR
filter.
"""
if signal.ndim == 1:
return sp.signal.lfilter(lpc_ref(signal, order), 1., signal)
elif signal.ndim == 2:
if usefft:
cf = lpc(signal, order, axis=-1)[0]
else:
c = acorr(signal, maxlag=order, onesided=True)/signal.shape[-1]
cf = levinson(c, order, axis=-1)[0]
return slfilter(cf, np.ones((cf.shape[0], 1)), signal)
else:
raise ValueError("Input of rank > 2 not supported yet")