;---------------------------------------------------------------------
; LPANAL. Performs LPC analysis
;
; snd        sound for analysis
; an-dur     duration of analysis (= duration of sound)
; skiptime   step frame to frame
; npoles     number of poles
;
; RESULT:    analysis data in list format.
;            Every element of the list is a list of the form
;
;            (RMS1 RMS2 ERR FILTER-COEFS)
;
;             RMS1  Energy (not rms value) of input signal
;             RMS2  Energy (not rms value) of residual signal
;             ERR   = sqrt(RMS2/RMS1) If it is small then VOICED sound,
;                                     else UNVOICED
;             FILTER-COEFS Array of filter coefs.
;
;
;                The z transform of filter is  H(z) = 1/A(z)
;                 
;                where A(z) is a polynome of the form:
;
;
;                A(z) = 1 + a1 z + a2 z^2 + a3 z^3 + ... + aP z^P
;
;             FILTER-COEFS is the array
;
;                  #(-aP -aP-1 -aP-2 ... a3 a2 a1)
;
;                 (this format is suited for the filter ALLPOLES)
;
 

(defun lpanal (snd an-dur skiptime npoles)
  (let* ((sr (snd-srate snd))
         (skipsize (round (* sr skiptime)))
         (numframes (round (- (/ (* sr an-dur) skipsize) 0.5)))
         (sndcpy (snd-copy  snd))
         (result (make-array numframes)))
     (dotimes (i numframes)
         (setf (aref result i)
               (snd-lpanal 
                  (snd-fetch-array sndcpy  (* 2 skipsize)  skipsize)
                  npoles)))
     (format t "LPANAL: duration: ~A frames: ~A  framedur: ~A skip: ~A poles ~A\n"
                                 an-dur     numframes     (* skiptime 2)  skiptime  npoles)
     result))


;; this code is based on fft_demo.lsp -- the idea is that an object
;; will return a stream of LPC frames. Other objects can manipulate
;; these frames, creating a new stream, or turn the frames back into
;; sound.

(setf lpc-class (send class :new '(sound framesize skipsize npoles)))

(send lpc-class :answer :isnew '(snd framedur skiptime np) '(
  (let ((sr (snd-srate snd)))
    (setf sound snd)
    (setf framesize (round (* sr framedur)))
    (setf skiptime (round (* sr skiptime)))
    (setf npoles np)))

(send lpc-class :next '() '(
  (let ((samps (snd-fetch-array framesize skipsize)))
    (cond ((null samps) nil)
          (t 
           (snd-lpanal samps npoles))))) 

(defun make-lpanal-iterator (sound framedur skiptime npoles)
  (send lpc-class :new (snd-copy sound) framedur skiptime npoles))
  

;---------------------------------------------------------------------
; SHOW-LPC-DATA.  Show values of LPC analysis frames
;
; lpc-data      data generated by LPANAL
; iniframe      first frame to show
; endframe      last frame to show
; poles?        T or NIL show or not filter coefs

(defun show-lpc-data (lpc-data iniframe endframe &optional (poles? NIL))
  (if (> endframe (length lpc-data)) (setf endframe (length lpc-data)))
  (dotimes (i (- endframe iniframe))
     (if poles? (format t "FRM ~A : ~A\n" (+ i iniframe) (aref lpc-data (+ i iniframe)))
                (format t "FRM ~A : ~A\n" (+ i iniframe) (reverse (cdr (reverse (aref lpc-data (+ i iniframe)))))))))



;----------------------------------------------------------------------
; LPC-FREQ. Show frequency response of ALLPOLES filter.
;           NEEDS MATLAB or OCTAVE
;
;
; HELPER FUNS :  GET-FILTER-COEFS from a LPC analysis data
;                       lpc-data:   data generated by LPCANAL
;                       numframe:   index of frame data
;
;                LPC-COEFS-TO-MATLAB : transforms LPC coefs format to Matlab format
;
; LPC-FREQ.
;
;           varname : the name of variable that holds coef array in MATLAB
;           lpc-data : as above
;           numframe : as above
;

(defun get-filter-coefs (lpc-data numframe)
  (nth 3 (aref lpc-data numframe)))


(defun lpc-coefs-to-matlab (lpc-data numframe)
  (let* ((lpc-coefs (get-filter-coefs lpc-data numframe))
         (lencoefs (length lpc-coefs))
         (result (make-array (1+ lencoefs))))
    (setf (aref result 0) 1.0)
    (dotimes (i lencoefs)
      (setf (aref result (1+ i))
            (- (aref lpc-coefs (- lencoefs i 1)))))
    result))


(defun lpc-freq (varname lpc-data numframe)
  (octave (list (list (lpc-coefs-to-matlab lpc-data numframe) varname 'ARR))))

 
;----------------------------------------------------------------------------
; ALLPOLES
;

(defun get-allpoles-gain (lpc-data numframe)
  (nth 2 (aref lpc-data numframe)))      ; se toma ERR para que la amplitud de
                                         ; la salida se aproxime a 1

(defun allpoles-from-lpc (snd lpc-data numframe)
   (snd-allpoles snd (get-filter-coefs lpc-data numframe) (get-allpoles-gain lpc-data numframe)))

;-------------------------------------------------------------------------------
; LPRESON

(defun get-lpreson-gain (lpc-data numframe)
  (sqrt (nth 1 (aref lpc-data numframe))))      ; se toma (sqrt RMS2) para que la amplitud se
                                                ; aproxime a la original


(defun get-lpreson-coefs (lpc-data numframe)
  (nth 3 (aref lpc-data numframe)))


