;Struct used in sound for Infantry 401
WaveHdr STRUC
format db 'RIFF'
filelen dd ?
wavefmt db 'WAVEfmt_'
fmt_len dd ?
fmt_tag dw ?
channel dw ?
samples dd ?
bytesps dd ?
bkalign dw ?
bitsps dw ?
data db 'data'
datalen dd ?
WaveHdr Ends
;Sound segments used for Infantry 401
;====== MIDI Segment ==================================
MIDISeg segment public
midibuffer db 65535 dup(?)
MIDISeg ends
;======= WAV Segment===================================_
SNDSEG SEGMENT
sndbuf db 2048 dup (10,20,50,80,90,80,50,20) ; 16k of sine wave
SNDSEG ENDS
;Sound Constants used for midi
Gremlins db 'kashmirx.xmi',0
PUBLIC Gremlins
Psycho db 'Psychox.xmi', 0
public Psycho
moby db 'mobyx.xmi', 0
public moby
mission db 'Misionx.xmi', 0
public mission
kashmir db 'Kashmirx.xmi', 0
public kashmir
puppet db 'Puppetsx.xmi', 0
Public puppet
MIDISize dd 02
PUBLIC MIDISize
; Sound Constants used for wav
sbuf_page db ?
sbuf_offset dw ?
sbuf_length dw 8192 ; 8 k - all that is guaranteed to not
; cross a DMA Page.
Pbuf db 7 dup (?)
finalbuffersize dw ?
mycounter dw -1
needrefill dw 0
loadbuf WaveHdr < >
GunShot db 'gun.wav',0 ; null terminated file names
Grenade db 'bomb.wav',0
Missle db 'missle.wav',0
Dies db 'dies.wav',0
IntroSound db 'intro.wav',0
ExitSound db 'exit.wav',0
MenuSelected db 'menu.wav',0
Blockage db 'oof.wav',0
Pain db 'ouch.wav',0
powerup db 'pickup.wav',0
fileno dw ?
wavremaining dd 0
wavlength dd 0
wavdone db 0
;procedures for Sound Written by Ray O'Connell
; input dx= offset of midi file to play (filename null term.)
; output none
; purpose: procedure open, writes, and closes a .xmi file into midi
; segment for playing
;
LoadMIDI PROC NEAR
push ax
push bx ;save registers
push cx
push dx
push ds
mov ax, cs
mov ds, ax
mov ah, 03dh
mov al, 0
int 21h ;open file call
jc erroropenMIDI
mov bx, ax
mov ah, 03fh
mov cx, 25000
mov dx, seg midibuffer
mov ds, dx
mov dx, 0
int 21h ;write file call
jc errorreadMIDI
mov cx, cs
mov ds, cx
mov MIDISize, eax
mov ah, 03eh
int 21h ;close file
jc errorcloseMIDI
jmp endmidiload
;extensive error checking follows
erroropenMIDI: ;handle open errors
TMODE
cmp ax, 2
jne notFNFmidi
mov dx, offset FileNotFound ;testing ax for error code
call DSPMSG ;and calling DSPMSG then exiting
call dosXit
notFNFmidi:
cmp ax, 4
jne notTMOFmidi
mov dx, offset TooMany
call DSPMSG
call dosxit
notTMOFmidi:
cmp ax, 5
jne notADOmidi
mov dx, offset DeniedAccessOpen
call DSPMSG
call dosXit
notADOmidi:
mov dx, offset InvalidAccess
call DSPMSG
call dosXit
errorreadMIDI: ;handling read errors
TMODE
cmp ax, 5 ;testing ax for error code
jne notDARmidi
mov dx, offset DeniedAccessRead
call DSPMSG ;and calling DSPMSG then exiting
call dosxit
notDARmidi:
mov dx, offset InvalidHandleRead
call DSPMSG
call dosxit
errorcloseMIDI:
TMODE ;handling close errors
mov dx, offset InvalidHandleCl
call DSPMSG ;and calling DSPMSG then exiting
call dosxit
endmidiload:
pop ds
pop dx
pop cx ;restore registers
pop bx
pop ax
RET
LoadMIDI ENDP
;Written by Ray A. O'Connell
; Input dx= offset of midi file to register with midpak
; output none (music file registered )
; purpose register file with midpak to play
RegisterMIDI PROC NEAR
push ax
push bx
push cx ;save used registers
push dx
push ds
mov bx, cs
mov ds, bx
mov bx, offset midibuffer
mov eax, MIDISize
mov di, ax ;setup so midpak knows length
mov cl, 16
shr eax, cl
mov si, ax
mov cx, seg midibuffer
mov ax, 0704h
int 66h ;interrupt call
cmp ax, 0
jne endreg
call dosxit ;exit if error
endreg:
pop ds
pop dx
pop cx ;restore registers
pop bx
pop ax
RET
RegisterMIDI ENDP
;Written by Ray A. O'Connell
;input- dx offset of midi variable
;output- none(no variables nor registers)
;purpose self explainatory
PlayMIDI PROC NEAR
push ax
push bx
mov ax, 0702h
mov bx, 0 ;setup call to midpak
int 66h
cmp ax, 0
jne endplay
call dosxit
endplay:
pop bx
pop ax
RET
PlayMIDI ENDP
;Written by Ray A. O'Connell
;input- none
;output- none
;purpose if pause, unpause the midi
ResumeMIDI PROC NEAR
push ax
mov ax, 070bh
int 66h
pop ax
RET
ResumeMIDI ENDP
;Written by Ray A. O'Connell
;input -None
;output -none
;purpose stop or at least pause the midi
StopMIDI PROC NEAR
push ax
mov ax, 0705h
int 66h
pop ax
RET
StopMIDI ENDP
;Written by Ray A. O'Connell
; This one combines three previous ones
; Now, instead of calling loadMIDI, RegisterMIDI, and PlayMIDI
; Just call GoMIDI
; Input: dx = offset of midi variable to play
; example mov dx, offset Kashmir
; output: midi music
; If playing one file, you can start another song by just calling
; GoMIDI with another file, it will just switch songs
;Written by Ray O'Connell
GoMIDI PROC NEAR
push dx
call LoadMIDI
call RegisterMIDI
call PlayMIDI
pop dx
RET
GoMIDI ENDP
;Written by Ray A. O'Connell
;; next one is LoopMIDI
; This function plays the song again when it ends. We probably
; won't need this in our demo but if someone is playing the
; game longer, this function will repeat the current song
; It needs to be called at some point on a regular basis
; (some sort of loop) since
; checks the status of MIDPAK to see if the song is finished
; if it is, it starts the song again
; I would suggest trying to find a loop that doesn't go too fast
; otherwise we would be wasting time with the proccessor trying
; every so often if the song finished
;
;Written by Ray O'Connell
LoopMIDI PROC NEAR
push ax
push dx
mov ax, 070ch
int 66h
cmp ax, 2
jne goons
call PlayMIDI
goons:
pop dx
pop ax
RET
LoopMIDI ENDP
;Written by Ray A. O'Connell
;sound procedures for WAV playing
;this is the interrupt function set as call back by WAVPlay
;which controls the loading of buffers and switching of modes
;when reaching the last buffer (based on Snd Lib 291)
SoundHandler proc far uses ax ds dx bx es di
mov ax, cs
mov ds, ax
inc mycounter ; so outside program can know where DMA is.
inc needrefill
mov ax, mycounter
and ax, 1
call LoadHalfBuffer ; fills buffer half ax (0 or 1)
dec needrefill
cmp wavdone, 1 ; one for current buffer, one for last
jne shdone
mov cx, finalbuffersize
call SB_SingleCycle ; last buffer read, so switch to SC
call SB_Stop
shdone:
ret
SoundHandler endp
;Written by Ray A. O'Connell
; Input: dx = offset of file to play (filename, null terminated)
; Output: eax = data length = bytes of Sound Data.
; (based on Sound Library 291)
OpenWav proc near
call rsave
mov ax, 03D00h
int 21h ; open file
jc wavfail
mov fileno, ax
mov ah, 03Fh
mov bx, fileno
mov cx, sizeof WaveHdr
mov dx, offset loadbuf
int 21h ; read the header into loadbuf
mov bx, dx
cmp byte ptr [bx+3] , 'd'
mov eax, loadbuf.datalen
mov wavlength, eax
mov wavremaining, eax
wavfail:
call rrest
ret
OpenWav endp
;Written by Ray A. O'Connell
; LoadHalfBuffer takes a 1 or 0 in ax, and reads from the file into the
; appropriate half of sndbuf.
; Input ax = secondhalf
; Output: CF = 1 if just loaded last buffer
; ax = number of bytes read
; (as is from Sound Library 291)
LoadHalfBuffer proc near uses bx cx dx es di
xor bx, bx
mov bh, sbuf_page
shl bh, 4 ; segment
mov es, bx
mov bx, sbuf_offset
;add bx, 048 ; FIXME
cmp ax, 0
je LoadingFirstBuffer
mov ax, sbuf_length
shr ax, 1
add bx, ax ; half buffer in, or not
LoadingFirstBuffer:
mov di, bx
push ds
mov bx, fileno
mov cx, sbuf_length
shr cx, 1 ; half buffer length
xor eax, eax
mov ah, 3Fh
mov dx, es
mov ds, dx
mov dx, di
int 021h ; read from file
pop ds
;cmp ax, cx ; did i read a full buffer?
cmp wavremaining, 0 ; Is there any more to be read?
jg wavenotdone
mov finalbuffersize, ax ; if not, it's the last buffer.
inc wavdone
WaveNotDone:
sub wavremaining, eax
ret
LoadHalfBuffer endp
;Written by Ray A. O'Connell
;calls SB_Play with single cycle mode
; was originally in WAVPlay but was moved out as seperate proc
; input none
; output sound
SingleCycle PROC NEAR
mov ecx, wavlength
mov edx, wavlength
mov ax, sbuf_offset
mov bh, 0 ; single cycle
mov bl, sbuf_page
call SB_Play ; just play one cycle.
mov wavdone, 2
RET
SingleCycle ENDP
;Written by Ray A. O'Connell
;function called when sound needed for modularity
; inputs dx = offset of sound to play (null terminated filename)
; ouputs sound!
;calls OpenWav, Single Cycle, LoadHalfBuffer and
;functions from SBLib291
WAVPlay PROC NEAR
call rsave
mov ax, cseg
mov ds, ax
call SB_Clean
mov wavdone, 0
; find all on one page 32k of 64k buffer
xor eax, eax
mov ax, seg sndbuf ; put linear address of buffer into eax
mov bx, offset sndbuf
shl eax, 4
add eax, offset sndbuf
push eax
dec ax
add ax, sbuf_length ; check if moving to end of buffer
pop eax ; crosses to next page.
jnc NotCrossPageBoundary
xor ebx, ebx
mov bx, sbuf_length
add eax, ebx ; new bottom of buffer
NotCrossPageBoundary:
mov sbuf_offset, ax ; offset composed of the lower 16 bits
shr eax, 8
mov sbuf_page, ah ; and page of the upper 4 bits as LSBs.
; set up and run the DMA transfer.
call SB_Init
jc failedinstall
mov ax, seg SoundHandler
mov es, ax
mov bx, offset SoundHandler
call SB_SetCallback
call OpenWav ; open file
mov ax, 0 ; load first half buffer
call LoadHalfBuffer
cmp wavdone, 1
je singlecycle1buf ; SC if it fits in one half buffer..
mov ax, 1 ; load second half buffer
call LoadHalfBuffer
cmp wavdone, 1
je singlecycle1buf ; or in the first and second halves..
mov ax, sbuf_offset
mov bh, 1 ; AutoInit
mov bl, sbuf_page
mov cx, sbuf_length ; Set DMA to traverse full buffer
mov dx, sbuf_length
shr dx, 1 ; Have DSP read HALF buffer before INT
call SB_Play
; Here's the hard part. We want to loop, waiting for the interrupt
; to trigger and tell us to fill the half of the buffer that the DMA
; had just read. And if while we were waiting the sound finished,
; we also want to exit the loop. And most importantly, if we are on
; the last (less than or equal to a) buffer worth of data, we want
; to switch the DSP to Single Cycle so it will stop playing at the
; end of our wav.
jmp DonePlaying
singlecycle1buf:
call SingleCycle
DonePlaying:
failedinstall:
call rrest
RET
WAVPlay ENDP
;Written by Ray A. O'Connell