
	IDEAL
	P386
	JUMPS
	MODEL FLAT,C

CHANNELS	equ	4	; Number of physical channels.

	DATASEG

FRACTION        dd      ?       ; Fixed decimal value used for freq shift
SOUNDEND	dd	?	; Point's to end of sound sample.

	ends

        include "prologue.mac"
	CODESEG

	public	BuildModStream

	public	ModSilence
	public	MergeMod1
	public	MergeMod2
	public	MergeMod3
	public	MergeMod4

Struc	MODSPEC

sound	dd	?	; beginning address of sound.
sndloc	dd	?	; current sound location.
sndlen  dd      ?       ; Length of this mod sample.
silence dd	?	; Amount of silence, between samples.
silout	dd	?	; Number of silence samples remaining from before.
freq	dw	?	; fixed decimal frequency modulator.
oneshot db	?	; True if sample is one shot.
trigger db	?	; True if sample is to be triggered.
buff	dd	?	; address of buffer stream being built.
bufflen dd	?	; Length of output buffer.
volume	dw	?	; Volume of mod.
lastvol dw	?	; Last volume of mod.
voltab	dw	256 dup(?)	; Address of precomputed volume table.

	Ends

Macro	useall
	uses	ebx,ecx,edx,esi,edi,es,ds
	endm

Proc	C   BuildModStream	near
	ARG	MODS:DWORD
	useall

	mov	esi,[MODS]		; Get address of MOD.
	xor	ebx,ebx 		; Zero out entire EBX register.
	mov	bx,[ds:(MODSPEC esi).volume]	 ; Get current volume
	cmp	bx,[ds:(MODSPEC esi).lastvol]	 ; Same as last volume?
	je	@@VOK			; yes, volume table Ok!
	mov	[ds:(MODSPEC esi).lastvol],bx ; Update, new last volume.
	lea	edi,[ds:(MODSPEC esi).voltab]  ; Get address of volume table.
	mov	ecx,-128	 ; from -128
	ALIGN	2
@@GO:	mov	eax,ecx 	; get the current scale value.
	mul	ebx
	sar	eax,(8-4)	    ; ok have signed 'word'.
	mov	[edi],ax
	add	edi,2
	inc	ecx
	cmp	ecx,128
	jl	@@GO
@@VOK:


	mov	esi,[MODS]	  ; Get address of MOD.
	push	ebp
	mov	ebp,esi 	; Have to use EBP as index register.
	mov	esi,[ds:(MODSPEC ebp).sound]   ; Get base sound location.
	add	esi,[ds:(MODSPEC ebp).sndlen]  ; Ending mod location.
	mov	[SOUNDEND],esi		; Save ending location.


	mov	esi,[ds:(MODSPEC ebp).sndloc]  ; Get current sound location.
	mov	edi,[ds:(MODSPEC ebp).buff]    ; Buffer being built.
	mov	ecx,[ds:(MODSPEC ebp).bufflen] ; Length of output buffer stream.
	xor	ebx,ebx 		; Zero out all 32 bits of EBX
	cmp	[ds:(MODSPEC ebp).trigger],0  ; Channel active?
	jne	@@SCHK		; Process channel of sound....
	mov	eax,0
	ALIGN	2
@@SND:	mov	[edi],ax	      ;; Build null buffer.
	add	edi,CHANNELS*2		 ;; skip to next entry.
	dec	ecx
	jnz	@@SND
	jmp	@@don
@@SCHK:
	mov	edx,[ds:(MODSPEC ebp).silout]  ; Silence remaining from last time?
	or	edx,edx 	  ; Any silence remainging to be sent?
	jz	@@SDON		; no, send data as one to one.
	mov	eax,0
	ALIGN	2
@@SIL:	mov	[edi],ax
	add	di,CHANNELS*2
	dec	edx		 ; Decrement silence remaining.
	jz	@@SDON
	dec	ecx
	jnz	@@SIL
	mov	[ds:(MODSPEC ebp).silout],edx ; Silence remaining.
	jmp	@@don
@@SDON:
	mov	[ds:(MODSPEC ebp).silout],edx	; finished.
	mov	dx,[ds:(MODSPEC ebp).freq]    ;

	push	ebp

	lea	ebp,[ds:(MODSPEC ebp).voltab]	   ; Get address of volume table.
	cmp	dx,256			; 1:1 frequency transposition?
	jb	@@DOWNIT
	ja	@@UPIT
	xor	eax,eax 	; Zero out all of EAX
	xor	ebx,ebx 	; Zero out EBX.
	ALIGN	2
@@ONE:	mov	bl,[esi]	      ; Get source byte.
	inc	esi			 ; Advance source index.
	mov	ax,[ebp+ebx*2]	     ; Translate against volume table.
	mov	[edi],ax	      ; Store result.
	add	edi,CHANNELS*2		 ; Advance
	cmp	esi,[SOUNDEND]	 ; Past end of mod?
	je	@@WRAP0 ; no, keep going.
	dec	ecx
	jnz	@@ONE
	pop	ebp		; Restore EBP
	jmp	@@don
@@WRAP0:
	pop	ebp		; Restore address of sound structure.
	mov	esi,[ds:(MODSPEC ebp).sound]	 ; Back to beginning.
	mov	eax,[ds:(MODSPEC ebp).silence]
	mov	[ds:(MODSPEC ebp).silout],eax
	cmp	[ds:(MODSPEC ebp).oneshot],1
	jne	@@SCHK
	mov	[ds:(MODSPEC ebp).trigger],0
	jmp	@@don

@@UPIT: xor	eax,eax
	mov	al,dh		; integer portion.
	mov	[FRACTION],eax		      ; Self modify this code.
	xor	dh,dh		; Zero integer portion.
	xor	ebx,ebx
	ALIGN	2
@@cnt2:
	mov	bl,[esi]	   ; Get sample.
	mov	ax,[ebp+ebx*2]	       ; translate against volume.
	mov	[edi],ax
	add	edi,CHANNELS*2
	add	dh,dl
	adc	esi,[FRACTION]
	cmp	esi,[SOUNDEND]	 ; Past end of mod?
	jae	@@WRAP0 ; no, keep going.
	dec	ecx
	jnz	@@cnt2
	pop	ebp
	jmp short @@don
@@DOWNIT:
	xor	dh,dh	; Inverse sample
	xor	ebx,ebx
	ALIGN	2
@@cnt:	mov	bl,[esi]      ;lodsb	       ; Get sample.
	mov	ax,[ebp+ebx*2] ; Translate against volume table.
	mov	[edi],ax
	add	edi,CHANNELS*2
	adc	dh,dl		; Add fractional amount in.
	adc	esi,0		; Back off source address if didn't underflow.
	cmp	esi,[SOUNDEND]	 ; Past end of mod?
	je	@@WRAP0 ; no, keep going.
	dec	ecx
	jnz	@@cnt
	pop	ebp
@@don:
	mov	[ds:(MODSPEC ebp).sndloc],esi
	pop	ebp

	ret
	endp

Macro	GenProc loop
	mov	esi,[MOD1]
	mov	edi,[DEST]
	mov	ecx,[ds:(MODSPEC esi).bufflen]
	mov	esi,[ds:(MODSPEC esi).buff]

	xor	ax,ax
	mov	dx,ax

@@OK:	mov	ax,[esi]      ; Get all 2 channels of data.
 ri = 2
 rept loop-1
	add	ax,[esi+ri]
  ri = ri + 2
 endm
	add	esi,CHANNELS*2	 ; Advance source.
	AdjustByte
	mov	[edi],al      ; Store result.
	inc	edi
	dec	ecx
	jnz	@@OK
	endm


;; There is a signed word in AX, we need to adjust it down to an
;; unsigned byte 0-255.
Macro	AdjustByte
	LOCAL	@@OVER,@@UNDER
	sar	ax,4
	add	ax,128		; Get it into 0-255.
	jns	@@OVER		; It's positive.
	xor	ax,ax
@@OVER: or	ah,ah		; high byte zero?
	jz	@@UNDER
	mov	ax,255		; Max it.
@@UNDER:
	endm

Proc    C   ModSilence near
	ARG	MOD1:DWORD,DEST:DWORD
	useall

	mov	esi,[MOD1]
	mov	edi,[DEST]
	mov	ecx,[ds:(MODSPEC esi).bufflen]
	shr	ecx,2		; /4
	mov	eax,80808080h
	rep	stosd

	ret
	endp


Proc	C	MergeMod1	near
	ARG	MOD1:DWORD,DEST:DWORD
	useall
	GenProc 1
	ret
	endp

Proc	C	MergeMod2	near
	ARG	MOD1:DWORD,DEST:DWORD
	useall
	GenProc 2
	ret
	endp

Proc	C	MergeMod3	near
	ARG	MOD1:DWORD,DEST:DWORD
	useall
	GenProc 3
	ret
	endp

Proc	C	MergeMod4	near
	ARG	MOD1:DWORD,DEST:DWORD
	useall
	GenProc 4
	ret
	endp


	ENDS
	END

