(* ;;-*-LISP-*- KEEP EMACS HAPPY ********************************
*
*     AUDIO
*
****************************************************************)

(DEFVAR AUDIO.BUFFER)
(DEFVAR AUDIO.BUFFER.SIZE 9)

(DEFPROP PAGEOFFSET
  MACRO
  ((BASE NPAGES) (\ADDBASE BASE (LLSH NPAGES 8))))

(DEFPROP ZEROPAGEOFFSET
  MACRO
  (OPENLAMBDA (B P)
	      (PROG (PP)
		(SETQ PP (LLSH P 8))
		(\ZEROWORDS (\ADDBASE B PP)
			    (\ADDBASE B (+ PP 255))))))

(DEFPROP .AUDIO.
  DOPVAL
  (1 AUDIO))

(BLOCKRECORD AUDIOBLK
  ((OUT0 WORD)
   (OUTN WORD)
   (MCPAGE BYTE)
   (MCWORD BYTE)
   (INDELTA WORD)))

(DEFEXPR (AUDIO.RESET)
  (PROG (N)
    (SETQ N (1- (x 256 AUDIO.BUFFER.SIZE)))
    (\ZEROWORDS AUDIO.BUFFER (\ADDBASE AUDIO.BUFFER N))
    (SETF (AUDIOBLK.OUT0 AUDIO.BUFFER) 256)
    (SETF (AUDIOBLK.OUTN AUDIO.BUFFER) N)
    (SETF (AUDIOBLK.MCPAGE AUDIO.BUFFER) 1)
    (SETF (AUDIOBLK.MCWORD AUDIO.BUFFER) 0)
    (SETF (AUDIOBLK.INDELTA AUDIO.BUFFER) 0)
))

(DEFEXPR (AUDIO.INIT)
  (PROG ()
    (SETQ AUDIO.BUFFER (\VAG2 63 0))
    (FOR I FROM 0 TO (1- AUDIO.BUFFER.SIZE)
     DO (\NEWPAGE (PAGEOFFSET AUDIO.BUFFER I)))
    (\LOCKPAGES AUDIO.BUFFER AUDIO.BUFFER.SIZE)
    (AUDIO.RESET)
))
(OR AUDIO.BUFFER (AUDIO.INIT))

(DEFEXPR (AUDIO.PLAY.FILE FILE)
  (* Play FILE.  For good results, FILE should be on {CORE}. *)
  (PROG (STREAM FILEPN MYPTR PAGECOUNT)
    (SETQ STREAM (OPENSTREAM FILE 'INPUT))
    (AUDIO.RESET)
    (SETQ PAGECOUNT (/ (+ (GETEOFPTR STREAM) 511) 512))
    (COND ((> PAGECOUNT AUDIO.BUFFER.SIZE)
	   (\READPAGES STREAM (1- PAGECOUNT)
		       (PAGEOFFSET AUDIO.BUFFER 1))
	   (* cause entire page map to be created.)
	   (SETQ FILEPN (1- AUDIO.BUFFER.SIZE))
	   (SETQ MYPTR 1))
	  (T (SETQ FILEPN PAGECOUNT)
	     (SETQ MYPTR (1+ PAGECOUNT))))
    (FOR I TO FILEPN
     DO (\READPAGES STREAM (1- I) (PAGEOFFSET AUDIO.BUFFER I)))
    (RESETFORM (\AUDIO AUDIO.BUFFER)
	       (FOR F FROM FILEPN TO (1- PAGECOUNT)
		DO (WHILE (EQ MYPTR (AUDIOBLK.MCPAGE AUDIO.BUFFER)))
		(\READPAGES STREAM F (PAGEOFFSET AUDIO.BUFFER MYPTR))
		(COND ((EQ MYPTR (1- AUDIO.BUFFER.SIZE))
		       (SETQ MYPTR 1))
		      (T (SETQ MYPTR (1+ MYPTR)))))
	       (WHILE (EQ MYPTR (AUDIOBLK.MCPAGE AUDIO.BUFFER)))
	       (* Zero one last page and turn 
		  off ucode when it gets there)
	       (ZEROPAGEOFFSET AUDIO.BUFFER MYPTR)
	       (UNTIL (EQ MYPTR (AUDIOBLK.MCPAGE AUDIO.BUFFER))))
    (CLOSEF STREAM)
))

(DEFEXPR (AUDIO.PLAY.ARRAY ARRAY)
  (* Play ARRAY.  ARRAY type BYTE origin 0. *)
  (PROG (BASE FILEPN MYPTR PAGECOUNT)
    (SETQ BASE (ARRAYP.BASE ARRAY))
    (AUDIO.RESET)
    (SETQ PAGECOUNT (/ (+ (ARRAYSIZE ARRAY) 511) 512))
    (COND ((> PAGECOUNT AUDIO.BUFFER.SIZE)
	   (\MOVEWORDS (PAGEOFFSET BASE (1- PAGECOUNT)) 0
		       (PAGEOFFSET AUDIO.BUFFER 1) 0 256)
	   (* cause entire page map to be created.)
	   (SETQ FILEPN (1- AUDIO.BUFFER.SIZE))
	   (SETQ MYPTR 1))
	  (T (SETQ FILEPN PAGECOUNT)
	     (SETQ MYPTR (1+ PAGECOUNT))))
    (FOR I TO FILEPN
     DO (\MOVEWORDS (PAGEOFFSET BASE (1- I)) 0
		    (PAGEOFFSET AUDIO.BUFFER I) 0 256))
    (RESETFORM (\AUDIO AUDIO.BUFFER)
	       (FOR F FROM FILEPN TO (1- PAGECOUNT)
		DO (WHILE (EQ MYPTR (AUDIOBLK.MCPAGE AUDIO.BUFFER)))
		(\MOVEWORDS (PAGEOFFSET BASE F) 0
			    (PAGEOFFSET AUDIO.BUFFER MYPTR) 0 256)
		(COND ((EQ MYPTR (1- AUDIO.BUFFER.SIZE))
		       (SETQ MYPTR 1))
		      (T (SETQ MYPTR (1+ MYPTR)))))
	       (WHILE (EQ MYPTR (AUDIOBLK.MCPAGE AUDIO.BUFFER)))
	       (* Zero one last page and turn 
		  off ucode when it gets there)
	       (ZEROPAGEOFFSET AUDIO.BUFFER MYPTR)
	       (UNTIL (EQ MYPTR (AUDIOBLK.MCPAGE AUDIO.BUFFER))))
))

(DEFEXPR (AUDIO.REPEAT.ARRAY ARRAY)
  (* Play ARRAY.  ARRAY type BYTE origin 0. *)
  (PROG (BASE FILEPN MYPTR PAGECOUNT)
    (SETQ BASE (ARRAYP.BASE ARRAY))
    (AUDIO.RESET)
    (SETQ PAGECOUNT (/ (+ (ARRAYSIZE ARRAY) 511) 512))
    (COND ((> PAGECOUNT AUDIO.BUFFER.SIZE)
	   (\MOVEWORDS (PAGEOFFSET BASE (1- PAGECOUNT)) 0
		       (PAGEOFFSET AUDIO.BUFFER 1) 0 256)
	   (* cause entire page map to be created.)
	   (SETQ FILEPN (1- AUDIO.BUFFER.SIZE))
	   (SETQ MYPTR 1))
	  (T (SETQ FILEPN PAGECOUNT)
	     (SETQ MYPTR (1+ PAGECOUNT))))
    (FOR I TO FILEPN
     DO (\MOVEWORDS (PAGEOFFSET BASE (1- I)) 0
		    (PAGEOFFSET AUDIO.BUFFER I) 0 256))
    (RESETFORM (\AUDIO AUDIO.BUFFER)
	       (FOR F FROM FILEPN TO (1- PAGECOUNT)
		DO (WHILE (EQ MYPTR (AUDIOBLK.MCPAGE AUDIO.BUFFER)))
		(\MOVEWORDS (PAGEOFFSET BASE F) 0
			    (PAGEOFFSET AUDIO.BUFFER MYPTR) 0 256)
		(COND ((EQ MYPTR (1- AUDIO.BUFFER.SIZE))
		       (SETQ MYPTR 1))
		      (T (SETQ MYPTR (1+ MYPTR)))))
	       (DO (FOR F FROM 1 TO (1- PAGECOUNT)
		    DO (WHILE (EQ MYPTR (AUDIOBLK.MCPAGE AUDIO.BUFFER)))
		    (\MOVEWORDS (PAGEOFFSET BASE F) 0
				(PAGEOFFSET AUDIO.BUFFER MYPTR) 0 256)
		    (COND ((EQ MYPTR (1- AUDIO.BUFFER.SIZE))
			   (SETQ MYPTR 1))
			  (T (SETQ MYPTR (1+ MYPTR)))))))
))

(DEFEXPR (AUDIO.RECORD FILE 
		       (OPTIONAL NSEC 5)
		       (OPTIONAL RECORDONFN 'DEFAULTRECORDONFN))
  (PROG (STREAM COP)
    (SETQ STREAM (OPENSTREAM FILE 'OUTPUT))
    (AUDIO.RESET)
    (SETQ COP 1)
    (OR (EQ NSEC T)
	(\TRUNCATEFILE STREAM (x 16 NSEC)
		       0))
    (* Preallocate space)
    (APPLY* RECORDONFN T)
    (* Wait for user to tell you to start)
    (RESETFORM (\AUDIO AUDIO.BUFFER)
	       (FOR F FROM 0 WHILE (APPLY* RECORDONFN NIL)
		DO (WHILE (EQ COP (AUDIOBLK.MCPAGE AUDIO.BUFFER)))
		(\WRITEPAGES STREAM F (PAGEOFFSET AUDIO.BUFFER COP))
		(ZEROPAGEOFFSET AUDIO.BUFFER COP)
		(COND ((EQ COP (1- AUDIO.BUFFER.SIZE))
		       (SETQ COP 1))
		      (T (SETQ COP (1+ COP))))
		FINALLY (SETQ COP (LLSH F 9))))
    (\SETEOFPTR STREAM COP)
    (CLOSEF FILE)))

(DEFVAR DEFAULTRECORDONFNCLK)

(DEFEXPR (DEFAULTRECORDONFN STARTP)
  (DECLARE (GLOBALVARS DEFAULTRECORDONFNCLK))
  (PROG ()
    (COND (STARTP
	   (PROMPTPRINT "Press and hold CTRL key to record.")
	   (UNTIL (KEYDOWNP 'CTRL))
	   (PROMPTPRINT "Recording in progress...")
	   (SETQ DEFAULTRECORDONFNCLK (CLOCK)))
	  ((KEYDOWNP 'CTRL)
	   (* Continue to record. *)
	   (RETURN T))
	  (T (PROMPTPRINT "Recording complete. Duration: ")
	     (PRINTOUT PROMPTWINDOW
		       (/$ (CLOCKDIFFERENCE DEFAULTRECORDONFNCLK)
			   1000.0))
	     " seconds." T))
))

(DEFEXPR (\AUDIO BUF)
  (* Any odd pointer turns off the audio ucode)
  (OR (.AUDIO. (OR BUF (\ADDBASE AUDIO.BUFFER 1)))
      (SHOULDNT "No audio microcode"))
  (\ADDBASE AUDIO.BUFFER 1))
(COND ((NOT (CCODEP (GETD '\AUDIO)))
       (COMPILE '\AUDIO)))

STOP