As with FFT frames, Nyquist does not have a special data type corresponding to a sequence of LPC frames. Instead, a sequence of frames is represented by an XLISP object. Whenever you send the selector :next to the object, you get back either NIL, indicating the end of the sequence, or you get an LPC frame. (Note that FFT frames and LPC frames are not compatible.)
This tutorial is based on the files demos/src/lpcdemo.lsp
and demos/src/lpcdemo.sal
in the Nyquist release. Not every line is covered, but this
tutorial should give you a pretty full explanation of how to use
LPC in Nyquist.
The code here is written in SAL syntax, with LISP syntax
following in fine print.
load "lpc.lsp"At the top of lpcdemo.lsp, you see the following lines:
(load "lpc")
variable *lpc-path* = current-path()The current-path function call returns the full file system path to the file being loaded (lpcdemo.sal or lpcdemo.lsp). We save this into *lpc-path* so we can find other related files later, even if the current directory is changed. *lpc-data* is one of the file names we will use later.
variable *lpc-data* = strcat(*lpc-path*, "lpc-exmpl.dat")
(setf *lpc-path* (current-path)) (setf *lpc-data* (strcat *lpc-path* "lpc-exmpl.dat"))
function do-lpc-analysis()You can analyze a sound using make-lpanal-iterator. This function takes a sound, a frame size, a step size, and the number of poles. In do-lpc-analysis (above), we used 0.08 seconds, 0.04 seconds, and 50 poles for these parameters. The result of make-lpanal-iterator is an object that will deliver LPC frames on demand. In this function, we will grab all of the frames and write them to a file using save-lpc-file. The data is written to "temp.dat". You should run this function and look at the file, which contains ASCII data. Later, we will use this data to filter a sound.
begin
with myfile = strcat(*lpc-path*, "demo-snd.aiff")
exec save-lpc-file(make-lpanal-iterator(s-read(myfile), 0.08, 0.04, 50),
"temp.dat")
end
(defun do-lpc-analysis () (let ((myfile (strcat *lpc-path* "a-snd-file.snd"))) (save-lpc-file (make-lpanal-iterator (s-read *myfile*) 0.08 0.04 50) "temp.dat")))
Since a-snd-file.snd is a very short and uninteresting sound, we will use a different set of LPC frames in the following synthesis examples. The data is in lpc-exmpl.dat, and the full path for this file was computed earlier and stored in *lpc-data*.
The first example will just show how to read the LPC data file using make-lpc-file-iterator, which takes the file name as a parameter. You can print some frame data using show-lpc-data as shown in the following:
function ex-1()The additional parameters to show-lpc-data are the starting frame (100), the ending frame (120), and a flag (NIL) indicating whether to print filter coefficients. If you want to look a little closer at the inner workings of this code, you can send a :next message to an LPC iterator object as follows:
exec show-lpc-data(make-lpc-file-iterator(*lpc-data*), 100, 120, NIL)
(defun ex-1 () (show-lpc-data (make-lpc-file-iterator *lpc-data*) 100 120 NIL))
set iterator = make-lpc-file-iterator(*lpc-data*)This will return the first frame of the LPC analysis. Send :next again to get the next frame.
exec send(iterator, :next)
(setf iterator (make-lpc-file-iterator *lpc-data*)) (send iterator :next)
The function allpoles-from-lpc constructs a filter from
the
frame and applies it to a sound. In this case, the source sound is
created
by buzz with a little vibrato provided by lfo:
functioni ex-4()Rather than iterate through a file to find the desired frame, you can also just store the desired frames as in the following:
play seq(allpoles-from-lpc(buzz, 12, f2, lfo(5.0, 4.0)),
nth-frame(make-lpc-file-iterator(*lpc-data*), 30),
allpoles-from-lpc(buzz, 12, f2, lfo(5.1, 4.0)),
nth-frame(make-lpc-file-iterator(*lpc-data*), 60),
allpoles-from-lpc(buzz, 12, g2, lfo(5.3, 4.0),
nth-frame(make-lpc-file-iterator(*lpc-data*), 100)))
(defun ex-4 () (play (seq (allpoles-from-lpc (buzz 12 f2 (lfo 5.0 4.0)) (nth-frame (make-lpc-file-iterator *lpc-data*) 30)) (allpoles-from-lpc (buzz 12 f2 (lfo 5.1 4.0)) (nth-frame (make-lpc-file-iterator *lpc-data*) 60)) (allpoles-from-lpc (buzz 12 g2 (lfo 5.3 4.0)) (nth-frame (make-lpc-file-iterator *lpc-data*) 100)) )))
set a-lpcdata = list(63.2144, 0.674387, 0.103287,
vector(-0.0381026, 0.00804115, 0.0109905, 0.0145117, 0.00199174,
-0.00129314, 0.0171826, 0.0181176, 0.00179391, -0.0114089,
-0.0120949, -0.000410595, -0.0122539, -0.0209354 -0.00804976,
-0.00345041, -0.00409532, -0.00227011, 0.014224, 0.0135451,
0.0056023, -0.00651142, -0.00564953, -0.0168921, -0.0377939,
-0.0449506, -0.0355592, -0.0339316, -0.0454434, 1.19336))
set e-lpcdata = list(40.7157, 0.149753, 0.0606467,
vector(0.0244574, -0.0225545, -0.0172724, -0.0122709, -0.0042946,
0.00886974, 0.0121516, 0.0120936, 0.00197545 -0.00582163,
-0.018367, -0.0201546, -0.00440599, 0.00638936, 0.0166275,
0.0185066, 0.00890464, -0.00158013, -0.00494974, -0.00479037,
0.0130814, 0.0138648, -0.0022018, -0.021368, -0.0343532,
-0.0312712, -0.0574975, -0.0918824, -0.112016, 1.31398))
set i-lpcdata = list(5.5391, 0.0321825, 0.0762238,
vector(-0.0341124, -0.0149688, -0.00585657, -0.0111572, 0.00769712,
0.0190367, 0.00885366, 0.0112762, 0.0118286, -0.00059044,
-0.0140864, -0.0123688, -0.0151128, 0.00214354, -0.00810219,
-0.00538188, 0.00631382, 0.020771, 0.0356498, 0.0295531,
0.0242797, 0.0124296, 0.00445127, -0.013062, -0.0387178,
-0.0527783, -0.0685511, -0.076575, -0.0846335, 1.24521))
(setf a-lpcdata '(63.2144 0.674387 0.103287 #(-0.0381026 0.00804115 0.0109905 0.0145117 0.00199174 -0.00129314 0.0171826 0.0181176 0.00179391 -0.0114089 -0.0120949 -0.000410595 -0.0122539 -0.0209354 -0.00804976 -0.00345041 -0.00409532 -0.00227011 0.014224 0.0135451 0.0056023 -0.00651142 -0.00564953 -0.0168921 -0.0377939 -0.0449506 -0.0355592 -0.0339316 -0.0454434 1.19336)))
(setf e-lpcdata '(40.7157 0.149753 0.0606467 #(0.0244574 -0.0225545 -0.0172724 -0.0122709 -0.0042946 0.00886974 0.0121516 0.0120936 0.00197545 -0.00582163 -0.018367 -0.0201546 -0.00440599 0.00638936 0.0166275 0.0185066 0.00890464 -0.00158013 -0.00494974 -0.00479037 0.0130814 0.0138648 -0.0022018 -0.021368 -0.0343532 -0.0312712 -0.0574975 -0.0918824 -0.112016 1.31398)))
(setf i-lpcdata '(5.5391 0.0321825 0.0762238 #(-0.0341124 -0.0149688 -0.00585657 -0.0111572 0.00769712 0.0190367 0.00885366 0.0112762 0.0118286 -0.00059044 -0.0140864 -0.0123688 -0.0151128 0.00214354 -0.00810219 -0.00538188 0.00631382 0.020771 0.0356498 0.0295531 0.0242797 0.0124296 0.00445127 -0.013062 -0.0387178 -0.0527783 -0.0685511 -0.076575 -0.0846335 1.24521)))The following function applies a filter to noise:
function noise-vocal(lpcdata, dur)Combining this with our definitions of different frames, we can write a little sequence
return allpoles-from-lpc(noise(dur), lpcdata)
(defun noise-vocal (lpcdata dur) (allpoles-from-lpc (noise dur) lpcdata))
function ex-5()We can do the same thing using a buzz sound rather than noise:
play seq(noise-vocal(a-lpcdata, 1),
noise-vocal(e-lpcdata, 1),
noise-vocal(i-lpcdata, 1))
(defun ex-5 () (play (seq (noise-vocal a-lpcdata 1) (noise-vocal e-lpcdata 1) (noise-vocal i-lpcdata 1))))
function buzz-vocal(lpcdata, dur)
return allpoles-from-lpc(buzz, 16, e2, const(0.0, dur),
lpcdata)
(defun buzz-vocal (lpcdata dur) (allpoles-from-lpc (buzz 16 e2 (const 0.0 dur)) lpcdata))
function ex-6()
play seq(buzz-vocal(a-lpcdata, 1),
buzz-vocal(e-lpcdata, 1)
buzz-vocal(i-lpcdata, 1))
(defun ex-6 () (play (seq (buzz-vocal a-lpcdata 1) (buzz-vocal e-lpcdata 1) (buzz-vocal i-lpcdata 1))))
Here, the LPC data from *lpc-data* is used to modulate noise:
function ex-7a()
;; parameters are sound, lpc-iterator, skiptime
return lpreson(noise(6.5),
make-lpc-file-iterator(*lpc-data*), 0.04)
function ex-7()
play ex-7a()
(defun ex-7a () ;; parameters are sound, lpc-iterator, skiptime (lpreson (noise 6.5) (make-lpc-file-iterator *lpc-data*) 0.04))
(defun ex-7 () (play (ex-7a)))The same thing can be done to filter a buzz sound. This example generates some vocal-like sounds in two-part harmony:
function ex-8a(p, dur)
return lpreson(buzz(16, p, 1.5 * lfo(3.0, dur)),
make-lpc-file-iterator(*lpc-data*), 0.04)
function ex-8()
play sim(seq(ex-8a(c4, 1), ex-8a(g3, 1), ex-8a(a3, 1),
ex-8a(b3, 1), ex-8a(c4 1)),
seq(ex-8a(c2, 2), ex-8a(f2, 1), ex-8a(g2, 1),
ex-8a(e2 1)))
(defun ex-8a (p dur) (lpreson (buzz 16 p (scale 1.5 (lfo 3.0 dur))) (make-lpc-file-iterator *lpc-data*) 0.04)) (defun ex-8 () (play (sim (seq (ex-8a c4 1) (ex-8a g3 1) (ex-8a a3 1) (ex-8a b3 1) (ex-8a c4 1)) (seq (ex-8a c2 2) (ex-8a f2 1) (ex-8a g2 1) (ex-8a e2 1)))))
More examples can be found in lpcdemo.lsp.