;;; most of this file is obsolete.
;;;
;;; playing-related examples previously scattered around at random
;;;
;;; play-sound func -- play current sound, calling (func data) on each buffer if func is passed
;;; play-often, play-until-c-g -- play sound n times or until C-g is typed
;;; play-region-forever -- play region over and over until C-g typed
;;; loop-between-marks -- play while looping continuously between two movable marks
;;; start-dac -- hold DAC open and play sounds via keyboard
;;; "vector synthesis"
;;; play-with-amps -- play channels with individually settable amps
;;; play-sine and play-sines -- produce tones direct to DAC

;;; see also play-syncd-marks and play-between-marks in marks.scm

(provide 'snd-play.scm)

(define* (open-play-output out-chans out-srate out-format out-bufsize)
  ;; returns (list audio-fd chans frames)
  (let* ((outchans (or out-chans 1))
	 (cur-srate (or out-srate (and (pair? (sounds)) (srate)) 22050))
	 (pframes (or out-bufsize 256))
	 (frm (or out-format (if (little-endian?) mus-lshort mus-bshort)))
	 (outbytes (* pframes 2))     ; 2 here since we'll first try to send short (16-bit) data to the DAC
	 (audio-fd 
	  ;; ALSA throws an error where the rest of the audio cases simply report failure
	  ;;   so we turn off the "error" printout, catch the error itself, and toss it
	  (let ((no-error (null? (hook-functions mus-error-hook))))
	    (if no-error
		(hook-push mus-error-hook (lambda (hook) (set! (hook 'result) #t))))
	    (let ((val (catch #t
			      (lambda ()
				(mus-audio-open-output 0 cur-srate outchans frm outbytes))
			      (lambda args -1)))) ; -1 returned in case of error
	      (if no-error 
		  (set! (hook-functions mus-error-hook) ()))
	      val))))
    (if (not (= audio-fd -1))
	(set! (dac-size) outbytes))
    (list audio-fd outchans pframes)))


(define* (play-sound func)
  "(play-sound func) plays the currently selected sound, calling func on each data buffer, if func exists (mono only)"
  (if (pair? (sounds))
      (let* ((filechans (chans))
	     (audio-info (open-play-output filechans (srate) #f 256))
	     (audio-fd (car audio-info))
	     ;; (outchans (cadr audio-info))
	     (pframes (caddr audio-info)))
	(if (not (= audio-fd -1))
	    (let ((len (framples)))
	      (do ((beg 0 (+ beg pframes)))
		  ((> beg len))
		(mus-audio-write audio-fd (make-shared-vector (func (channel->float-vector beg pframes)) (list 1 pframes)) pframes))
	      (mus-audio-close audio-fd))
	    (snd-print ";could not open dac")))
      (snd-print ";no sounds open")))

#|
(play-sound (lambda (data) (float-vector-scale! data 2.0) data))
|#

;;; this could also be done with a function argument to the play function -- get a
;;;   sampler for the sound, call it on each invocation of the argument function etc


;;; -------- play sound n times -- (play-often 3) for example.

(define (play-often n) 
  "(play-often n) plays the selected sound 'n' times (interruptible via C-g)"
  (let ((plays (- n 1)))
    (define (play-once reason)
      (if (and (> plays 0)
	       (= reason 0))
	  (begin
	    (set! plays (- plays 1))
	    (play (selected-sound) :start 0 :stop play-once))))
    (play (selected-sound) :start 0 :stop play-once)))

;(bind-key #\p 0 (lambda (n) "play often" (play-often (max 1 n))))


;;; -------- play sound until c-g

(define (play-until-c-g)
  "(play-until-c-g) plays the selected sound until you interrupt it via C-g"
  (define (play-once reason)
    (if (= reason 0)
	(play (selected-sound) :start 0 :stop play-once)))
  (play (selected-sound) :start 0 :stop play-once))


;;; -------- play region over and over until C-g typed

(define (play-region-forever reg1)
  "(play-region-forever reg) plays region 'reg' until you interrupt it via C-g"

  (let ((reg (if (integer? reg1) (integer->region reg1) reg1)))

    (define (play-region-again reason)
      (if (= reason 0) ; 0=play completed normally
	  (play reg :wait #f :stop play-region-again)))

    (play reg :wait #f :stop play-region-again)))

;(bind-key #\p 0 (lambda (n) "play region forever" (play-region-forever ((regions) (max 0 n)))))


;;; -------- play while looping continuously between two movable marks

(define (loop-between-marks m1 m2 bufsize)
  "(loop-between-marks mark1 mark2 buffersize) plays while looping between two marks.  x typed in the graph, or C-g in the listener exits the loop."
  (let* ((pos1 (mark-sample m1))
	 (pos2 (mark-sample m2))
	 (beg (min pos1 pos2))
	 (end (max pos1 pos2))
	 (bytes (* bufsize 2)) ; mus-audio-write handles the translation to short (and takes frames, not bytes as 3rd arg)
	 (audio-fd (mus-audio-open-output 0 (srate) 1 mus-lshort bytes))
	 (stop-looping #f))
    (if (not (= audio-fd -1))
	(begin
	  (bind-key #\space 0 (lambda () (set! stop-looping #t))) ; type space in the graph to stop this loop
	  (do ()
	      (stop-looping
	       (mus-audio-close audio-fd)
	       (unbind-key #\space 0))
	    (mus-audio-write audio-fd (make-shared-vector (channel->float-vector beg bufsize) (list 1 bufsize)) bufsize)
	    (set! beg (+ beg bufsize))
	    (if (>= beg end)
		(begin
		  (set! pos1 (mark-sample m1)) ; get current mark positions (can change while looping)
		  (set! pos2 (mark-sample m2))
		  (set! beg (min pos1 pos2))
		  (set! end (max pos1 pos2)))))))))

;;; m1 and m2 are marks
;;; (loop-between-marks (caaar (marks)) (cadaar (marks)) 512)



;;; -------- hold DAC open and play sounds via keyboard

(define* (start-dac (sr 44100) (chans 1))
  "(start-dac (srate 44100) (chans 1)) starts the DAC running continuously in the background"
  (play #f :srate sr :channels chans))

(define stop-dac stop-playing)



;;; play-with-amps -- play channels with individually settable amps

(define play-with-amps
  (lambda (sound . amps)
    "(play-with-amps snd :rest amps) plays snd with each channel scaled by the corresponding 
amp: (play-with-amps 0 1.0 0.5) plays channel 2 of stereo sound at half amplitude"
    (let ((chans (channels sound)))
      (do ((chan 0 (+ 1 chan)))
          ((= chan chans))
        (let ((player (make-player sound chan)))
          (set! (amp-control player) (amps chan))
          (add-player player)))
      (start-playing chans (srate sound)))))


;;; play-sine and play-sines

(define (play-sine freq amp)
  "(play-sine freq amp) plays a 1 second sinewave at freq and amp"
  (let ((len 44100)
	(osc (make-oscil freq)))
    (play (lambda ()
	    (set! len (- len 1))
	    (if (<= len 0)
		#f
		(* amp (oscil osc)))))))


(define (play-sines freqs-and-amps)
  "(play-sines freqs-and-amps) produces a tone given its spectrum: (play-sines '((440 .4) (660 .3)))"
  (let* ((len 44100)
	 (num-oscs (length freqs-and-amps))
	 (frqs (make-float-vector num-oscs))
	 (amps (make-float-vector num-oscs)))
    (do ((i 0 (+ i 1)))
	((= i num-oscs))
      (set! (frqs i) (hz->radians (car (freqs-and-amps i))))
      (set! (amps i) (cadr (freqs-and-amps i))))
    (let ((ob (make-oscil-bank frqs (make-float-vector num-oscs 0.0) amps)))
      (play (lambda ()
	      (and (positive? (set! len (- len 1)))
		   (oscil-bank ob)))))))

;(play-sines '((425 .05) (450 .01) (470 .01) (546 .02) (667 .01) (789 .034) (910 .032)))
