
SFX__BuffSize	=	286			; Must be an even number!
SFX__FastMode	=	0
SFX__Debug	=	1
SFX__Mode	=	1

Go		move.w	#248,d0
		bsr.w	SFX_Init		; Initialize with fairly low
						; rate. (This makes us use
						; samples with low frequencies,
						; which again saves memory!)
		beq.s	.Quit

		moveq.l	#0,d0
		lea.l	Sample1,a0
		lea.l	Sample1End,a1
		bsr.w	SFX_StartSound		; Start sample 1

		moveq.l	#1,d0
		lea.l	Sample2,a0
		lea.l	Sample2End,a1
		bsr.w	SFX_StartSound		; Start sample 2

.a		cmp.b	#$ff,$dff006
		bne.s	.a
.b		cmp.b	#$ff,$dff006
		beq.s	.b

		btst	#6,$bfe001		; Wait for mouse
		bne.s	.a

.Quit		bsr.w	SFX_End			; End

		moveq.l	#0,d0			; Successful exit
		rts

;------------------------------------------------------------------------------
;
;	$VER: SoundFX v1.2 sound replay - by Håvard "Howard" Pedersen
;	© 1994-95 Mental Diseases
;
;	A simple program for handling the replay of multiple soundeffects
;	through a single audio channel.
;
;	I cannot be held responsible for any damage caused directly or in-
;	directly by this code. Still, every released version is thouroughly
;	tested with Mungwall and Enforcer, official Commodore debugging tools.
;	These programs traps writes to unallocated ram and reads/writes to/from
;	non-ram memory areas, which should cover most bugs.
;
;	HISTORY:
;
;v1.0	Based on an article in AM/FM #11 by Teijo Kinuunen. Simple thing with
;	preset playback rate. Soundeffects length has to be a multiple of
;	SFX__BuffSize.
;
;v1.1	Upvamped version.
;	* Selectable playback rate. (No big deal)
;	* Mastervolume.
;
;v1.2	Threw the original mixing scheme out.
;	* Fixed a bug where the interrupt sometimes didn't fill the entire
;	  output-buffer. (Clicks and distorted sound could occur!)
;	* The end-of-sound detection was a bit buggy. Fixed.
;	* Earlier there was some problems with the sound not always appearing
;	  when running SoundFX for the first time. This _seems_ to be fixed.
;	* Included fast mode. (Max 246/248 in playback rate!)
;	* Added SFX_StopSound().
;	* Optimized mixing a lot.
;	* SoundFX did not stop when errors occured. Now errors are properly
;	  signalled.
;	* Did some alterations to make the source more portable. (Stopped using
;	  includes and my personal macros.)
;	* Uses Disable()/Enable() instead of some bogus macros to disable
;	  interrupts.
;	* Added non-interrupt mode, to make it possible to mix the channels
;	  within a vertical-blank interrupt.
;
;	TODO:
;
;* Expand to 2 channels/4 voices. (optional)
;
;------------------------------------------------------------------------------
;
;	CONSTANTS:
;
;Constant:	SFX__BuffSize(INTEGER)
;Purpose:	Specifies the size of SoundFXs output buffer. The number should
;		be even. A large value causes less overhead by the interrupt
;		code but uses more memory for the buffers.
;
;Constant:	SFX__FastMode(BOOL)
;Purpose:	If set to non-zero, the fastmode will be enabled. The fastmode
;		provides faster mixing but is only capable of playing samples
;		at half the normal rates. Please also note that the sounddata
;		does not need to be halved when using the fastmode. This mixing
;		mode may produce high-frequency tones which may both distort
;		the sound and destroy loudspeakers! To get rid of this extra-
;		feature, enable the audio filter. :)
;
;Constant:	SFX__Debug(BOOL)
;Purpose:	If set to true, colors will be used to display CPU usage for
;		mixing loop.
;
;Constant:	SFX__Mode(BOOL)
;Purpose:	Selects the mode for SoundFX to run in. Modes are:
;		0 - User vertical blank. SoundFX will be set up for vertical
;		    blank operation, but relies on the user to call
;		    SFX_VBLHandle() each vertical blank.
;		1 - OS AudioInterrupt. SoundFX installs the interrupt itself
;		    using the operating system.
;		Vertical blank modes needs you to carefully pick the buffersize
;		so that it's just about finished playing in one frame.
;
;	PUBLIC FUNCTIONS:
;
;Function:	SFX_Init(Rate)(D0)
;Purpose:	Initalize the routine and install audio-interrupt. Remember
;		that when the fastmode is enabled the actual sample playback
;		frequence will be half of what you state. Maximum values are
;		123 for PAL and 124 for NTSC. Remember to double these figures
;		when using the fastmode.
;
;Function:	SFX_End()
;Purpose:	Remove our audio interrupt and free any allocated resources.
;
;Function:	SFX_StartSound(Partition,Wave,Waveend)(D0,A0,A1)
;Purpose:	Start playing the selected sound. The sounddata should be
;		halved if the fastmode is disabled.
;
;Function:	SFX_StopSound(Partition)(D0)
;Purpose:	Stops the current sound.
;
;Function:	SFX_SetVolume(Volume.w (0-64))(D0)
;Purpose:	Alters the playback volume. Use for fading.
;
;	PRIVATE FUNCTIONS:
;
;Function:	SFX_VBLInit()
;Purpose:	Initializes SoundFX for use without using the audio interrupt.
;
;Function:	SFX_VBLEnd()
;Purpose:	Closes down SoundFX when audio interrupt hasn't been used.
;
;Function:	SFX_VBLHandle()
;		Does the actuall stuff when SoundFX is run in VBL-mode.
;
;Function:	SFX_AudIntInit()
;Purpose:	Initializes the audio interrupt handler.
;
;Function:	SFX_AudIntEnd()
;Purpose:	Closes down the audio interrupt handler.
;
;Function:	SFX_AudInt()
;Purpose:	This is the interrupt-handler for SoundFX when running in audio
;		interrupt mode.
;
;Function:	SFX_DoMix()
;Purpose:	Does actual mixing.
;
;------------------------------------------------------------------------------

; Set all undefined constants to default values. If you dont't like 'em, feel
; free to change, though you'll have to do it all over for each new version.

	ifnd SFX__BuffSize
SFX__BuffSize	=	1024			; 16-131072
	endc

	ifnd SFX__FastMode
SFX__FastMode	=	0			; BOOL
	endc

	ifnd SFX__Debug
SFX__Debug	=	0			; BOOL
	endc

	ifnd SFX__Mode
SFX__Mode	=	1			; BOOL
	endc

	rsreset	; Voice data
vd_StartPtr	rs.l	1			; Start of data, CHIP
vd_EndPtr	rs.l	1			; End of data, CHIP
vd_Active	rs.l	1
vd_sizeof	rs.b	0

SFX_WAITDMA	macro
		move.w	$dff006,d1
		moveq.l	#80-1,d0
.DMALoop1	cmp.w	$dff006,d1
		beq.s	.DMALoop1
		move.w	$dff006,d1
.DMALoop2	cmp.w	$dff006,d1
		beq.s	.DMALoop2
		move.w	$dff006,d1
   		dbf	d0,.DMALoop1
		endm

;------------------------------------------------------------------------------
;==				INIT					     ==
;------------------------------------------------------------------------------
SFX_Init
	ifne SFX__FastMode
		lsr.l	#1,d0		; Double playback rate
	endc

		move.w	d0,SFX_PlayRate

		moveq.l	#0,d0
		bsr.w	SFX_StopSound		; Play no sound.

		moveq.l	#1,d0
		bsr.w	SFX_StopSound		; Play no sound.

		lea	SFX_AudioBuff,a0
		move.w	#SFX__BuffSize-1,d1
.ClrLoop	clr.w	(a0)+			; Clear track buffers
		dbf	d1,.ClrLoop

		clr.b	SFX_WhichBuffer		; Buffer flag

	ifeq SFX__Mode
		bsr.w	SFX_VBLInit
	endc
	ifeq SFX__Mode-1
		bsr.w	SFX_AudIntInit
	endc
		beq.s	.Err

		moveq.l	#1,d0
		rts

.Err		bsr.s	SFX_End
		moveq	#0,d0
		rts

;------------------------------------------------------------------------------
;==				END					     ==
;------------------------------------------------------------------------------
SFX_End
	ifeq SFX__Mode
		bsr.w	SFX_VBLEnd
	endc
	ifeq SFX__Mode-1
		bsr.w	SFX_AudIntEnd
	endc

		rts

;------------------------------------------------------------------------------
;==				SETVOLUME				     ==
;------------------------------------------------------------------------------
SFX_SetVolume	move.w	d0,$dff0d8
		move.w	d0,SFX_Volume
		rts

;------------------------------------------------------------------------------
;==				STARTSOUND				     ==
;------------------------------------------------------------------------------
SFX_StartSound	tst.l	d0
		beq.s	.Zero

		lea	SFX_PartitionB,a2
		move.l	a0,vd_StartPtr(a2)	; Init ptr
		move.l	a1,vd_EndPtr(a2)	; Init endptr
		move.l	#1,vd_Active(a2)

		rts

.Zero		lea	SFX_PartitionA,a2
		move.l	a0,vd_StartPtr(a2)	; Init ptr
		move.l	a1,vd_EndPtr(a2)	; Init endptr
		move.l	#1,vd_Active(a2)

		rts

;------------------------------------------------------------------------------
;==				STOPSOUND				     ==
;------------------------------------------------------------------------------
SFX_StopSound	tst.l	d0
		beq.s	.Zero

		lea	SFX_PartitionB,a2
		move.l	#SFX_ZeroData,vd_StartPtr(a2)	; Init startptr
		move.l	#SFX_ZeroData+SFX__BuffSize,vd_EndPtr(a2); Init endptr
		move.l	#0,vd_Active(a2)

		rts

.Zero		lea	SFX_PartitionA,a2
		move.l	#SFX_ZeroData,vd_StartPtr(a2)	; Init startptr
		move.l	#SFX_ZeroData+SFX__BuffSize,vd_EndPtr(a2); Init endptr
		move.l	#0,vd_Active(a2)

		rts

	ifeq SFX__Mode

;------------------------------------------------------------------------------
;==				VBL INIT				     ==
;------------------------------------------------------------------------------
SFX_VBLInit	lea	$dff000,a0

		move.w	#$0008,$96(a0)		; dmacon (disable audio)
		move.l	#$0400,$9a(a0)		; intena (disable audio)

		move.w	#SFX__BuffSize/2,aud3+ac_len(a0); Buffer size
		move.w	#65535,aud3+ac_per(a0)		; Period
		move.l	#SFX_AudioBuff,aud3+ac_ptr(a0)	; Pointer
		move.w	SFX_Volume,aud3+ac_vol(a0)	; Volume

		SFX_WAITDMA

		move.w	#$8008,$96(a0)		; dmacon (enable audio)

		moveq.l	#1,d0
		rts

;------------------------------------------------------------------------------
;==				VBL END					     ==
;------------------------------------------------------------------------------
SFX_VBLEnd	move.w	#$0008,$dff096
		rts

;------------------------------------------------------------------------------
;==			VBL FRAMEHANDLER				     ==
;------------------------------------------------------------------------------
SFX_VBLFrame	lea.l	SFX_AudioBuff,a1
		not.b	SFX_WhichBuffer		; Swap buffer
		beq.s	.SetBuff

.UseBuff1	lea.l	SFX__BuffSize(a1),a1	; Get next buffer

.SetBuff	lea.l	$dff000,a0

		move.w	#$0008,$96(a0)		; dmacon (disable audio)

		move.w	#SFX__BuffSize/2,aud3+ac_len(a0); Buffer size
		move.w	SFX_PlayRate,aud3+ac_per(a0)	; Period
		move.l	a1,aud3+ac_ptr(a0)		; Pointer
		move.w	SFX_Volume,aud3+ac_vol(a0)	; Volume

		SFX_WAITDMA

		move.w	#$8008,$96(a0)		; dmacon (enable audio)

		bsr.s	SFX_DoMix

		rts

	endc

	ifeq SFX__Mode-1

;------------------------------------------------------------------------------
;==			AUDIOINTERRUPT INIT				     ==
;------------------------------------------------------------------------------
SFX_AudIntInit	moveq	#10,d0			; INTB_AUD3
		lea	SFX_AudIntReq,a1
		move.l	4.w,a6
		jsr	-162(a6)		; _LVOSetIntVector

		move.l	d0,SFX_PrevAudInt
		beq.w	.Err

		lea	$dff000,a0
		move.w	#SFX__BuffSize/2,$d4(a0); Buffer size
		move.w	#65535,$d6(a0)		; Period
		move.l	#SFX_AudioBuff,$d0(a0)	; Pointer
		move.w	SFX_Volume,$d8(a0)	; Volume

		lea	$dff000,a0
		move.w	#$4000,$9a(a0)

		move.l	4.w,a6
		jsr	-120(a6)		; _LVODisable

		lea	$dff000,a0
		move.w	#$0008,$96(a0)		; Audio DMA off
		move.w	#$8400,$9a(a0)		; Enable sound interrupt
		move.w	#$c000,$9a(a0)
		move.w	#$8008,$96(a0)		; DMA Audio channel 3 ON

		move.l	4.w,a6
		jsr	-126(a6)		; _LVOEnable

		moveq.l	#1,d0
		rts

.Err		moveq.l	#0,d0
		rts

;------------------------------------------------------------------------------
;==			AUDIOINTERRUPT END				     ==
;------------------------------------------------------------------------------
SFX_AudIntEnd	tst.b	SFX_IfPlay
		beq.s	.NoPlay

		move.w	#1<<10,$dff09a
		move.w	#$8,$dff096
		clr.b	SFX_IfPlay

.NoPlay		move.l	SFX_PrevAudInt,d0
		beq.s	.NoInt			; Any interrupt?

		move.l	#0,SFX_PrevAudInt	; Clear interrupt
		move.w	#1<<10,$dff09a		; intena
		move.l	d0,a1
		moveq	#10,d0
		move.l	4.w,a6
		jsr	-162(a6)		; _LVOSetIntVector

.NoInt		move.w	#$0008,$dff096		; Stop Audio DMA

		rts

;------------------------------------------------------------------------------
;==				INTHANDLER				     ==
;------------------------------------------------------------------------------
SFX_AudInt	movem.l	d2-a0/a2-a6,-(sp)

		not.b	SFX_WhichBuffer		; Swap buffer
		beq.s	.SetBuff

.UseBuff1	lea.l	SFX__BuffSize(a1),a1	; Get next buffer

.SetBuff	lea.l	$dff000,a0
		move.l	a1,$d0(a0)

 		move.w	#SFX__BuffSize/2,$d4(a0); Buffer size
		move.w	SFX_PlayRate,$d6(a0)	; Period
		move.w	SFX_Volume,$d8(a0)	; Volume

		move.w	#1<<10,$9c(a0)		; Clear intreq bit

		bsr.s	SFX_DoMix

		moveq.l	#0,d0

		movem.l	(sp)+,d2-a0/a2-a6
		rts

	endc

;------------------------------------------------------------------------------
;==				DOMIX					     ==
;------------------------------------------------------------------------------
SFX_DoMix
	ifne SFX__Debug
		move.w	#$fff,$dff180
	endc

		lea.l	SFX_PartitionA,a3

		tst.l	vd_Active(a3)		; Addvalue
		beq.s	.AOk

	ifeq SFX__FastMode
		add.l	#SFX__BuffSize,vd_StartPtr(a3)	; Next chunk
		move.l	(a3),d7			; Fetch ptr
		add.l	#SFX__BuffSize,d7
		cmp.l	vd_EndPtr(a3),d7	; Will we break?
		bls.s	.AOk
	else
		add.l	#SFX__BuffSize/2,vd_StartPtr(a3); Next chunk
		move.l	(a3),d7			; Fetch ptr
		add.l	#SFX__BuffSize/2,d7
		cmp.l	vd_EndPtr(a3),d7	; Will we break?
		bls.s	.AOk
	endc

		clr.l	vd_Active(a3)		; Stop sound
		move.l	#SFX_ZeroData,vd_StartPtr(a3)
		move.l	#SFX_ZeroData+SFX__BuffSize,vd_EndPtr(a3)

.AOk		lea.l	SFX_PartitionB,a4

		tst.l	vd_Active(a4)		; Addvalue
		beq.s	.BOk

	ifeq SFX__FastMode
		add.l	#SFX__BuffSize,vd_StartPtr(a4)	; Next chunk
		move.l	(a4),d7			; Fetch ptr
		add.l	#SFX__BuffSize,d7
		cmp.l	vd_EndPtr(a4),d7	; Will we break?
		bls.s	.BOk
	else
		add.l	#SFX__BuffSize/2,vd_StartPtr(a4)	; Next chunk
		move.l	(a4),d7			; Fetch ptr
		add.l	#SFX__BuffSize/2,d7
		cmp.l	vd_EndPtr(a4),d7	; Will we break?
		bls.s	.BOk
	endc

		clr.l	vd_Active(a4)		; Stop sound
		move.l	#SFX_ZeroData,vd_StartPtr(a4)
		move.l	#SFX_ZeroData+SFX__BuffSize,vd_EndPtr(a4)

.BOk		move.l	vd_StartPtr(a3),a3	; Get startaddress A
		move.l	vd_StartPtr(a4),a4	; Get startaddress B

	ifeq SFX__FastMode
		move.l	#SFX__BuffSize-1,d5
	else
		move.l	#(SFX__BuffSize/2)-1,d5
	endc
.MixLoop

	ifeq SFX__FastMode
		move.b	(a3)+,d4		; Get sample 1
		add.b	(a4)+,d4		; Add sample 2
		move.b	d4,(a1)+		; Store value in buffer.
	else
		move.b	(a3)+,(a1)+		; Get sample 1
		move.b	(a4)+,(a1)+		; Get sample 2
	endc

		dbf	d5,.MixLoop

	ifne SFX__Debug
		move.w	#0,$dff180
	endc
		rts

;------------------------------------------------------------------------------

	SECTION	MiscData,DATA

SFX_ZeroData	dcb.b	SFX__BuffSize,0
	EVEN

SFX_PartitionA	dcb.b	vd_sizeof,0
SFX_PartitionB	dcb.b	vd_sizeof,0

	ifeq SFX__Mode-1

SFX_AudIntReq	dc.l	0,0			; SUCC, PRED
		dc.b	2			; NT_INTERRUPT
		dc.b	0
		dc.l	.IntName

		dc.l	SFX_AudioBuff
		dc.l	SFX_AudInt,0
.IntName	dc.b	"SoundFX audioint",0
	EVEN
SFX_PrevAudInt	dc.l	0

	endc

SFX_PlayRate	dc.w	0
SFX_Volume	dc.w	64
SFX_IfPlay	dc.b	0
SFX_WhichBuffer	dc.b	0


	SECTION ChipData,DATA_C

SFX_AudioBuff	ds.b	SFX__BuffSize*2		; Two buffers

	SECTION Samples,DATA

; Your samples. Locate in fast if possible!

	INCDIR	"!Sources:Utility/"

Sample1	INCBIN	SAMPLE1
	dcb.b	64,0
Sample1End

Sample2	INCBIN	SAMPLE2
	dcb.b	416,0
Sample2End

	END

