Nyquist provides functions to perform Linear Prediction Coding (LPC) analysis and synthesis. In simple terms, LPC analysis assumes that a sound is the result of an all-pole filter applied to a source with a flat spectrum. LPC is good for characterizing the general spectral shape of a signal, which may be time-varying as in speech sounds. For synthesis, any source can be filtered, allowing the general spectral shape of one signal (used in analysis) to be applied to any source (used in synthesis). A popular effect is to give vowel-like spectra to musical tones, creating an artificial (or sometimes natural) singing voice.
Examples of LPC analysis and synthesis can be found in the file
nyquist/lib/lpc/lpc_tutorial.html
,
which is part of the standard Nyquist release.
As with FFT processing, LPC analysis takes a sound as input and returns
a stream of frames. Frames are returned from an object using the :next
selector just as with FFT frames. An LPC frame is a list consisting of:
RMS1, the energy of the input signal, RMS2, the energy of the
residual signal, ERR, the square root of RMS1/RMS2, and
FILTER-COEFS, an array of filter coefficients. To make code more
readable and to avoid code dependence on the exact format of a frame,
the functions lpc-frame-rms1
,
lpc-frame-rms2
,
lpc-frame-err
, and
lpc-frame-filter-coefs
can be
applied to a frame to obtain the respective fields.
The z transform
of the filter is H(z) = 1/A(z), where A(z) is a
polynomial of the form A(z) = 1 + a1z-1 +
a2z-2 + ... + apz-p. The FILTER-COEFS array has
the form #(
ap ap-1 ... a3
a2 a1)
.
The file lpc.lsp
defines some useful classes and functions. The file
is not automatically loaded with Nyquist, so you must execute
(load "lpc")
before using them.
make-lpanal-iterator(sound, framedur, skiptime, npoles)
[SAL](make-lpanal-iterator sound framedur skiptime npoles)
[LISP]lpanal-class
,
that returns LPC frames from successive frames of samples in
sound. The duration (in seconds)
of each frame is given by framedur, a
FLONUM
. The skip size (in seconds) between successive frames
is given by skiptime, a FLONUM
. Typical values for
framedur and skiptime are 0.08 and 0.04, giving 25 frames
per second and a 50% frame overlap. The number of poles is given
by npoles, a FIXNUM
. The result is an object that
responds to the :next
selector by returning a frame as
described above. NIL
is returned when sound terminates.
(Note that one or more of the last analysis windows may be
padded with zeros. NIL
is only returned when the corresponding
window would begin after the termination time of the sound.)make-lpc-file-iterator(filename)
[SAL](make-lpc-file-iterator filename)
[LISP]lpc-file-class
to access them. Create a file using save-lpc-file
(see below).save-lpc-file(lpc-iterator, filename)
[SAL](save-lpc-file lpc-iterator filename)
[LISP]make-lpc-file-iterator
(see above).show-lpc-data(lpc-iterator,
iniframe, endframe [, poles?])
[SAL](show-lpc-data lpc-iterator iniframe endframe
[poles?])
[LISP]lpanal-class
or
lpc-file-class
. Frames are numbered from zero, and only
files starting at iniframe (a FIXNUM
) and ending before
endframe (also a FIXNUM
) are printed. By default, only
the values for RMS1, RMS2, and ERR are printed, but
if optional parameter poles? is non-NIL
, then
the LPC coefficients are also printed.allpoles-from-lpc(snd, lpc-frame)
[SAL](allpoles-from-lpc snd lpc-frame)
[LISP]allpoles-from-lpc
to apply this filter to snd,
a SOUND
. To obtain lpc-frame, a LIST
containing an LPC frame, either send :next
to an
LPC iterator, or use nth-frame
(see below). The result
is a SOUND
whose duration is the same as that of snd.nth-frame(lpc-iterator, numframe)
[SAL](nth-frame lpc-iterator numframe)
[LISP]snd
:
allpoles-from-lpc(snd, nth-frame(make-lpc-file-iterator(filename), n))
.lpreson(snd, lpc-iterator,
skiptime)
[SAL](lpreson snd lpc-iterator skiptime)
[LISP]SOUND
to be filtered is snd, and the source of
LPC frames is lpc-iterator, typically an instance of
lpanal-class
or lpc-file-class
. The frame
period (in seconds) is given by skiptime (a FLONUM
).
This number does not have to agree with the skiptime used
to analyze the frames. (Greater values will cause the filter
evolution slow down, and smaller values will cause it to
speed up.) The result is a SOUND
. The duration of the
result is the minimum of the duration of snd and that of
the sequence of frames. lpc-frame-rms1(frame)
[SAL](lpc-frame-rms1 frame)
[LISP]lpc-frame-rms2(frame)
[SAL](lpc-frame-rms2 frame)
[LISP]lpc-frame-err(frame)
[SAL](lpc-frame-err frame)
[LISP]lpc-frame-filter-coefs(frame)
[SAL](lpc-frame-filter-coefs frame)
[LISP]The lowest-level Nyquist functions for LPC are
snd-lpanal
for analysis,snd-allpoles
, an all-pole filter with fixed coefficients, andsnd-lpreson
, an all-pole filter that takes frames from an LPC iterator.
snd-lpanal(samps, npoles)
[SAL](snd-lpanal samps npoles)
[LISP]FIXNUM
) poles from an
ARRAY
of samples (FLONUMS
). Note that snd-fetch-array
can be used to fetch a sequence of frames from a sound. Ordinarily, you
should not use this function. Use make-lpanal-iterator
instead.snd-allpoles(snd, lpc-coefs, gain)
[SAL](snd-allpoles snd lpc-coefs gain)
[LISP]SOUND
. The filter coefficients are given by lpc-coefs
(an ARRAY
), and the filter gain is given by gain, a FLONUM
.
The result is a SOUND
whose duration matches that of snd.
Ordinarily, you should use allpoles-from-lpc
instead (see above).snd-lpreson(snd, lpc-iterator,
skiptime)
[SAL](snd-lpreson snd lpc-iterator skiptime)
[LISP]lpreson
(see above).