(defpackage "ecl-mpv" (:nicknames :mpv))

(in-package "ecl-mpv")

(defmacro getsin (len frq &optional (offset 0)
                           (fs 44100) (A 127)) "
(getsin len frq)
counter that returns count mod len of a
frequency frq 127 sin w*count

The actual utility of len could be debated.
"
 `(let* ((len ,len) (frq ,frq)
         (offset ,offset) (fs ,fs) (a ,a)
         (coef (/ (* 2 PI frq) fs)))
    (lambda ()
      (nth-value
       0
       (round
        (* A 0.5
           (1+ (sin (* coef
                       (setf
                        offset
                        (mod (incf offset)
                             fs)))))))))))

(defmacro enbuff (&optional (file "buffer.u8")) "
Opens a :direction :io file to use as a buffer
optional different file name.
"
 `(setq *io*
        (open ,file
              :direction
              :io)))

(defmacro debuff () "
closes and :aborts whatever *io* is
"
 `(when *io* (close *buff* :abort t)))

(defmacro getout () "
Makes (out) a closure that returns an output
stream to mpv, which has been instructed to
configure and then pass rawaudio through to its
default audio device.
Nonportable ECL extension to spawn process
Has to be like this or ECL will try and gc proc
"
 `(setf
   (symbol-function 'out)
   (multiple-value-bind
         (streams ret process)
       (ext:run-program
        "mpv"
        '("--demuxer=rawaudio"
          "--demuxer-rawaudio-format=u8"
          "--demuxer-rawaudio-rate=44100"
          "-")
        :wait nil)
     (lambda ()
       (two-way-stream-output-stream streams)))))

(defmacro norm (form) "
(/ ,form 127)
"
 `(/ ,form 127))

(defmacro mron (form) "
(truncate (* 127 ,form))
"
 `(nth-value 0 (truncate (* 127 ,form))))

(defmacro compose (&body body) "
de/norms and composes some closures in a closure
"
 `(lambda ()
    (mron
     (reduce '*
             ,(append '(list) (mapcar (lambda (x)
                        `(norm (funcall ,x)))
                      body))
             :initial-value 1))))