#IF AUDIOTRACE
;
#DEFINE	AUDTRACE(STR)	PUSH DE \ LD DE, STR \ CALL WRITESTR \ POP DE
#DEFINE AUDTRACE_A	CALL PRTHEXBYTE
#DEFINE AUDTRACE_B	PUSH AF \ LD A, B \ CALL PRTHEXBYTE \ POP AF
#DEFINE AUDTRACE_D	PUSH AF \ LD A, D \ CALL PRTHEXBYTE \ POP AF
#DEFINE AUDTRACE_E	PUSH AF \ LD A, E \ CALL PRTHEXBYTE \ POP AF
#DEFINE AUDTRACE_L	PUSH AF \ LD A, L \ CALL PRTHEXBYTE \ POP AF
#DEFINE AUDTRACE_HL	CALL PRTHEXWORDHL
#DEFINE AUDTRACE_BC	PUSH HL \ PUSH BC \ POP HL \ CALL PRTHEXWORDHL \ POP HL
#DEFINE AUDTRACE_DE	PUSH HL \ PUSH DE \ POP HL \ CALL PRTHEXWORDHL \ POP HL
#DEFINE AUDTRACE_IY	PUSH HL \ PUSH IY \ POP HL \ CALL PRTHEXWORDHL \ POP HL
;
#DEFINE AUDDEBUG(S) push hl \ CALL PRTSTRD \ .TEXT S \ .TEXT "$" \ pop hl	; $$$$$$ PRINT STRING S TO CONSOLE - PRTD("HELLO") - NO TRAILING $ REQUIRED
;
#DEFINE AUDTRACE_CR AUDDEBUG("\r\n$")
;
#ELSE
;
#DEFINE	AUDTRACE(S)
#DEFINE AUDTRACE_A
#DEFINE AUDTRACE_B
#DEFINE AUDTRACE_D
#DEFINE AUDTRACE_E
#DEFINE AUDTRACE_L
#DEFINE AUDTRACE_BC
#DEFINE AUDTRACE_HL
#DEFINE AUDTRACE_DE
#DEFINE AUDTRACE_IY
;
#DEFINE AUDDEBUG(STR)
;
#DEFINE AUDTRACE_CR
;
#ENDIF
;
#IFNDEF AUDIO_INC
#DEFINE AUDIO_INC
;
; NOTE TABLE VALUES ARE SHIFTED A FEW EXTRA BITS TO MAKE THE
; DIVISION AS ACCURATE AS POSSIBLE.
;
AUD_SCALE	.EQU	3
;
; ON ENTRY, DE IS ADDRESS OF NOTE TABLE, HL IS NOTE TO PLAY
; NOTE VALUE 0 MEANS B0b/A0# IN OCTAVE 0 WHICH IS THE FIRST ENTRY
; OF THE NOTE TABLE.  THE NOTE TABLE REPRESENTS THE FREQUENCIES
; FOR 1 FULL OCTAVE IN QUARTER NOTES.  SINCE THERE ARE 12 NOTES
; IN AN OCTAVE, THE TABLE HAS 48 ENTRIES FOR ALL QUARTER NOTES.
;
; ON EXIT, HL CONTAINS THE PERIOD VALUE TO PROGRAM INTO THE PSG
; DERIVED FROM THE NOTE TABLE SCALED TO THE REQUESTED OCTAVE.
;
AUD_NOTE:
	AUDDEBUG("AUDNOTE ")
	AUDTRACE_HL
	AUDTRACE_CR
	AUDTRACE_DE
	AUDTRACE_CR
;
	; START BY SEPARATING THE NOTE AND OCTAVE PORTION
	; OF THE INCOMING TONE VALUE IN HL
	PUSH	DE			; SAVE NOTE TABLE ADR
	LD	DE,48			; 48 QUARTERNOTES PER OCTAVE
	CALL	DIV16			; SEPARATE OCTAVE AND NOTE
;
	; THE QUOTIENT (BC) IS THE OCTAVE NUMBER REQUESTED
	; THE REMAINDER (HL) IS THE QUARTER NOTE WITHIN THE OCTAVE.
;
	; NOW USE THE QUARTER NOTE VALUE TO LOOKUP THE CORRESPONDING
	; PSG PERIOD VALUE IN THE NOTE TABLE.
	ADD	HL,HL			; SCALE FOR 2 BYTE TABLE ENTRY SIZE
	POP	DE			; RECOVER THE TABLE ADR
	ADD	HL,DE			; HL := DESIRED TABLE ENTRY ADR
	LD	A,(HL)			; GET LOW BYTE OF PERIOD TO A
	INC	HL			; POINT TO HIGH BYTE VALUE
	LD	H,(HL)			; GET HIGH BYTE OF PERIOD TO H
	LD	L,A			; PUT LOW BYTE INTO L
;
	; NOW WE SCALE THE PERIOD VALUE DOWN BASED ON THE OCTAVE VALUE IN C
	; AND BY THE AUD_SCALE VALUE THAT WAS USED IN THE NOTE TABLE.
	; FOR EACH OCTAVE, THE PERIOD IS HALVED WHICH DOUBLES THE FREQUENCY
	; THAT WILL BE PRODUCED BY THE PSG.  SINCE MOVING UP AN OCTAVE
	; SHOULD PRODUCE A FREQUENCY THAT IS TWICE THE LOWER OCTIAVE, THIS
	; WORKS NICELY, ALTHOUGH SOME ERROR MAY BE INTRODUCED.
;
	LD	A,AUD_SCALE		; SHIFT BY AUD_SCALE BITS
	ADD	A,C			; ... AND OCTAVE
	LD	B,A			; USE AS LOOP COUNTER
AUD_NOTE1:
	SRL	H			; RIGHT SHIFT HL
	RR	L			; ... BY ONE BIT
	DJNZ	AUD_NOTE1		; LOOP UNTIL DONE
;
	; IF THE RESULTANT PERIOD IS ZERO, IT MEANS THAT THE REQUESTED
	; PERIOD IS TOO LOW (FREQUENCY TOO HIGH) FOR THE PSG TO PRODUCE.
	LD	A, L			; CHECK FOR ZERO
	OR	H			; ... MEANING PSG CAN'T DO IT
	RET	NZ			; IF NOT ZERO, RETURN THE CALCULATED PERIOD
	DEC	HL			; OTHERWISE RETURN -1 PERIOD (ERROR)
	RET
#ENDIF
