kolibrios/programs/develop/libraries/ufmod/ufmod.asm
Yogev Ezra d21a6e85e7 Added libraries/ufmod source code.
git-svn-id: svn://kolibrios.org@1845 a494cfbc-eb01-0410-851d-a64ba20cac60
2011-02-04 20:13:33 +00:00

1105 lines
23 KiB
NASM

; UFMOD.ASM
; ---------
; uFMOD public source code release. Provided as-is.
SOUND_VERSION equ 100h ; required Infinity sound driver version
FSOUND_Block equ 10
FSOUND_BlockSize equ 1024 ; 1 << FSOUND_Block
if DEBUG
; Debug messages:
sDBGMSG1 db "uFMOD: XM track loaded",13,10,0
sDBGMSG2 db "uFMOD: Infinity driver loaded",13,10,0
sDBGMSG3 db "uFMOD: Buffer created",13,10,0
sDBGMSG4 db "uFMOD: Sound buffer destroyed",13,10,0
sDBGMSG5 db "uFMOD: Infinity version: ",0
; DEBUG board: print a string.
DBG_print_s:
; EDX = msg (trailing 0 is required!)
pushad
DBG_print_s_loop:
mov eax,63
mov ebx,1
mov cl,[edx]
test cl,cl
jz DBG_print_s_R
int 40h
inc edx
jmp DBG_print_s_loop
DBG_print_s_R:
popad
ret
; DEBUG board: print the hex value in EAX.
DBG_print_x:
; EAX = val
pushad
mov esi,eax
mov edx,OFFSET MixBuf
mov ecx,7
mov DWORD PTR [edx+8],0A0Dh
print_eax_loop:
mov eax,esi
and al,0Fh
cmp al,10
sbb al,69h
das
mov [edx+ecx],al
shr esi,4
dec ecx
jns print_eax_loop
call DBG_print_s
popad
ret
endif ; DEBUG
if RAMP_STRONG
volumerampsteps equ 128
volumeramps_pow equ 7
endif
if RAMP_WEAK
volumerampsteps equ 16
volumeramps_pow equ 4
endif
if RAMP_NONE
volumerampsteps equ 64
volumeramps_pow equ 6
endif
XM_MEMORY equ 1
XM_FILE equ 2
XM_NOLOOP equ 8
XM_SUSPENDED equ 16
FMUSIC_ENVELOPE_SUSTAIN equ 2
FMUSIC_ENVELOPE_LOOP equ 4
FMUSIC_FREQ equ 1
FMUSIC_VOLUME equ 2
FMUSIC_PAN equ 4
FMUSIC_TRIGGER equ 8
FMUSIC_VOLUME_OR_FREQ equ 3
FMUSIC_VOLUME_OR_PAN equ 6
FMUSIC_VOL_OR_FREQ_OR_TR equ 11
FMUSIC_FREQ_OR_TRIGGER equ 9
NOT_FMUSIC_TRIGGER equ 0F7h
NOT_FMUSIC_TRIGGER_OR_FRQ equ 0F6h
; FMUSIC_XMCOMMANDS enum:
FMUSIC_XM_PORTAUP equ 1
FMUSIC_XM_PORTADOWN equ 2
FMUSIC_XM_PORTATO equ 3
FMUSIC_XM_VIBRATO equ 4
FMUSIC_XM_PORTATOVOLSLIDE equ 5
FMUSIC_XM_VIBRATOVOLSLIDE equ 6
FMUSIC_XM_TREMOLO equ 7
FMUSIC_XM_SETPANPOSITION equ 8
FMUSIC_XM_SETSAMPLEOFFSET equ 9
FMUSIC_XM_VOLUMESLIDE equ 10
FMUSIC_XM_PATTERNJUMP equ 11
FMUSIC_XM_SETVOLUME equ 12
FMUSIC_XM_PATTERNBREAK equ 13
FMUSIC_XM_SPECIAL equ 14
FMUSIC_XM_SETSPEED equ 15
FMUSIC_XM_SETGLOBALVOLUME equ 16
FMUSIC_XM_GLOBALVOLSLIDE equ 17
FMUSIC_XM_KEYOFF equ 20
FMUSIC_XM_PANSLIDE equ 25
FMUSIC_XM_MULTIRETRIG equ 27
FMUSIC_XM_TREMOR equ 29
FMUSIC_XM_EXTRAFINEPORTA equ 33
; FMUSIC_XMCOMMANDSSPECIAL enum:
FMUSIC_XM_FINEPORTAUP equ 1
FMUSIC_XM_FINEPORTADOWN equ 2
FMUSIC_XM_SETGLISSANDO equ 3
FMUSIC_XM_SETVIBRATOWAVE equ 4
FMUSIC_XM_SETFINETUNE equ 5
FMUSIC_XM_PATTERNLOOP equ 6
FMUSIC_XM_SETTREMOLOWAVE equ 7
FMUSIC_XM_SETPANPOSITION16 equ 8
FMUSIC_XM_RETRIG equ 9
FMUSIC_XM_NOTECUT equ 12
FMUSIC_XM_NOTEDELAY equ 13
FMUSIC_XM_PATTERNDELAY equ 14
if AC97SND_ON
file_read:
; buf in EAX
; size in EDX
push ebx
push esi
push edi
push ebp
xchg eax,edi
file_read_begin:
test edx,edx
jg file_read_chk_cache
file_read_done:
pop ebp
pop edi
pop esi
pop ebx
ret
; *** CHECK IN THE CACHE
file_read_chk_cache:
mov ebp,OFFSET file_struct
mov esi,[ebp-24]
sub esi,[ebp+28] ; cache_offset
js file_read_cache_done
mov ecx,8192
sub ecx,esi
jle file_read_cache_done
add esi,OFFSET MixBuf
sub edx,ecx
jns file_read_do_cache
add ecx,edx
file_read_do_cache:
add [ebp-24],ecx
rep movsb
test edx,edx
jle file_read_done ; data read from the cache (no need to access the FS)
file_read_cache_done:
; *** FS BATCH READ OPERATION
mov eax,[ebp-24]
mov ecx,edx
add ecx,eax
and ecx,0FFFFE000h
sub ecx,eax
jle file_read_fs_done ; Too few data requested for a FS batch operation
sub edx,ecx
mov [ebp+4],eax ; file offset
mov [ebp+12],ecx ; NumberOfBytesToRead
mov [ebp+16],edi ; lpBuffer
mov ebx,ebp
add edi,ecx
push 70
add [ebp-24],ecx
pop eax
int 40h
file_read_fs_done:
; *** UPDATE THE CACHE
mov ecx,[ebp-24]
and ecx,0FFFFE000h
mov [ebp+4],ecx ; file offset
mov [ebp+28],ecx ; cache_offset
mov DWORD PTR [ebp+12],8192 ; NumberOfBytesToRead
mov DWORD PTR [ebp+16],OFFSET MixBuf ; lpBuffer
mov ebx,ebp
push 70
pop eax
int 40h
jmp file_read_begin
if INFO_API_ON
PUBLIC _uFMOD_GetTitle
_uFMOD_GetTitle:
mov eax,OFFSET szTtl
ret
endif
else
uFMOD_mem dd mem_open, mem_read
if XM_FILE_ON
uFMOD_fs dd file_open, file_read
endif
szInfinity db "INFINITY",0
if JUMP_TO_PAT_ON
; Jump to the given pattern
PUBLIC _uFMOD_Jump2Pattern
_uFMOD_Jump2Pattern:
mov eax,[esp+4]
mov ecx,OFFSET _mod+36
movzx eax,ax
and DWORD PTR [ecx+FMUSIC_MODULE.nextrow-36],0
cmp ax,[ecx+FMUSIC_MODULE.numorders-36]
sbb edx,edx
and eax,edx
mov [ecx+FMUSIC_MODULE.nextorder-36],eax
ret
endif
if VOL_CONTROL_ON
; Set global volume [0: silence, 25: max. volume]
vol_scale dw 1 ; -45 dB
dw 130 ; -24
dw 164 ; -23
dw 207 ; -22
dw 260 ; -21
dw 328 ; -20
dw 413 ; -19
dw 519 ; -18
dw 654 ; -17
dw 823 ; -16
dw 1036 ; -15
dw 1305 ; -14
dw 1642 ; -13
dw 2068 ; -12
dw 2603 ; -11
dw 3277 ; -10
dw 4125 ; -9
dw 5193 ; -8
dw 6538 ; -7
dw 8231 ; -6
dw 10362 ; -5
dw 13045 ; -4
dw 16423 ; -3
dw 20675 ; -2
dw 26029 ; -1
dw 32768 ; 0 dB
PUBLIC _uFMOD_SetVolume
_uFMOD_SetVolume:
mov eax,[esp+4]
cmp eax,25
jna get_vol_scale
push 25
pop eax
get_vol_scale:
mov ax,[vol_scale+eax*2]
mov [ufmod_vol],eax
ret
endif
if PAUSE_RESUME_ON
; Pause the currently playing song.
PUBLIC _uFMOD_Pause
_uFMOD_Pause:
mov al,1
jmp $+4
; Resume the currently paused song.
PUBLIC _uFMOD_Resume
_uFMOD_Resume:
xor eax,eax
mov BYTE PTR [ufmod_pause_],al
ret
endif
if INFO_API_ON
; Return currently playing signal stats:
; lo word : RMS volume in R channel
; hi word : RMS volume in L channel
PUBLIC _uFMOD_GetStats
_uFMOD_GetStats:
push 4
jmp _uFMOD_InfoApi
; Return currently playing row and order:
; lo word : row
; hi word : order
PUBLIC _uFMOD_GetRowOrder
_uFMOD_GetRowOrder:
push 8
jmp _uFMOD_InfoApi
; Return the time in milliseconds since the song was started.
PUBLIC _uFMOD_GetTime
_uFMOD_GetTime:
push 0
_uFMOD_InfoApi:
pop edx
mov eax,[time_ms+edx]
ret
endif
; ***********************
; * XM_MEMORY CALLBACKS *
; ***********************
mem_read:
; buf in EAX
; size in EDX
push edi
push esi
xchg eax,edi ; buf
mov esi,OFFSET mmf
lodsd
mov ecx,edx
add edx,[esi]
cmp edx,eax
jl copy
sub eax,[esi]
xchg eax,ecx
copy:
test ecx,ecx
jle mem_read_R
lodsd
add eax,[esi]
mov [esi-4],edx
mem_do_copy:
mov dl,[eax]
mov [edi],dl
inc eax
inc edi
dec ecx
jnz mem_do_copy
mem_read_R:
pop esi
pop edi
if INFO_API_ON
PUBLIC _uFMOD_GetTitle
_uFMOD_GetTitle:
mov eax,OFFSET szTtl
endif
mem_open:
ret
; *********************
; * XM_FILE CALLBACKS *
; *********************
if XM_FILE_ON
file_open:
; pszName in ESI
; Prepare the FILE struct for subsecuent I/O:
lea eax,[ebp+8] ; file_struct
xor edx,edx
mov [eax],edx ; +0 subfunction: 0 = read
mov [eax+8],edx ; +8 Reserved
; +12 NumberOfBytesToRead
; +16 lpBuffer
push -1
push 1
mov [eax+20],dl ; +20 db 0
mov [eax+21],esi ; +21 lpFileName
pop DWORD PTR [eax+28] ; cache_offset
pop DWORD PTR [eax-28] ; [mmf] = maximum size
ret
file_read:
; buf in EAX
; size in EDX
push ebx
push esi
push edi
push ebp
xchg eax,edi
file_read_begin:
test edx,edx
jg file_read_chk_cache
file_read_done:
pop ebp
pop edi
pop esi
pop ebx
ret
; *** CHECK IN THE CACHE
file_read_chk_cache:
mov ebp,OFFSET file_struct
mov esi,[ebp-24]
sub esi,[ebp+28] ; cache_offset
js file_read_cache_done
mov ecx,8192
sub ecx,esi
jle file_read_cache_done
add esi,OFFSET MixBuf
sub edx,ecx
jns file_read_do_cache
add ecx,edx
file_read_do_cache:
add [ebp-24],ecx
rep movsb
test edx,edx
jle file_read_done ; data read from the cache (no need to access the FS)
file_read_cache_done:
; *** FS BATCH READ OPERATION
mov eax,[ebp-24]
mov ecx,edx
add ecx,eax
and ecx,0FFFFE000h
sub ecx,eax
jle file_read_fs_done ; Too few data requested for a FS batch operation
sub edx,ecx
mov [ebp+4],eax ; file offset
mov [ebp+12],ecx ; NumberOfBytesToRead
mov [ebp+16],edi ; lpBuffer
mov ebx,ebp
add edi,ecx
push 70
add [ebp-24],ecx
pop eax
int 40h
file_read_fs_done:
; *** UPDATE THE CACHE
mov ecx,[ebp-24]
and ecx,0FFFFE000h
mov [ebp+4],ecx ; file offset
mov [ebp+28],ecx ; cache_offset
mov DWORD PTR [ebp+12],8192 ; NumberOfBytesToRead
mov DWORD PTR [ebp+16],OFFSET MixBuf ; lpBuffer
mov ebx,ebp
push 70
pop eax
int 40h
jmp file_read_begin
endif
endif ; AC97SND_ON = 0
uFMOD_lseek:
; pos in EAX
; org in ECX
; !org in Z
mov edx,OFFSET mmf+4
jz mem_ok
add eax,[edx]
mem_ok:
test eax,eax
js mem_seek_R
cmp eax,[edx-4]
ja mem_seek_R
mov [edx],eax
mem_seek_R:
ret
; Dynamic memory allocation
alloc:
; EAX: how many bytes to allocate
add eax,3
and eax,-4
jle alloc_error2
mov ecx,OFFSET ufmod_heap
alloc_lookup:
cmp DWORD PTR [ecx],0
je do_alloc
mov ecx,[ecx]
cmp [ecx+4],eax
jl alloc_lookup
sub [ecx+4],eax
mov eax,[ecx+4]
lea eax,[eax+ecx+8]
ret
do_alloc:
add eax,8
push ebx
push edi
mov ebx,eax
add ebx,8191
neg eax
and ebx,-8192
push ecx
add eax,ebx
xchg eax,edi
push 12
push 68
mov ecx,ebx
pop eax
pop ebx
int 40h
; Test for error condition
test eax,eax
pop ebx
mov edx,edi ; free space
jz alloc_error1
mov [ebx],eax
mov edi,eax
lea eax,[eax+edx+8]
mov [edi+4],edx
pop edi
pop ebx
ret
alloc_error1:
pop edi
pop ebx
alloc_error2:
xor eax,eax
pop edx ; EIP
pop ebx
leave
_alloc_R:
ret
; Starts playing a song.
PUBLIC _uFMOD_LoadSong
_uFMOD_LoadSong:
; *** FREE PREVIOUS TRACK, IF ANY
call _uFMOD_StopSong
if AC97SND_ON
mov ecx,[esp+4]
push ebx
push esi
push edi
push ebp
mov ebp,OFFSET uFMOD_fopen
; Prepare the FILE struct for subsecuent I/O:
lea eax,[ebp+8] ; file_struct
xor edx,edx
mov [eax],edx ; +0 subfunction: 0 = read
mov [eax+8],edx ; +8 Reserved
; +12 NumberOfBytesToRead
; +16 lpBuffer
mov [eax+20],dl ; +20 db 0
mov [eax+21],ecx ; +21 lpFileName
push -1
push 1
mov DWORD PTR [ebp+4],OFFSET file_read ; uFMOD_fread
pop DWORD PTR [eax+28] ; cache_offset
mov [eax-24],edx
pop DWORD PTR [eax-28] ; [mmf] = maximum size
else
mov eax,[esp+8] ; param
mov ecx,[esp+12] ; fdwSong
mov edx,[esp+4] ; lpXM
test edx,edx
jz _alloc_R
; *** SET I/O CALLBACKS
push ebx
push esi
push edi
push ebp
mov ebp,OFFSET uFMOD_fopen
mov [ebp-20],eax ; mmf
xor eax,eax
mov [ebp-16],eax ; mmf+4
test cl,XM_MEMORY
mov esi,OFFSET uFMOD_mem
if XM_FILE_ON
jnz set_callbacks
test cl,XM_FILE
lea esi,[esi+(uFMOD_fs-uFMOD_mem)]
endif
jz goto_StopSong
set_callbacks:
if NOLOOP_ON
test cl,XM_NOLOOP
setnz [ebp-24] ; ufmod_noloop
endif
if PAUSE_RESUME_ON
and cl,XM_SUSPENDED
mov [ebp-23],cl ; ufmod_pause_
endif
mov edi,ebp ; uFMOD_fopen
movsd
movsd
mov esi,edx ; uFMOD_fopen:lpXM <= ESI
if VOL_CONTROL_ON
cmp [ebp-4],eax ; ufmod_vol
jne play_vol_ok
mov WORD PTR [ebp-4],32768
play_vol_ok:
endif
xor edi,edi
; *** INIT THE INFINITY DRIVER
lea eax,[edi+68]
lea ebx,[edi+16]
mov ecx,OFFSET szInfinity
int 40h
test eax,eax
mov [hSound],eax
jz goto_StopSong
if DEBUG
mov edx,OFFSET sDBGMSG2
call DBG_print_s
endif
; *** CHECK THE DRIVER VERSION
push edi ; ver = 0
push esp ; &ver
mov edx,esp
push 4 ; .out_size
push edx ; .output = &&ver
push edi ; .inp_size
push edi ; .input
push edi ; .code = SRV_GETVERSION
push eax ; .handle = [hSound]
lea ebx,[edi+17]
lea eax,[edi+68]
mov ecx,esp
int 40h
add esp,28
pop eax ; ver
if DEBUG
mov edx,OFFSET sDBGMSG5
call DBG_print_s
call DBG_print_x
endif
shr eax,16
cmp eax,SOUND_VERSION
ja _uFMOD_StopSong+4 ; obsolete program version (Hint: try adjusting SOUND_VERSION!)
; *** ALLOCATE A HEAP OBJECT
lea eax,[edi+68]
lea ebx,[edi+11]
int 40h
test eax,eax
jz goto_StopSong
; *** LOAD THE TRACK
mov [ebp-12],esi ; mmf+8 <= pMem
if XM_FILE_ON
call DWORD PTR [ebp] ; uFMOD_fopen
endif
endif ; AC97SND_ON = 0
call LoadXM
test eax,eax
goto_StopSong:
jz _uFMOD_StopSong+4
if DEBUG
mov edx,OFFSET sDBGMSG1
call DBG_print_s
endif
if AC97SND_ON
else
xor edi,edi
; *** CREATE THE PCM BUFFER
push edi ; size (default is 64Kb)
push PCM_format ; format: 16-bit / stereo / sampling rate
mov edx,esp
push OFFSET hBuff
mov eax,esp
push 4 ; .out_size
push eax ; .output = &&hBuff
push 8 ; .inp_size
push edx ; .input
push 1 ; .code = SND_CREATE_BUFF
push DWORD PTR [hSound] ; .handle
lea eax,[edi+68]
lea ebx,[edi+17]
mov ecx,esp
int 40h
pop esi ; <- hSound
add esp,32
test eax,eax
jnz _uFMOD_StopSong+4 ; buffer not created
if DEBUG
mov edx,OFFSET sDBGMSG3
call DBG_print_s
endif
xchg eax,esi ; return the driver handle
endif ; AC97SND_ON = 0
; *** ENABLE PCM OUTPUT
mov [SW_Exit],eax
pop ebp
pop edi
pop esi
pop ebx
ret
; Stop the currently playing song, if any, and free all resources allocated for that song.
PUBLIC _uFMOD_StopSong
_uFMOD_StopSong:
push ebx
push esi
push edi
push ebp
; _uFMOD_StopSong+4
xor edi,edi
mov ebp,OFFSET ufmod_heap
; *** DISABLE PCM OUTPUT
mov [ebp+16],edi ; SW_Exit
if AC97SND_ON
else
; *** STOP AND DESTROY THE PCM BUFFER
mov eax,[ebp+12] ; hBuff
test eax,eax
jz SND_buffer_free
push eax ; buffer
mov edx,esp
push edi ; .out_size
push edi ; .output
push 4 ; .inp_size
push edx ; .input
push 11 ; .code = SND_STOP
push DWORD PTR [ebp+8] ; .handle = [hSound]
lea eax,[edi+68]
lea ebx,[edi+17]
mov ecx,esp
int 40h
mov DWORD PTR [esp+4],2 ; .code = SND_DESTROY_BUFF
lea eax,[edi+68]
int 40h
add esp,28
if DEBUG
mov edx,OFFSET sDBGMSG4
call DBG_print_s
endif
SND_buffer_free:
mov [ebp+12],edi ; hBuff
endif ; AC97SND_ON = 0
; *** FREE THE HEAP
mov esi,[ebp] ; ufmod_heap
heapfree:
test esi,esi
jz free_R
mov ecx,esi
mov esi,[esi]
lea eax,[edi+68]
lea ebx,[edi+13]
int 40h
jmp heapfree
free_R:
xor eax,eax
if AC97SND_ON
else
if INFO_API_ON
; *** CLEAR THE INFO API DATA
lea ecx,[eax+4]
mov edi,OFFSET time_ms
rep stosd
endif
endif ; AC97SND_ON = 0
mov DWORD PTR [ebp],eax ; ufmod_heap
pop ebp
pop edi
pop esi
pop ebx
ret
PUBLIC _uFMOD_WaveOut
_uFMOD_WaveOut:
push edi
push ebp
xor edi,edi
if AC97SND_ON
; *** PCM OUTPUT ENABLED?
cmp DWORD PTR [SW_Exit],edi
lea eax,[edi+1] ; return error if output not enabled
je _uFMOD_WaveOut_R
; *** COMPUTE THE NUMBER OF FREE BLOCKS AVAILABLE
lea ecx,[esp+12] ; &hBuff
push edi ; space = 0
mov edx,esp
push 4 ; .out_size
push edx ; .output = &space
push 4 ; .inp_size
push ecx ; .input
push 17 ; .code = SND_GETFREESPACE
mov ecx,[ecx]
push DWORD PTR [hSound] ; .handle
mov [hBuff],ecx
else
mov ebp,OFFSET hSound
; *** PCM OUTPUT ENABLED?
cmp [ebp+8],edi ; SW_Exit
lea eax,[edi+1] ; return error if output not enabled
je _uFMOD_WaveOut_R
; *** COMPUTE THE NUMBER OF FREE BLOCKS AVAILABLE
push edi ; space = 0
mov edx,esp
push 4 ; .out_size
lea ecx,[ebp+4] ; &hBuff
push edx ; .output = &space
push 4 ; .inp_size
push ecx ; .input
push 17 ; .code = SND_GETFREESPACE
push DWORD PTR [ebp] ; .handle = [hSound]
endif ; AC97SND_ON = 0
lea ebx,[edi+17]
lea eax,[edi+68]
mov ecx,esp
int 40h
add esp,24
pop edi ; <- space
shr edi,FSOUND_Block+2 ; / (FSOUND_BlockSize * 4)
jz _uFMOD_WaveOut_R ; no free blocks available
_uFMOD_WaveOut_loop:
call uFMOD_do_WaveOut
neg eax
dec edi
ja _uFMOD_WaveOut_loop
_uFMOD_WaveOut_R:
pop ebp
pop edi
ret
uFMOD_do_WaveOut:
mov ecx,FSOUND_BlockSize*2
push ebx
push esi
push edi
mov edi,OFFSET MixBuf
xor eax,eax
push edi ; mixbuffer <= MixBuf
push edi ; <- MixPtr
; MIXBUFFER CLEAR
mov esi,OFFSET _mod+36
rep stosd
if AC97SND_ON
else
if PAUSE_RESUME_ON
cmp [ufmod_pause_],al
xchg eax,ebp
jne do_swfill
endif
endif ; AC97SND_ON = 0
mov ebp,FSOUND_BlockSize
; UPDATE MUSIC
mov ebx,[esi+FMUSIC_MODULE.mixer_samplesleft-36]
fill_loop_1:
test ebx,ebx
jnz mixedleft_nz
; UPDATE XM EFFECTS
cmp [esi+FMUSIC_MODULE.tick-36],ebx ; new note
mov ecx,[esi+FMUSIC_MODULE.pattern-36]
jne update_effects
dec ebx
; process any rows commands to set the next order/row
mov edx,[esi+FMUSIC_MODULE.nextorder-36]
mov eax,[esi+FMUSIC_MODULE.nextrow-36]
mov [esi+FMUSIC_MODULE.nextorder-36],ebx
test edx,edx
mov [esi+FMUSIC_MODULE.nextrow-36],ebx
jl fill_nextrow
mov [esi+FMUSIC_MODULE.order-36],edx
fill_nextrow:
test eax,eax
jl update_note
mov [esi+FMUSIC_MODULE.row-36],eax
update_note:
; mod+36 : ESI
call DoNote
if ROWCOMMANDS_ON
cmp DWORD PTR [esi+FMUSIC_MODULE.nextrow-36],-1
jne inc_tick
endif
mov eax,[esi+FMUSIC_MODULE.row-36]
inc eax
; if end of pattern
; "if(mod->nextrow >= mod->pattern[mod->orderlist[mod->order]].rows)"
cmp ax,[ebx]
jl set_nextrow
mov edx,[esi+FMUSIC_MODULE.order-36]
movzx ecx,WORD PTR [esi+FMUSIC_MODULE.numorders-36]
inc edx
xor eax,eax
cmp edx,ecx
jl set_nextorder
if AC97SND_ON
else
; We've reached the end of the order list. So, stop playing, unless looping is enabled.
if NOLOOP_ON
cmp [ufmod_noloop],al
je set_restart
pop eax ; skip MixPtr
pop edx ; skip mixbuffer
pop edi
inc eax ; end of loop reached while XM_NOLOOP flag is enabled
pop esi
pop ebx
ret
set_restart:
endif
endif ; AC97SND_ON = 0
movzx edx,WORD PTR [esi+FMUSIC_MODULE.restart-36]
cmp edx,ecx
sbb ecx,ecx
and edx,ecx
set_nextorder:
mov [esi+FMUSIC_MODULE.nextorder-36],edx
set_nextrow:
mov [esi+FMUSIC_MODULE.nextrow-36],eax
jmp inc_tick
update_effects:
; mod+36 : ESI
call DoEffs
inc_tick:
mov eax,[esi+FMUSIC_MODULE.speed-36]
mov ebx,[esi+FMUSIC_MODULE.mixer_samplespertick-36]
inc DWORD PTR [esi+FMUSIC_MODULE.tick-36]
if PATTERNDELAY_ON
add eax,[esi+FMUSIC_MODULE.patterndelay-36]
endif
cmp [esi+FMUSIC_MODULE.tick-36],eax
jl mixedleft_nz
if PATTERNDELAY_ON
and DWORD PTR [esi+FMUSIC_MODULE.patterndelay-36],0
endif
and DWORD PTR [esi+FMUSIC_MODULE.tick-36],0
mixedleft_nz:
mov edi,ebp
cmp ebx,edi
jae fill_ramp
mov edi,ebx
fill_ramp:
pop edx ; <- MixPtr
sub ebp,edi
lea eax,[edx+edi*8]
push eax ; MixPtr += (SamplesToMix<<3)
; tail : [arg0]
; len : EDI
; mixptr : EDX
; _mod+36 : ESI
call Ramp
if AC97SND_ON
else
if INFO_API_ON
lea eax,[edi+edi*4]
cdq
shl eax,2
mov ecx,FSOUND_MixRate/50
div ecx
; time_ms += SamplesToMix*FSOUND_OOMixRate*1000
add [time_ms],eax
endif
endif ; AC97SND_ON = 0
sub ebx,edi ; MixedLeft -= SamplesToMix
test ebp,ebp
jnz fill_loop_1
mov [esi+FMUSIC_MODULE.mixer_samplesleft-36],ebx ; <= MixedLeft
if AC97SND_ON
else
if INFO_API_ON
mov ecx,[esi + FMUSIC_MODULE.row-36]
or ecx,[esi + FMUSIC_MODULE.order-2-36]
mov DWORD PTR [s_row],ecx
endif
endif ; AC97SND_ON = 0
do_swfill:
; *** CLIP AND COPY BLOCK TO OUTPUT BUFFER
pop eax ; skip MixPtr
pop esi ; <- mixbuffer
if AC97SND_ON
else
if INFO_API_ON
; ebx : L channel RMS volume
; ebp : R channel RMS volume
xor ebx,ebx
endif
endif ; AC97SND_ON = 0
mov edi,esi
mov ecx,FSOUND_BlockSize*2
align 4
fill_loop_2:
lodsd
if AC97SND_ON
mov ebx,eax
cdq
xor eax,edx
sub eax,edx
mov ebp,255*volumerampsteps/2
xor edx,edx
div ebp
cmp edx,255*volumerampsteps/4
sbb eax,-1
cmp eax,8000h
sbb edx,edx
not edx
or eax,edx
sar ebx,31
and eax,7FFFh
xor eax,ebx
sub eax,ebx
else
if INFO_API_ON
push edi
cdq
mov edi,eax
push esi
xor eax,edx
mov esi,255*volumerampsteps/2
sub eax,edx
xor edx,edx
div esi
cmp edx,255*volumerampsteps/4
pop esi
sbb eax,-1
cmp eax,8000h
sbb edx,edx
not edx
or eax,edx
sar edi,31
and eax,7FFFh
if VOL_CONTROL_ON
mul DWORD PTR [ufmod_vol]
shr eax,15
endif
; sum. the L and R channel RMS volume
ror ecx,1
sbb edx,edx
and edx,eax
add ebp,edx ; += |vol|
rol ecx,1
sbb edx,edx
not edx
and edx,eax
add ebx,edx ; += |vol|
xor eax,edi
sub eax,edi
pop edi
else
mov ebx,eax
cdq
xor eax,edx
sub eax,edx
mov ebp,255*volumerampsteps/2
xor edx,edx
div ebp
cmp edx,255*volumerampsteps/4
sbb eax,-1
cmp eax,8000h
sbb edx,edx
not edx
or eax,edx
sar ebx,31
and eax,7FFFh
if VOL_CONTROL_ON
mul DWORD PTR [ufmod_vol]
shr eax,15
endif
xor eax,ebx
sub eax,ebx
endif
endif ; AC97SND_ON = 0
dec ecx
stosw
jnz fill_loop_2
if AC97SND_ON
else
if INFO_API_ON
shr ebp,FSOUND_Block ; R_vol / FSOUND_BlockSize
shl ebx,16-FSOUND_Block ; (L_vol / FSOUND_BlockSize) << 16
mov bx,bp
mov DWORD PTR [L_vol],ebx
endif
endif ; AC97SND_ON = 0
; *** DISPATCH DATA TO THE AC97 DRIVER
push FSOUND_BlockSize*4 ; size
push OFFSET MixBuf ; &src
push DWORD PTR [hBuff] ; buffer
mov edx,esp
push ecx ; .out_size
push ecx ; .output
push 12 ; .inp_size
push edx ; .input
push 9 ; .code = SND_OUT
push DWORD PTR [hSound] ; .handle
lea eax,[ecx+68]
lea ebx,[ecx+17]
mov ecx,esp
int 40h
add esp,36
pop edi
pop esi
pop ebx
ret