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 file lpcdemo.lsp in the demos folder fo 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.
(load "lpc")At the top of lpcdemo.lsp, you see the following lines:
(setf *lpc-path* (current-path)) (setf *lpc-data* (strcat *lpc-path* "lpc-exmpl.dat"))The (current-path) function call returns the full file system path to the file being loaded (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.
(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")))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.
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:
(defun ex-1 () (show-lpc-data (make-lpc-file-iterator *lpc-data*) 100 120 NIL))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:
(setf iterator (make-lpc-file-iterator *lpc-data*)) (send iterator :next)This will return the first frame of the LPC analysis. Send :next again to get the next frame.
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:
(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)) )))Rather than iterate through a file to find the desired frame, you can also just store the desired frames as in the following:
(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:
(defun noise-vocal (lpcdata dur) (allpoles-from-lpc (noise dur) lpcdata))Combining this with our definitions of different frames, we can write a little sequence
(defun ex-5 () (play (seq (noise-vocal a-lpcdata 1) (noise-vocal e-lpcdata 1) (noise-vocal i-lpcdata 1))))We can do the same thing using a buzz sound rather than noise:
(defun buzz-vocal (lpcdata dur) (allpoles-from-lpc (buzz 16 e2 (const 0.0 dur)) lpcdata))
(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:
(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:
More examples can be found in lpcdemo.lsp.