;---------------------------------------------------------------------
; 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)
;

(setfn lpc-frame-rms1 car)
(setfn lpc-frame-rms2 cadr)
(setfn lpc-frame-err caddr)
(setfn lpc-frame-filter-coefs cadddr) 

;; LPANAL-CLASS -- does lpc analysis. Frames are returned
;;   from an iterator object

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

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

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

(defun make-lpanal-iterator (sound framedur skiptime npoles)
  (send lpanal-class :new (snd-copy sound) framedur skiptime npoles))
  
;; LPC-FILE-CLASS -- iterator returns frames from file
;;
(setf lpc-file-class (send class :new '(file)))

(send lpc-file-class :answer :isnew '(filename) '(
  (setf file (open filename))
  (if (null file) (error "lpc-file-class could not open file" filename))))

(send lpc-file-class :answer :next '() '(
  (read file)))

(defun make-lpc-file-iterator (filename)
  (send lpc-file-class :new filename))


;; SAVE-LPC-FILE -- create a file from an iterator. This file can
;; be turned back into an iterator using make-lpc-file-iterator.
;;
(defun save-lpc-file (lpc-iterator filename)
  (let ((fp (open filename :direction :output))
        (frame t))
    (while frame
      (setf frame (send lpc-iterator :next))
      (if frame (format fp "~A~%" frame)))
    (close fp)))



;; SHOW-LPC-DATA. Show values of LPC analysis frames from interator.
;;
(defun show-lpc-data (lpc-iterator iniframe endframe &optional (poles? NIL))
  (dotimes (i iniframe) (send lpc-iterator :next))
  (dotimes (i (- endframe iniframe))
    (let ((frame (send lpc-iterator :next)))
      (cond ((null frame) (return))
            (poles?
             (format t "FRM ~A : ~A\n" (+ i iniframe) frame))
            (t
             (format t "FRM ~A : ~A\n" (+ i iniframe) 
                     (reverse (cdr (reverse frame)))))))))
                         

;----------------------------------------------------------------------
; 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
;


; THIS CODE TO GET FREQUENCY ASSUMES AN ARRAY OF LPC FRAMES AND REQUIRES
; MATLAB OR OCTAVE. I HAVE NOT ADAPTED THIS TO USE THE STREAM OF FRAMES
; APPROACH. -RBD
;
;(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
;
; THIS VERSION IS FOR ARRAY OF FRAMES
;
;(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)))

; ALLPOLES USES A SINGLE FRAME TO CREATE A FILTER
;
; We introduce two functions: 
;   NTH-FRAME runs the iterator to get the nth frame;
;   ALLPOLES-FROM-LPC filters a sound given a frame from an iterator

;; NTH-FRAME - get the nth frame from lpc iterator, 
;;    first frame is numbered zero
(defun nth-frame (lpc-iterator numframe)
  (dotimes (i numframe) (send lpc-iterator :next))
  (send lpc-iterator :next))


;; ALLPOLES-FROM-LPC -- filter a sound using an LPC frame
;;
(defun allpoles-from-lpc (snd lpc-frame)
  (snd-allpoles snd (lpc-frame-filter-coefs lpc-frame) 
                    (lpc-frame-err lpc-frame))) ;; use ERR for gain

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

(setfn lpreson snd-lpreson)
