diff --git a/programs/develop/libraries/ufmod/Fasm/frmwrk.asm b/programs/develop/libraries/ufmod/Fasm/frmwrk.asm new file mode 100644 index 0000000000..5a8bd8bac7 --- /dev/null +++ b/programs/develop/libraries/ufmod/Fasm/frmwrk.asm @@ -0,0 +1,142 @@ +; FRMWRK.ASM +; ---------- +; A set of common GUI code used in uFMOD examples for KolibriOS. +; Feel free to reuse it in your own projects if you like it ;) + +; --------------------------------------------------------------- +; void _cdecl _MessageBox (szCap, lpString, cbString); +; void _cdecl _MessageBoxCB(szCap, lpString, cbString, cbProc); +; --------------------------------------------------------------- + +; This is similar to a Win32 MessageBox. The box is centered +; on screen. It contains a single-line text message and an +; "OK" button. This function returns when user closes the +; box (via the X button or via the OK button). An optional +; callback subroutine may be specified to be called when no +; events are pending in the event queue. + +; NOTE: Doesn't work if you already have defined a window +; in the current process! Doesn't modify the event mask. So, +; make sure keyboard events are enabled before calling this +; function. This function doesn't check the validity of the +; supplied parameters! + +; Parameters: +; szCap - A pointer to the ASCIIz string containing the +; caption. A trailing zero char IS required. +; lpString - A pointer to an ASCII string containing a single +; line message to pop up in the box. No trailing +; zero char is required. +; cbString - number of characters in string. +; cbProc - Address of the callback subroutine. Can be NULL. + +sOK db "OK" +if FRMWRK_CALLBACK_ON +_MessageBoxCB: +else +_MessageBox: +end if + push ebp + push esi + push edi + push ebx + xor ebp,ebp ; global 0 + mov esi,[esp+28] ; cbString + mov edi,[esp+20] ; szCap + + ; Get screen metrics. + lea eax,[ebp+14] + int 40h + mov ecx,eax + movzx eax,ax + shr ecx,16 ; screen w + xchg eax,edx ; screen h + lea ebx,[esi*2+esi] + lea ebx,[ebx*2+28] ; w = string len * 6 + 28 + sub ecx,ebx + shr ecx,1 + shl ecx,16 + or ebx,ecx + lea ecx,[ebp+52h] ; h = 52h + sub edx,ecx + shr edx,1 + shl edx,16 + or ecx,edx ; y = (screen h - window h) / 2 + mov edx,ebx ; x = (screen w - window w) / 2 + +_MessageBoxCB_redraw: + ; Start redraw. + push edx + lea eax,[ebp+12] + lea ebx,[ebp+1] + int 40h + + ; Define and draw window. + xor eax,eax + mov ebx,edx ; x, w (ECX: y, h) + mov edx,34C0C0C0h ; style and BG color + int 40h + + ; Define the OK button. + push esi + lea eax,[ebp+8] + sub ebx,28+0Ah + shr bx,1 + shl ebx,16 ; x = (window w - button w) / 2 + mov bx,18+0Ah ; w = 18 + 0Ah + mov ecx,001C0012h ; y = 1Ch, h = 12h + lea edx,[ebp+1] ; ID = close + mov esi,0C0C0C0h ; color + int 40h + + ; Draw the OK label. + lea eax,[ebp+4] + add ebx,90000h ; x = button x + 9 + mov bx,22h ; y = 22h + xor ecx,ecx ; style, font and color + mov edx,sOK ; string + lea esi,[ebp+2] ; length + int 40h + pop esi + + ; Draw text string. + lea eax,[ebp+4] + mov ebx,000A000Ah ; x = 0Ah, y = 0Ah + xor ecx,ecx ; style, font and color + mov edx,[esp+28] ; lpString + int 40h + + ; End redraw. + lea eax,[ebp+12] + lea ebx,[ebp+2] + int 40h + +if FRMWRK_CALLBACK_ON +_MessageBoxCB_eventloop: + mov edx,[esp+36] ; cbProc + test edx,edx + lea eax,[ebp+10] + jz _MessageBoxCB_peekevent + + ; Invoke the callback. + call edx + + lea eax,[ebp+23] + lea ebx,[ebp+10] ; wait for at most 0.1 sec +_MessageBoxCB_peekevent: + int 40h + dec eax + js _MessageBoxCB_eventloop +else + lea eax,[ebp+10] + int 40h + dec eax +end if + pop edx + jz _MessageBoxCB_redraw + + pop ebx + pop edi + pop esi + pop ebp + ret diff --git a/programs/develop/libraries/ufmod/Fasm/jmp2pat.asm b/programs/develop/libraries/ufmod/Fasm/jmp2pat.asm new file mode 100644 index 0000000000..3aecccd448 --- /dev/null +++ b/programs/develop/libraries/ufmod/Fasm/jmp2pat.asm @@ -0,0 +1,239 @@ +; JMP2PAT.ASM +; ----------- +; Sometimes it makes sense merging various XM tracks +; sharing the same instruments in a single XM file. +; This example program uses such an XM file actually +; containing 3 tracks and the _uFMOD_Jump2Pattern +; function to play all 3 tracks in the same file. + +; A precompiled version (not packed or whatever) is +; available in bin\ + +use32 +org 0 +db 'MENUET01' +dd 1 +dd START ; Entry point +dd uFMOD_IMG_END ; End of code and initialized data +dd MEMORY_END ; End of uninitialized (BSS) data +dd STACK_B ; Bottom of the stack +dd 0 ; Args +dd 0 ; Reserved + +; uFMOD setup: +UF_FREQ equ 48000 ; Set sampling rate to 48KHz (22050, 44100, 48000) +UF_RAMP equ STRONG ; Select STRONG interpolation (NONE, WEAK, STRONG) +UD_MODE equ UNSAFE ; Select UNSAFE mode (NORMAL, UNSAFE) +DEBUG equ 0 ; Skip debug-board messages +NOLINKER equ 1 ; Select "no linker" mode + +; uFMOD constants: +XM_MEMORY = 1 +XM_FILE = 2 +XM_NOLOOP = 8 +XM_SUSPENDED = 16 +uFMOD_MIN_VOL = 0 +uFMOD_MAX_VOL = 25 +uFMOD_DEFAULT_VOL = 25 + +; BLITZXMK.XM tracked by Kim (aka norki): +; [00:07] - track #1 +; [08:10] - track #2 +; [11:13] - track #3 +xm file '..\ufmodlib\media\BLITZXMK.XM' +xm_length = $ - xm + +; Optimization: +; This header file is suitable for blitzxmk.xm track only! +; If you change the track, update the optimization header. +; (Use the standart eff.inc file for a general purpose player app.) +include '..\ufmodlib\media\blitz.eff.inc' + +; Include the GUI framework. +FRMWRK_CALLBACK_ON equ 0 ; Disable callback +include 'frmwrk.asm' + +; UI text messages. +vals dd 0,8,11 ; Preset pattern indexes +wnd_btns1 db "1 2 3 Pause " +wnd_btns2 db "1 2 3 Resume" +wnd_btns_l = $ - wnd_btns2 +wnd_cap db "Jump2Pattern",0 +err_txt db "Error" +err_txt_l = $ - err_txt +err_cap db ":-(",0 + +START: + ; Start playback. + push XM_MEMORY + push xm_length + push xm + call _uFMOD_LoadSong + + ; Stack fixing is required here, but in this simple + ; example leaving ESP as it is won't harm. In a real + ; application you should uncomment the following line: + ; add esp,12 + + test eax,eax + jz error + xor ebp,ebp ; global 0 + mov [wnd_btns],wnd_btns1 + + ; Switch keyboard mode to SCANCODE. + lea ebx,[ebp+1] + lea eax,[ebp+66] + mov ecx,ebx + int 40h + + ; Get screen metrics. + lea eax,[ebp+14] + int 40h + mov ecx,eax + movzx eax,ax + shr ecx,16 ; screen w + xchg eax,edx ; screen h + mov ebx,wnd_btns_l*6+42 + sub ecx,ebx + shr ecx,1 + shl ecx,16 + or ebx,ecx + lea ecx,[ebp+40h] ; h = 40h + sub edx,ecx + shr edx,1 + shl edx,16 + or ecx,edx ; y = (screen h - window h) / 2 + mov edx,ebx ; x = (screen w - window w) / 2 + +redraw: + ; Start redraw. + push edx + lea eax,[ebp+12] + lea ebx,[ebp+1] + int 40h + + ; Define and draw window. + xor eax,eax + mov ebx,edx ; x, w (ECX: y, h) + mov edx,34C0C0C0h ; style and BG color + mov edi,wnd_cap + int 40h + + ; Define the 1 2 3 Pause/Resume buttons. + lea eax,[ebp+8] + mov ebx,0A0012h ; x = 0Ah, w = 12h + mov ecx,0A0012h ; y = 0Ah, h = 10h + lea edx,[ebp+10] ; ID = #10 + mov esi,0C0C0C0h ; color + int 40h + mov ebx,280012h ; x = 28h, w = 12h + inc edx ; ID = #11 + int 40h + mov ebx,460012h ; x = 46h, w = 12h + inc edx ; ID = #12 + int 40h + mov ebx,640030h ; x = 64h, w = 30h + inc edx ; ID = #13 + int 40h + + ; Draw the labels. + lea eax,[ebp+4] + mov ebx,120011h ; x = 12h, y = 11h + xor ecx,ecx ; style, font and color + mov edx,[wnd_btns] ; string + lea esi,[ebp+wnd_btns_l] ; length + int 40h + + ; End redraw. + lea eax,[ebp+12] + lea ebx,[ebp+2] + int 40h + +eventloop: + ; Update the PCM buffer. + call _uFMOD_WaveOut + + lea eax,[ebp+23] + lea ebx,[ebp+10] ; wait for at most 0.1 sec + int 40h + dec eax + js eventloop ; 0 = idle + jz redraw ; 1 = redraw + + dec eax ; 2 = keyboard event + jnz chk_eventbutton + + ; Get key scancode. + lea eax,[ebp+2] + int 40h + cmp ah,19h ; P + je do_PauseResume + cmp ah,13h ; R + je do_PauseResume +chk_kb123: + movzx eax,ah + sub eax,2 + jmp do_Jump2Pat123 + +chk_eventbutton: ; 3 = button event + lea eax,[ebp+17] + int 40h + cmp ah,1 ; Close + je break_loop + cmp ah,13 ; Pause/Resume + jne chk_btn123 + +do_PauseResume: + cmp BYTE [paused],1 + mov edx,_uFMOD_Resume + mov ebx,wnd_btns1 + je do_Resume + mov edx,_uFMOD_Pause + mov ebx,wnd_btns2 +do_Resume: + call edx + xor BYTE [paused],1 + mov [wnd_btns],ebx + jmp redraw + +chk_btn123: ; 1 2 3 + movzx eax,ah + sub eax,10 + +do_Jump2Pat123: + cmp eax,3 + jae eventloop + push DWORD [vals+eax*4] + call _uFMOD_Jump2Pattern + pop eax ; fix stack + jmp eventloop +break_loop: + + ; Stop playback. + call _uFMOD_StopSong + +r: ; Exit. + xor eax,eax + dec eax + int 40h + +error: + push err_txt_l ; cbString + push err_txt ; lpString + push err_cap ; szCap + call _MessageBox + ; add esp,16 + jmp r + + ; Include the whole uFMOD sources here. (Right after + ; your main code to avoid naming conflicts, but still + ; inside your code section.) + macro PUBLIC symbol {} ; hide all publics + include '..\ufmodlib\src\fasm.asm' + +wnd_btns dd ? +paused db ? +align 4 + rb 1020 +STACK_B dd ? ; Stack bottom +MEMORY_END: ; End of uninitialized (BSS) data diff --git a/programs/develop/libraries/ufmod/Fasm/make.bat b/programs/develop/libraries/ufmod/Fasm/make.bat new file mode 100644 index 0000000000..0ee97daefa --- /dev/null +++ b/programs/develop/libraries/ufmod/Fasm/make.bat @@ -0,0 +1,20 @@ +@echo off +rem Make the uFMOD examples. +rem Compiler: FASM +rem Target OS: KolibriOS + +rem FASM Path: +SET UF_FASM=\fasm + +if not exist "%UF_FASM%\fasm.exe" goto Err1 +"%UF_FASM%\fasm" mini.asm mini +"%UF_FASM%\fasm" jmp2pat.asm jmp2pat +goto TheEnd + +:Err1 +echo Couldn't find fasm.exe in %UF_FASM%\ + +:TheEnd +pause +@echo on +cls diff --git a/programs/develop/libraries/ufmod/Fasm/mini.asm b/programs/develop/libraries/ufmod/Fasm/mini.asm new file mode 100644 index 0000000000..fd3f40c5b8 --- /dev/null +++ b/programs/develop/libraries/ufmod/Fasm/mini.asm @@ -0,0 +1,109 @@ +; MINI.ASM +; -------- +; Minimalistic uFMOD usage example. + +; Shows how to play an XM track in memory, +; including proper error handling. + +; A precompiled version (not packed or whatever) is +; available in bin\ + +use32 +org 0 +db 'MENUET01' +dd 1 +dd START ; Entry point +dd uFMOD_IMG_END ; End of code and initialized data +dd MEMORY_END ; End of uninitialized (BSS) data +dd STACK_B ; Bottom of the stack +dd 0 ; Args +dd 0 ; Reserved + +; uFMOD setup: +UF_FREQ equ 48000 ; Set sampling rate to 48KHz (22050, 44100, 48000) +UF_RAMP equ STRONG ; Select STRONG interpolation (NONE, WEAK, STRONG) +UD_MODE equ UNSAFE ; Select UNSAFE mode (NORMAL, UNSAFE) +DEBUG equ 0 ; Skip debug-board messages +NOLINKER equ 1 ; Select "no linker" mode + +; uFMOD constants: +XM_MEMORY = 1 +XM_FILE = 2 +XM_NOLOOP = 8 +XM_SUSPENDED = 16 +uFMOD_MIN_VOL = 0 +uFMOD_MAX_VOL = 25 +uFMOD_DEFAULT_VOL = 25 + +; The XM track. +xm file '..\ufmodlib\media\mini.xm' +xm_length = $ - xm + +; Optimization: +; This header file is suitable for mini.xm track only! +; If you change the track, update the optimization header. +; (Use the standart eff.inc file for a general purpose player app.) +include '..\ufmodlib\media\mini.eff.inc' + +; Include the GUI framework. +FRMWRK_CALLBACK_ON equ 1 ; Enable callback +include 'frmwrk.asm' + +; UI text messages. +msg_txt db "uFMOD ruleZ!" +msg_txt_l = $ - msg_txt +msg_cap db "FASM",0 +err_txt db "Error" +err_txt_l = $ - err_txt +err_cap db ":-(",0 + +START: + ; Start playback. + push XM_MEMORY + push xm_length + push xm + call _uFMOD_LoadSong + + ; Stack fixing is required here, but in this simple + ; example leaving ESP as it is won't harm. In a real + ; application you should uncomment the following line: + ; add esp,12 + + test eax,eax + jz error + + ; Wait for user input. + push _uFMOD_WaveOut ; cbProc <- continue fetching data! + push msg_txt_l ; cbString + push msg_txt ; lpString + push msg_cap ; szCap + call _MessageBoxCB + ; add esp,16 + + ; Stop playback. + call _uFMOD_StopSong + +r: ; Exit. + xor eax,eax + dec eax + int 40h + +error: + push 0 ; cbProc <- no callback + push err_txt_l ; cbString + push err_txt ; lpString + push err_cap ; szCap + call _MessageBoxCB + ; add esp,16 + jmp r + + ; Include the whole uFMOD sources here. (Right after + ; your main code to avoid naming conflicts, but still + ; inside your code section.) + macro PUBLIC symbol {} ; hide all publics + include '..\ufmodlib\src\fasm.asm' + +align 4 + rb 1020 +STACK_B dd ? ; Stack bottom +MEMORY_END: ; End of uninitialized (BSS) data diff --git a/programs/develop/libraries/ufmod/Masm32/make.bat b/programs/develop/libraries/ufmod/Masm32/make.bat new file mode 100644 index 0000000000..43472bd360 --- /dev/null +++ b/programs/develop/libraries/ufmod/Masm32/make.bat @@ -0,0 +1,35 @@ +@echo off +rem Set compiler location: +SET MASM32=\masm32 +SET UF_FASM=\fasm + +if not exist "%MASM32%\bin\ml.exe" goto Err1 +if not exist "%UF_FASM%\fasm.exe" goto Err2 +"%MASM32%\bin\ml" /c /coff mini.asm +"%MASM32%\bin\link" /DRIVER /SUBSYSTEM:NATIVE /BASE:-0x10000 /ALIGN:0x10000 /MERGE:.data=.text -ignore:4078 mini.obj ufmod.obj +del mini.obj +echo virtual at 0 >tmp.asm +echo file 'mini.exe':3Ch,4 >>tmp.asm +echo load pehea dword from 0 >>tmp.asm +echo file 'mini.exe':pehea+0F8h,28h >>tmp.asm +echo load physofs dword from 4+14h >>tmp.asm +echo load mem dword from 4+8 >>tmp.asm +echo file 'mini.exe':physofs+16,4 >>tmp.asm +echo load sz dword from $-4 >>tmp.asm +echo end virtual >>tmp.asm +echo file 'mini.exe':physofs,sz >>tmp.asm +echo store dword mem at 14h >>tmp.asm +"%UF_FASM%\fasm" tmp.asm mini +del mini.exe +del tmp.asm + +goto TheEnd +:Err1 +echo Couldn't find ml.exe in %MASM32%\bin +goto TheEnd +:Err2 +echo Couldn't find fasm.exe in %UF_FASM%\ + +:TheEnd +pause +cls diff --git a/programs/develop/libraries/ufmod/Masm32/mini.asm b/programs/develop/libraries/ufmod/Masm32/mini.asm new file mode 100644 index 0000000000..eb94617a3b --- /dev/null +++ b/programs/develop/libraries/ufmod/Masm32/mini.asm @@ -0,0 +1,266 @@ +; MINI.ASM +; -------- +; Shows how to place data and code inside (!!!) the XM track. + +.386 +.model flat + +include ufmod.inc ; uFMOD API + +.CODE + +PE_BASE db "MENUET01" + dd 1 + dd OFFSET _START ; Entry point + dd OFFSET BSS_START ; End of code and initialized data + dd OFFSET BSS_END ; End of uninitialized (BSS) data + dd OFFSET STACK_B ; Bottom of the stack + dd 0 ; Args + dd 0 ; Reserved + +err_txt db "Error" +err_txt_l EQU $ - err_txt + +_START: + ; Start playback. + push XM_MEMORY + push xm_length + push BYTE PTR xm + +; Let's place the stream right inside the code section. +xm_length EQU 905 +xm EQU $ - PE_BASE +xm_unused_000 LABEL BYTE + +; *** The following 60 bytes are not used. So, we'll place +; *** some code here. +; (the actual size and location of such gaps may be +; found out using the Eff utility) + + call uFMOD_LoadSong + xor ebp,ebp ; global 0 + + ; Stack fixing is required here, but in this simple + ; example leaving ESP as it is won't harm. In a real + ; application you should uncomment the following line: + ; add esp,12 + + test eax,eax + jz error + + ; Wait for user input. + push uFMOD_WaveOut ; cbProc <- continue fetching data! + push BYTE PTR msg_txt_l ; cbString + push OFFSET msg_txt ; lpString + push OFFSET msg_cap ; szCap + call MessageBoxCB + ; add esp,16 + + ; Stop playback. + call uFMOD_StopSong + +r: ; Exit. + lea eax,[ebp-1] + int 40h + +error: + push ebp ; cbProc <- no callback + push BYTE PTR err_txt_l ; cbString + push BYTE PTR (err_txt - PE_BASE) ; lpString + push OFFSET err_cap ; szCap + call MessageBoxCB + ; add esp,16 + jmp r + +org xm_unused_000 + 60 + db 034h,000h,000h,000h,020h,000h,000h,000h,002h,000h,00Dh,000h,001h,000h,001h,000h + db 00Ah,000h,091h,000h,000h,001h,002h,003h,004h,005h,006h,007h,000h,001h,002h,003h + db 004h,005h,006h,007h,008h,009h,00Ah,00Bh,008h,009h,00Ch,00Bh,008h,009h,00Ah,00Bh + db 008h,009h,00Ch,00Bh,009h,000h,000h,000h,000h,004h,000h,001h,000h,083h,016h,001h + db 080h,080h,02Eh,001h,000h,00Eh,060h,080h,03Ah,001h,000h,00Eh,062h,081h,061h,083h + db 035h,001h,009h,000h,000h,000h,000h,004h,000h,001h,000h,083h,016h,001h,080h,080h + db 02Eh,001h,000h,00Eh,060h,080h,035h,001h,000h,00Eh,062h,081h,061h,083h,038h,001h + db 009h,000h,000h,000h,000h,004h,000h,001h,000h,083h,016h,001h,080h,080h,02Eh,001h + db 000h,00Eh,060h,080h,038h,001h,000h,00Eh,062h,080h,083h,033h,001h,009h,000h,000h + db 000h,000h,006h,000h,001h,000h,083h,016h,001h,080h,080h,02Eh,001h,000h,00Eh,060h + db 080h,033h,001h,000h,00Eh,061h,081h,061h,083h,035h,001h,083h,00Dh,001h,083h,036h + db 001h,080h,083h,036h,001h,009h,000h,000h,000h,000h,004h,000h,001h,000h,083h,00Fh + db 001h,080h,080h,02Eh,001h,000h,00Eh,060h,080h,036h,001h,000h,00Eh,062h,081h,061h + db 083h,033h,001h,009h,000h,000h,000h,000h,006h,000h,001h,000h,083h,00Fh,001h,080h + db 080h,02Eh,001h,000h,00Eh,060h,080h,033h,001h,000h,00Eh,061h,081h,061h,083h,02Eh + db 001h,083h,012h,001h,083h,033h,001h,080h,083h,035h,001h,009h,000h,000h,000h,000h + db 006h,000h,001h,000h,083h,016h,001h,080h,080h,02Eh,001h,000h,00Eh,060h,080h,035h + db 001h,000h,00Eh,061h,081h,061h,083h,02Eh,001h,083h,00Dh,001h,083h,031h,001h,080h + db 083h,02Eh,001h,009h,000h,000h,000h,000h,008h,000h,001h,000h,083h,012h,001h,098h + db 00Ah,001h,083h,019h,001h,088h,00Ah,083h,01Eh,001h,081h,061h,083h,012h,001h,080h + db 083h,014h,001h,080h,083h,01Bh,001h,080h,083h,020h,001h,080h,083h,014h,001h,080h + db 009h,000h,000h,000h,000h,008h,000h,001h,000h,083h,012h,001h,081h,061h,083h,019h + db 001h,080h,083h,01Eh,001h,080h,083h,012h,001h,080h,083h,019h,001h,083h,031h,001h + db 083h,01Eh,001h,080h,083h,012h,001h,083h,031h,001h,083h,019h,001h,080h,009h,000h + db 000h,000h,000h,008h,000h,001h,000h,083h,014h,001h,083h,033h,001h,083h,01Bh,001h + db 080h,083h,020h,001h,083h,031h,001h,083h,014h,001h,080h,083h,01Bh,001h,083h,030h + db 001h,083h,020h,001h,080h,083h,014h,001h,083h,031h,001h,083h,01Bh,001h,080h,009h + db 000h,000h,000h,000h,008h,000h,001h,000h,083h,016h,001h,083h,030h,001h,083h,01Dh + db 001h,083h,031h,001h,083h,022h,001h,083h,035h,001h,083h,016h,001h,098h,00Ah,001h + db 083h,01Dh,001h,088h,00Ah,083h,022h,001h,081h,061h,083h,016h,001h,080h,083h,01Dh + db 001h,080h,009h,000h,000h,000h,000h,008h,000h,001h,000h,083h,016h,001h,080h,083h + db 01Dh,001h,080h,083h,022h,001h,080h,083h,016h,001h,080h,083h,018h,001h,080h,083h + db 01Dh,001h,080h,083h,011h,001h,080h,083h,018h,001h,080h,009h,000h,000h,000h,000h + db 008h,000h,001h,000h,083h,016h,001h,083h,030h,001h,083h,01Dh,001h,083h,031h,001h + db 083h,019h,001h,083h,02Eh,001h,083h,016h,001h,098h,00Ah,001h,083h,01Dh,001h,088h + db 00Ah,083h,019h,001h,081h,061h,083h,016h,001h,080h,083h,01Dh,001h,080h,0F1h,000h + db 000h,000h +xm_unused_001 LABEL BYTE + +; The following 23 bytes are not used. +; So, let's place the MessageBox text and caption instead. +; UI text messages. +msg_txt db "uFMOD ruleZ!" +msg_txt_l equ $ - msg_txt +msg_cap db "MASM32",0 +err_cap db ":-(",0 + +org xm_unused_001 + 23 + db 001h,000h,012h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + db 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + db 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + db 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + db 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + db 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + db 000h,000h,000h,000h,000h,000h,000h,000h,040h,000h,008h,000h,02Ch,000h,00Eh,000h + db 008h,000h,018h,000h,016h,000h,020h,000h,008h,000h,02Dh,000h,00Dh,000h,032h,000h + db 004h,000h,03Ch,000h,007h,000h,044h,000h,004h,000h,05Ah,000h,000h,000h,064h,000h + db 000h,000h,06Eh,000h,000h,000h,000h,000h,020h,000h,00Ah,000h,028h,000h,01Eh,000h + db 018h,000h,032h,000h,020h,000h,03Ch,000h,020h,000h,046h,000h,020h,000h,050h,000h + db 020h,000h,05Ah,000h,020h,000h,064h,000h,020h,000h,06Eh,000h,020h,000h,078h,000h + db 020h,000h,082h,000h,020h,000h,009h,006h,001h,002h,004h,002h,003h,005h,001h,000h + db 000h,000h,000h,000h,080h,000h,00Ch,000h,000h,000h,000h,000h,000h,000h,00Ch,000h + db 000h,000h,040h,000h,001h,080h,0F9h,000h,0BFh,000h,0C3h,000h,00Ah,000h,057h,000h + db 06Eh,000h,023h,000h + +; ---------------------------------------------------------- +; void MessageBoxCB(szCap, lpString, cbString, cbProc); +; ---------------------------------------------------------- + +; This is similar to a Win32 MessageBox. The box is centered +; on screen. It contains a single-line text message and an +; "OK" button. This function returns when user closes the +; box (via the X button or via the OK button). An optional +; callback subroutine may be specified to be called when no +; events are pending in the event queue. + +; NOTE: Doesn't work if you already have defined a window +; in the current process! Doesn't modify the event mask. So, +; make sure keyboard events are enabled before calling this +; function. This function doesn't check the validity of the +; supplied parameters! + +; Parameters: +; szCap - A pointer to the ASCIIz string containing the +; caption. A trailing zero char IS required. +; lpString - A pointer to an ASCII string containing a single +; line message to pop up in the box. No trailing +; zero char is required. +; cbString - number of characters in string. +; cbProc - Address of the callback subroutine. Can be NULL. + +sOK db "OK" +MessageBoxCB: +; EBP = 0 + mov esi,[esp+12] ; cbString + mov edi,[esp+4] ; szCap + + ; Get screen metrics. + lea eax,[ebp+14] + int 40h + mov ecx,eax + movzx eax,ax + shr ecx,16 ; screen w + xchg eax,edx ; screen h + lea ebx,[esi*2+esi] + lea ebx,[ebx*2+28] ; w = string len * 6 + 28 + sub ecx,ebx + shr ecx,1 + shl ecx,16 + or ebx,ecx + lea ecx,[ebp+52h] ; h = 52h + sub edx,ecx + shr edx,1 + shl edx,16 + or ecx,edx ; y = (screen h - window h) / 2 + mov edx,ebx ; x = (screen w - window w) / 2 + +_MessageBoxCB_redraw: + ; Start redraw. + push edx + lea eax,[ebp+12] + lea ebx,[ebp+1] + int 40h + + ; Define and draw window. + xor eax,eax + mov ebx,edx ; x, w (ECX: y, h) + mov edx,34C0C0C0h ; style and BG color + int 40h + + ; Define the OK button. + push esi + lea eax,[ebp+8] + sub ebx,28+0Ah + shr bx,1 + shl ebx,16 ; x = (window w - button w) / 2 + mov bx,18+0Ah ; w = 18 + 0Ah + mov ecx,001C0012h ; y = 1Ch, h = 12h + lea edx,[ebp+1] ; ID = close + mov esi,0C0C0C0h ; color + int 40h + + ; Draw the OK label. + lea eax,[ebp+4] + add ebx,90000h ; x = button x + 9 + mov bx,22h ; y = 22h + xor ecx,ecx ; style, font and color + mov edx,OFFSET sOK ; string + lea esi,[ebp+2] ; length + int 40h + pop esi + + ; Draw text string. + lea eax,[ebp+4] + mov ebx,000A000Ah ; x = 0Ah, y = 0Ah + xor ecx,ecx ; style, font and color + mov edx,[esp+12] ; lpString + int 40h + + ; End redraw. + lea eax,[ebp+12] + lea ebx,[ebp+2] + int 40h + +_MessageBoxCB_eventloop: + mov edx,[esp+20] ; cbProc + test edx,edx + lea eax,[ebp+10] + jz _MessageBoxCB_peekevent + + ; Invoke the callback. + call edx + + lea eax,[ebp+23] + lea ebx,[ebp+10] ; wait for at most 0.1 sec +_MessageBoxCB_peekevent: + int 40h + dec eax + js _MessageBoxCB_eventloop + + pop edx + jz _MessageBoxCB_redraw + ret + +.DATA? +BSS_START LABEL BYTE + db 1020 dup (?) +STACK_B dd ? ; Stack bottom +BSS_END LABEL BYTE + +END _START \ No newline at end of file diff --git a/programs/develop/libraries/ufmod/Masm32/ufmod.inc b/programs/develop/libraries/ufmod/Masm32/ufmod.inc new file mode 100644 index 0000000000..0649480cd3 --- /dev/null +++ b/programs/develop/libraries/ufmod/Masm32/ufmod.inc @@ -0,0 +1,241 @@ +; uFMOD header file +; Target OS: KolibriOS +; Compiler: MASM32 + +; HANDLE uFMOD_LoadSong( +; void *lpXM, +; void *param, +; int fdwSong +; ) +; --- +; Description: +; --- +; Loads the given XM song and starts playing it as soon as you +; call uFMOD_WaveOut for the first time. Playback won't begin +; if XM_SUSPENDED flag is specified. It will stop any currently +; playing song before loading the new one. +; --- +; Parameters: +; --- +; lpXM +; Specifies the song to load. If this parameter is 0, any +; currently playing song is stopped. In such a case, function +; does not return a meaningful value. fdwSong parameter +; determines whether this value is interpreted as a filename +; or as a pointer to an image of the song in memory. +; param +; If XM_MEMORY is specified, this parameter should be the size +; of the image of the song in memory. +; If XM_FILE is specified, this parameter is ignored. +; fdwSong +; Flags for playing the song. The following values are defined: +; XM_FILE lpXM points to filename. param is ignored. +; XM_MEMORY lpXM points to an image of a song in memory. +; param is the image size. Once, uFMOD_LoadSong +; returns, it's safe to free/discard the memory +; buffer. +; XM_NOLOOP An XM track plays repeatedly by default. Specify +; this flag to play it only once. +; XM_SUSPENDED The XM track is loaded in a suspended state, +; and will not play until the uFMOD_Resume function +; is called. This is useful for preloading a song +; or testing an XM track for validity. +; --- +; Return Values: +; --- +; On success, returns the handle of the Infinity Sound driver. +; Returns 0 on failure. +uFMOD_LoadSong PROTO C :DWORD,:DWORD,:DWORD + +; int uFMOD_WaveOut(void) +; --- +; Description: +; --- +; Updates the internal playback buffer. +; --- +; Remarks: +; --- +; This function should be called from the same thread +; uFMOD_LoadSong was previously called. Playback doesn't actually +; begin when calling uFMOD_LoadSong, but when calling uFMOD_WaveOut +; after a successful uFMOD_LoadSong call. Afterwards, you should +; call uFMOD_WaveOut repeatedly at least once every 250 ms to +; prevent "buffer underruns". +; uFMOD_WaveOut is a non-blocking function. The accuracy of the +; InfoAPI functions (uFMOD_GetStats, uFMOD_GetRowOrder and +; uFMOD_GetTime) depends on the periodicity of this function being +; invoked. +; --- +; Return Values: +; --- +; Returns non zero on error. +uFMOD_WaveOut PROTO C + +; void uFMOD_StopSong(void) +; --- +; Description: +; --- +; Stops the currently playing song, freeing the associated +; resources. +; --- +; Remarks: +; --- +; Does nothing if no song is playing at the time the call is made. +uFMOD_StopSong PROTO C + +; void uFMOD_Jump2Pattern( +; unsigned int pat +; ) +; --- +; Description: +; --- +; Jumps to the specified pattern index. +; --- +; Parameters: +; --- +; pat +; Next zero based pattern index. +; --- +; Remarks: +; --- +; uFMOD doesn't automatically perform Note Off effects before jumping +; to the target pattern. In other words, the original pattern will +; remain in the mixer until it fades out. You can use this feature to +; your advantage. If you don't like it, just insert leading Note Off +; commands in all patterns intended to be used as uFMOD_Jump2Pattern +; targets. +; if the pattern index lays outside of the bounds of the pattern order +; table, calling this function jumps to pattern 0, effectively +; rewinding playback. +uFMOD_Jump2Pattern PROTO C :DWORD + +; void uFMOD_Pause(void) +; --- +; Description: +; --- +; Pauses the currently playing song, if any. +; --- +; Remarks: +; --- +; While paused you can still control the volume (uFMOD_SetVolume) and +; the pattern order (uFMOD_Jump2Pattern). The RMS volume coefficients +; (uFMOD_GetStats) will go down to 0 and the progress tracker +; (uFMOD_GetTime) will "freeze" while the song is paused. +; uFMOD_Pause doesn't perform the request immediately. Instead, it +; signals to pause when playback reaches next chunk of data. +; This way, uFMOD_Pause performs asynchronously and returns very fast. +; It is not cumulative. So, calling uFMOD_Pause many times in a row +; has the same effect as calling it once. +; You shouldn't stop calling uFMOD_WaveOut while the song is paused! +uFMOD_Pause PROTO C + +; void uFMOD_Resume(void) +; --- +; Description: +; --- +; Resumes the currently paused song, if any. +; --- +; Remarks: +; --- +; uFMOD_Resume doesn't perform the request immediately. Instead, it +; signals to resume when uFMOD_WaveOut is called again. uFMOD_Resume +; is not cumulative. So, calling it many times in a row has the same +; effect as calling it once. +uFMOD_Resume PROTO C + +; unsigned int uFMOD_GetStats(void) +; --- +; Description: +; --- +; Returns the current RMS volume coefficients in (L)eft and (R)ight +; channels. +; low-order word: RMS volume in R channel +; hi-order word: RMS volume in L channel +; Range from 0 (silence) to $7FFF (maximum) on each channel. +; --- +; Remarks: +; --- +; This function is useful for updating a VU meter. It's recommended +; to rescale the output to log10 (decibels or dB for short), because +; human ears track volume changes in a dB scale. You may call +; uFMOD_GetStats() as often as you like, but take in mind that uFMOD +; updates both channel RMS volumes at the same rate uFMOD_WaveOut +; function is called. In other words, you should call uFMOD_WaveOut +; more often to increase the accuracy of uFMOD_GetStats. +uFMOD_GetStats PROTO C + +; unsigned int uFMOD_GetRowOrder(void) +; --- +; Description: +; --- +; Returns the currently playing row and order. +; low-order word: row +; hi-order word: order +; --- +; Remarks: +; --- +; This function is useful for synchronization. uFMOD updates both +; row and order values at the same rate uFMOD_WaveOut function is +; called. In other words, you should call uFMOD_WaveOut more often +; to increase the accuracy of uFMOD_GetRowOrder. +uFMOD_GetRowOrder PROTO C + +; unsigned int uFMOD_GetTime(void) +; --- +; Description: +; --- +; Returns the time in milliseconds since the song was started. +; --- +; Remarks: +; --- +; This function is useful for synchronizing purposes. Multimedia +; applications can use uFMOD_GetTime to synchronize GFX to sound, +; for example. An XM player can use this function to update a progress +; meter. +uFMOD_GetTime PROTO C + +; unsigned char* uFMOD_GetTitle(void) +; --- +; Description: +; --- +; Returns the current song's title. +; --- +; Remarks: +; --- +; Not every song has a title, so be prepared to get an empty string. +uFMOD_GetTitle PROTO C + +; void uFMOD_SetVolume( +; unsigned int vol +; ) +; --- +; Description: +; --- +; Sets the global volume. The volume scale is linear. +; --- +; Parameters: +; --- +; vol +; New volume. Range: from uFMOD_MIN_VOL (muting) to uFMOD_MAX_VOL +; (maximum volume). Any value above uFMOD_MAX_VOL maps to maximum +; volume. +; --- +; Remarks: +; --- +; uFMOD internally converts the given values to a logarithmic scale (dB). +; Maximum volume is set by default. The volume value is preserved across +; uFMOD_LoadSong calls. You can set the desired volume level before +; actually starting to play a song. +; You can use Infinity Sound API to control the L and R channels volumes +; separately. It also has a wider range than uFMOD_SetVolume, sometimes +; allowing to amplify the sound volume as well, as opposed to +; uFMOD_SetVolume only being able to attenuate it. +uFMOD_SetVolume PROTO C :DWORD + +XM_MEMORY EQU 1 +XM_FILE EQU 2 +XM_NOLOOP EQU 8 +XM_SUSPENDED EQU 16 +uFMOD_MIN_VOL EQU 0 +uFMOD_MAX_VOL EQU 25 +uFMOD_DEFAULT_VOL EQU 25 \ No newline at end of file diff --git a/programs/develop/libraries/ufmod/Masm32/ufmod.obj b/programs/develop/libraries/ufmod/Masm32/ufmod.obj new file mode 100644 index 0000000000..49d0bddf03 Binary files /dev/null and b/programs/develop/libraries/ufmod/Masm32/ufmod.obj differ diff --git a/programs/develop/libraries/ufmod/Nasm/make.bat b/programs/develop/libraries/ufmod/Nasm/make.bat new file mode 100644 index 0000000000..7f187198d5 --- /dev/null +++ b/programs/develop/libraries/ufmod/Nasm/make.bat @@ -0,0 +1,18 @@ +@echo off +rem Compiler: NASM +rem Target OS: KolibriOS + +rem NASM Path: +SET UF_NASM=\nasm + +if not exist "%UF_NASM%\nasmw.exe" goto Err1 +"%UF_NASM%\nasmw" -fbin -t -O5 -i..\ufmodlib\src\ mini.asm +goto TheEnd + +:Err1 +echo Couldn't find nasmw.exe in %UF_NASM%\ + +:TheEnd +pause +@echo on +cls diff --git a/programs/develop/libraries/ufmod/Nasm/mini.asm b/programs/develop/libraries/ufmod/Nasm/mini.asm new file mode 100644 index 0000000000..0f38be42da --- /dev/null +++ b/programs/develop/libraries/ufmod/Nasm/mini.asm @@ -0,0 +1,225 @@ +; MINI.ASM +; -------- +; Minimalistic uFMOD usage example. + +; Shows how to play an XM track in memory, +; including proper error handling. + +BITS 32 +org 0 +db "MENUET01" +dd 1 +dd START ; Entry point +dd uFMOD_IMG_END ; End of code and initialized data +dd MEMORY_END ; End of uninitialized (BSS) data +dd STACK_B ; Bottom of the stack +dd 0 ; Args +dd 0 ; Reserved + +; uFMOD setup: +%define f48000 ; Set sampling rate to 48KHz (22050, 44100, 48000) +%define STRONG ; Select STRONG interpolation (NONE, WEAK, STRONG) +%define UNSAFE ; Select UNSAFE mode (NORMAL, UNSAFE) +%define NODEBUG ; Skip debug-board messages +%define NOLINKER ; Select "no linker" mode + +; uFMOD constants: +%define uFMOD_MIN_VOL 0 +%define uFMOD_MAX_VOL 25 +%define uFMOD_DEFAULT_VOL 25 + +; The XM track. +xm incbin "..\ufmodlib\media\mini.xm" +xm_length equ $ - xm + +; Optimization: +; This header file is suitable for mini.xm track only! +; If you change the track, update the optimization header. +; (Use the standart eff.inc file for a general purpose player app.) +%include "..\ufmodlib\media\mini.eff.inc" + +; UI text messages. +msg_txt db "uFMOD ruleZ!" +msg_txt_l equ $ - msg_txt +msg_cap db "NASM",0 +err_txt db "Error" +err_txt_l equ $ - err_txt +err_cap db ":-(",0 + +START: + ; Start playback. + push XM_MEMORY + push xm_length + push xm + call _uFMOD_LoadSong + + ; Stack fixing is required here, but in this simple + ; example leaving ESP as it is won't harm. In a real + ; application you should uncomment the following line: + ; add esp,12 + + test eax,eax + jz error + + ; Wait for user input. + push _uFMOD_WaveOut ; cbProc <- continue fetching data! + push msg_txt_l ; cbString + push msg_txt ; lpString + push msg_cap ; szCap + call _MessageBoxCB + ; add esp,16 + + ; Stop playback. + call _uFMOD_StopSong + +r: ; Exit. + xor eax,eax + dec eax + int 40h + +error: + push 0 ; cbProc <- no callback + push err_txt_l ; cbString + push err_txt ; lpString + push err_cap ; szCap + call _MessageBoxCB + ; add esp,16 + jmp r + +; --------------------------------------------------------------- +; void _cdecl _MessageBoxCB(szCap, lpString, cbString, cbProc); +; --------------------------------------------------------------- + +; This is similar to a Win32 MessageBox. The box is centered +; on screen. It contains a single-line text message and an +; "OK" button. This function returns when user closes the +; box (via the X button or via the OK button). An optional +; callback subroutine may be specified to be called when no +; events are pending in the event queue. + +; NOTE: Doesn't work if you already have defined a window +; in the current process! Doesn't modify the event mask. So, +; make sure keyboard events are enabled before calling this +; function. This function doesn't check the validity of the +; supplied parameters! + +; Parameters: +; szCap - A pointer to the ASCIIz string containing the +; caption. A trailing zero char IS required. +; lpString - A pointer to an ASCII string containing a single +; line message to pop up in the box. No trailing +; zero char is required. +; cbString - number of characters in string. +; cbProc - Address of the callback subroutine. Can be NULL. + +sOK db "OK" +_MessageBoxCB: + push ebp + push esi + push edi + push ebx + xor ebp,ebp ; global 0 + mov esi,[esp+28] ; cbString + mov edi,[esp+20] ; szCap + + ; Get screen metrics. + lea eax,[ebp+14] + int 40h + mov ecx,eax + movzx eax,ax + shr ecx,16 ; screen w + xchg eax,edx ; screen h + lea ebx,[esi*2+esi] + lea ebx,[ebx*2+28] ; w = string len * 6 + 28 + sub ecx,ebx + shr ecx,1 + shl ecx,16 + or ebx,ecx + lea ecx,[ebp+52h] ; h = 52h + sub edx,ecx + shr edx,1 + shl edx,16 + or ecx,edx ; y = (screen h - window h) / 2 + mov edx,ebx ; x = (screen w - window w) / 2 + +_MessageBoxCB_redraw: + ; Start redraw. + push edx + lea eax,[ebp+12] + lea ebx,[ebp+1] + int 40h + + ; Define and draw window. + xor eax,eax + mov ebx,edx ; x, w (ECX: y, h) + mov edx,34C0C0C0h ; style and BG color + int 40h + + ; Define the OK button. + push esi + lea eax,[ebp+8] + sub ebx,28+0Ah + shr bx,1 + shl ebx,16 ; x = (window w - button w) / 2 + mov bx,18+0Ah ; w = 18 + 0Ah + mov ecx,001C0012h ; y = 1Ch, h = 12h + lea edx,[ebp+1] ; ID = close + mov esi,0C0C0C0h ; color + int 40h + + ; Draw the OK label. + lea eax,[ebp+4] + add ebx,90000h ; x = button x + 9 + mov bx,22h ; y = 22h + xor ecx,ecx ; style, font and color + mov edx,sOK ; string + lea esi,[ebp+2] ; length + int 40h + pop esi + + ; Draw text string. + lea eax,[ebp+4] + mov ebx,000A000Ah ; x = 0Ah, y = 0Ah + xor ecx,ecx ; style, font and color + mov edx,[esp+28] ; lpString + int 40h + + ; End redraw. + lea eax,[ebp+12] + lea ebx,[ebp+2] + int 40h + +_MessageBoxCB_eventloop: + mov edx,[esp+36] ; cbProc + test edx,edx + lea eax,[ebp+10] + jz _MessageBoxCB_peekevent + + ; Invoke the callback. + call edx + + lea eax,[ebp+23] + lea ebx,[ebp+10] ; wait for at most 0.1 sec +_MessageBoxCB_peekevent: + int 40h + dec eax + js _MessageBoxCB_eventloop + + pop edx + jz _MessageBoxCB_redraw + + pop ebx + pop edi + pop esi + pop ebp + ret + + ; Include the whole uFMOD sources here. (Right after + ; your main code to avoid naming conflicts, but still + ; inside your code section.) + %include "nasm.asm" + +alignb 4 + resb 1020 +STACK_B resd 1 ; Stack bottom +MEMORY_END: ; End of uninitialized (BSS) data diff --git a/programs/develop/libraries/ufmod/core.asm b/programs/develop/libraries/ufmod/core.asm new file mode 100644 index 0000000000..1770e3c576 --- /dev/null +++ b/programs/develop/libraries/ufmod/core.asm @@ -0,0 +1,3374 @@ +; CORE.ASM +; -------- +; uFMOD public source code release. Provided as-is. + +if VIBRATO_OR_TREMOLO +sin127 db 00h,0Ch,19h,25h,31h,3Ch,47h,51h,5Ah,62h,6Ah,70h,75h,7Ah,7Dh,7Eh + db 7Fh,7Eh,7Dh,7Ah,75h,70h,6Ah,62h,5Ah,51h,47h,3Ch,31h,25h,19h,0Ch +endif +if INSTRUMENTVIBRATO_ON +sin64 db 00h,02h,03h,05h,06h,08h,09h,0Bh,0Ch,0Eh,10h,11h,13h,14h,16h,17h + db 18h,1Ah,1Bh,1Dh,1Eh,20h,21h,22h,24h,25h,26h,27h,29h,2Ah,2Bh,2Ch + db 2Dh,2Eh,2Fh,30h,31h,32h,33h,34h,35h,36h,37h,38h,38h,39h,3Ah,3Bh + db 3Bh,3Ch,3Ch,3Dh,3Dh,3Eh,3Eh,3Eh,3Fh,3Fh,3Fh,40h,40h,40h,40h,40h +endif +if AMIGAPERIODS_ON +f0_0833 dd 8.3333336e-2 +f13_375 dd 1.3375e1 +endif +f0_0013 dd 1.302083375e-3 +f8363_0 dd 8.3630004275e3 + +; Mixer ramping +Ramp: +; [arg0] - ptr. to end of buffer +; ESI - _mod+36 +; EDI - length +; EDX - buffer + ; LOOP THROUGH CHANNELS + mov ecx,[esi+FMUSIC_MODULE.Channels-36] + push ebx + push ebp +loop_ch: + push esi + mov esi,[ecx+FSOUND_CHANNEL.fsptr] ; load the correct SAMPLE pointer for this channel + test esi,esi ; if(!fsptr) skip this channel! + jz MixExit_1 + push edx ; mix buffer + push edx ; cur. mix buffer pointer + mov ebx,[ecx+FSOUND_CHANNEL.mixpos] + ; Set up a mix counter. See what will happen first, will the output buffer + ; end be reached first? or will the end of the sample be reached first? whatever + ; is smallest will be the mixcount. + push edi +CalculateLoopCount: + cmp BYTE PTR [ecx+FSOUND_CHANNEL.speeddir],0 + mov edx,[esi+FSOUND_SAMPLE.loopstart] + mov eax,[ecx+FSOUND_CHANNEL.mixposlo] + jne samplesleftbackwards + ; work out how many samples left from mixpos to loop end + add edx,[esi+FSOUND_SAMPLE.looplen] + sub edx,ebx + ja submixpos + mov edx,[esi+FSOUND_SAMPLE._length] + sub edx,ebx +submixpos: + ; edx : samples left (loopstart+looplen-mixpos) + neg edx + neg eax + adc edx,ebx +samplesleftbackwards: + ; work out how many samples left from mixpos to loop start + neg edx + add edx,ebx + js MixExit + ; edx:eax now contains number of samples left to mix + mov ebp,[ecx+FSOUND_CHANNEL.speedlo] + shrd eax,edx,5 + mov ebx,[ecx+FSOUND_CHANNEL.speedhi] + shr edx,5 + shrd ebp,ebx,5 + jnz speedok ; divide by 0 check + mov ebp,FREQ_40HZ_p + mov DWORD PTR [ecx+FSOUND_CHANNEL.speedlo],FREQ_40HZ_f +speedok: + div ebp + xor ebp,ebp + neg edx + adc eax,ebp + jz DoOutputbuffEnd + mov ebx,OFFSET uFMOD_fopen + cmp eax,edi + ; set a flag to say mix will end when end of output buffer is reached + seta [ebx-22] ; mix_endflag + jae staywithoutputbuffend + xchg eax,edi +staywithoutputbuffend: +if RAMP_NONE +else + movzx eax,WORD PTR [ecx+FSOUND_CHANNEL.ramp_count] + ; VOLUME RAMP SETUP + ; Reasons to ramp + ; 1 volume change + ; 2 sample starts (just treat as volume change - 0 to volume) + ; 3 sample ends (ramp last n number of samples from volume to 0) + ; now if the volume has changed, make end condition equal a volume ramp + test eax,eax +endif + mov edx,[ecx+FSOUND_CHANNEL.leftvolume] + mov ebp,[ecx+FSOUND_CHANNEL.rightvolume] +if RAMP_NONE + mov [ecx+FSOUND_CHANNEL.ramp_leftvolume],ebp + mov [ecx+FSOUND_CHANNEL.ramp_rightvolume],edx +else + mov [ebx-16],edi ; mmf+4 <- remember mix count before modifying it + jz volumerampstart + ; if it tries to continue an old ramp, but the target has changed, + ; set up a new ramp + cmp dx,[ecx+FSOUND_CHANNEL.ramp_lefttarget] + jne volumerampstart + cmp bp,[ecx+FSOUND_CHANNEL.ramp_righttarget] + je volumerampclamp ; restore old ramp +volumerampstart: + ; SETUP NEW RAMP + mov [ecx+FSOUND_CHANNEL.ramp_lefttarget],dx + shl edx,volumeramps_pow + sub edx,[ecx+FSOUND_CHANNEL.ramp_leftvolume] + xor eax,eax + sar edx,volumeramps_pow + mov DWORD PTR [ecx+FSOUND_CHANNEL.ramp_leftspeed],edx + jz novolumerampL + mov al,volumerampsteps +novolumerampL: + mov [ecx+FSOUND_CHANNEL.ramp_righttarget],bp + shl ebp,volumeramps_pow + sub ebp,[ecx+FSOUND_CHANNEL.ramp_rightvolume] + sar ebp,volumeramps_pow + mov DWORD PTR [ecx+FSOUND_CHANNEL.ramp_rightspeed],ebp + jz novolumerampR + mov al,volumerampsteps +novolumerampR: + test eax,eax + mov [ecx+FSOUND_CHANNEL.ramp_count],ax + jz volumerampend +volumerampclamp: + cmp edi,eax + jbe volumerampend ; dont clamp mixcount + mov edi,eax +volumerampend: + mov eax,[ecx+FSOUND_CHANNEL.ramp_leftspeed] + mov [ebx],eax ; ramp_leftspeed + mov eax,[ecx+FSOUND_CHANNEL.ramp_rightspeed] + mov [ebx+4],eax ; ramp_rightspeed +endif + mov [ebx-20],edi ; mmf + ; SET UP ALL OF THE REGISTERS HERE FOR THE INNER LOOP + ; edx : speed + ; ebx : mixpos + ; ebp : speed low + ; esi : destination pointer + ; edi : counter + mov ebx,[ecx+FSOUND_CHANNEL.mixpos] + lea ebx,[ebx*2+esi+FSOUND_SAMPLE.buff] + push esi + cmp BYTE PTR [ecx+FSOUND_CHANNEL.speeddir],0 + mov esi,[esp+8] ; <- cur. mix buffer + mov edx,[ecx+FSOUND_CHANNEL.speedhi] + mov ebp,[ecx+FSOUND_CHANNEL.speedlo] + je MixLoop16 + ; neg edx:ebp + neg ebp + not edx + sbb edx,-1 + align 4 +MixLoop16: + push edi + push edx + movsx edi,WORD PTR [ebx] + movsx eax,WORD PTR [ebx+2] + mov edx,[ecx+FSOUND_CHANNEL.mixposlo] + sub eax,edi + shr edx,1 ; force unsigned + imul edx + shl edi,volumeramps_pow-1 + shld edx,eax,volumeramps_pow + add edx,edi +if RAMP_NONE + mov eax,edx + imul edx,[ecx+FSOUND_CHANNEL.ramp_rightvolume] + add [esi],edx + imul DWORD PTR [ecx+FSOUND_CHANNEL.ramp_leftvolume] + add [esi+4],eax +else + xchg eax,edx + mov edi,eax + imul DWORD PTR [ecx+FSOUND_CHANNEL.ramp_rightvolume] + shrd eax,edx,volumeramps_pow-1 + rol edx,1 + and edx,1 + add eax,edx + sar eax,1 + add [esi],eax + xchg eax,edi + imul DWORD PTR [ecx+FSOUND_CHANNEL.ramp_leftvolume] + shrd eax,edx,volumeramps_pow-1 + rol edx,1 + and edx,1 + add eax,edx + mov edi,[uFMOD_fread] + sar eax,1 + add [esi+4],eax + mov edx,[uFMOD_fopen] + add [ecx+FSOUND_CHANNEL.ramp_rightvolume],edi ; + cur_ramp_rightspeed + add [ecx+FSOUND_CHANNEL.ramp_leftvolume],edx ; + cur_ramp_leftspeed +endif + pop edx + xor eax,eax + add [ecx+FSOUND_CHANNEL.mixposlo],ebp + pop edi + adc eax,edx + add esi,8 + dec edi + lea ebx,[ebx+eax*2] + jnz MixLoop16 + mov edi,[esp+20h] ; find out how many OUTPUT samples left to mix + mov [esp+8],esi ; update cur. mix buffer + sub edi,esi + shr edi,3 ; edi <- # of samples left + pop esi ; esi <- sample pointer + lea eax,[esi+FSOUND_SAMPLE.buff] + sub ebx,eax + shr ebx,1 + mov [ecx+FSOUND_CHANNEL.mixpos],ebx +if RAMP_NONE + xor edx,edx +else + ; DID A VOLUME RAMP JUST HAPPEN? + movzx edx,WORD PTR [ecx+FSOUND_CHANNEL.ramp_count] + test edx,edx + jz DoOutputbuffEnd + mov eax,[mmf] + cdq + sub [ecx+FSOUND_CHANNEL.ramp_count],ax + ; if(!rampcount) a ramp has FINISHED, so finish the rest of the mix + jnz DoOutputbuffEnd + sub eax,[mmf+4] + ; clear out the ramp speeds + mov [ecx+FSOUND_CHANNEL.ramp_leftspeed],edx + neg eax + mov [ecx+FSOUND_CHANNEL.ramp_rightspeed],edx + ; is it 0 because ramp ended only? or both ended together? + ; if sample ended together with ramp... problems... loop isn't handled + sbb edx,edx + ; start again and continue rest of mix + test edi,edx + jnz CalculateLoopCount ; dont start again if nothing left + xor edx,edx +endif +DoOutputbuffEnd: + cmp [mix_endflag],dl + jne MixExit + movzx eax,BYTE PTR [esi+FSOUND_SAMPLE.loopmode] + ; SWITCH ON LOOP MODE TYPE + dec eax ; check for normal loop (FSOUND_LOOP_NORMAL = 1) + jnz CheckBidiLoop + mov eax,[esi+FSOUND_SAMPLE.loopstart] + mov ebp,[esi+FSOUND_SAMPLE.looplen] + add eax,ebp + cmp ebx,eax + jbe rewind_ok + sub ebx,eax + xchg eax,ebx + div ebp +rewind_ok: + sub ebp,edx + sub ebx,ebp + jmp ChkLoop_OK +CheckBidiLoop: + dec eax ; FSOUND_LOOP_BIDI = 2 + neg eax + adc edx,-1 + and [ecx+FSOUND_CHANNEL.mixposlo],edx + and [ecx+FSOUND_CHANNEL.mixpos],edx + and [ecx+FSOUND_CHANNEL.fsptr],edx + jz MixExit + cmp [ecx+FSOUND_CHANNEL.speeddir],al ; FSOUND_MIXDIR_FORWARDS + je BidiForward +BidiBackwards: + mov eax,[esi+FSOUND_SAMPLE.loopstart] + neg ebp + dec eax + sub ebp,1 + dec BYTE PTR [ecx+FSOUND_CHANNEL.speeddir] ; set FSOUND_MIXDIR_FORWARDS + sbb eax,ebx + mov ebx,[esi+FSOUND_SAMPLE.loopstart] + add ebx,eax + cmp eax,[esi+FSOUND_SAMPLE.looplen] + jl BidiFinish +BidiForward: + mov eax,[esi+FSOUND_SAMPLE.loopstart] + add eax,[esi+FSOUND_SAMPLE.looplen] + lea edx,[eax-1] + sbb eax,ebx + neg ebp + xchg eax,ebx + sub ebp,1 + adc ebx,edx + inc BYTE PTR [ecx+FSOUND_CHANNEL.speeddir] ; go backwards + cmp ebx,[esi+FSOUND_SAMPLE.loopstart] + jl BidiBackwards +BidiFinish: + mov [ecx+FSOUND_CHANNEL.mixposlo],ebp +ChkLoop_OK: + test edi,edi + mov [ecx+FSOUND_CHANNEL.mixpos],ebx + jnz CalculateLoopCount +MixExit: + pop edi + pop eax ; discard cur. mix buffer pointer + pop edx +MixExit_1: + add ecx,FSOUND_CHANNEL_size + pop esi + cmp ecx,[esi+FMUSIC_MODULE.uFMOD_Ch-36] + jl loop_ch + pop ebp + pop ebx + ret + +if AMIGAPERIODS_ON +AmigaPeriod: +; [sptr] in ECX +; note in EAX +; ESI != 0 + mov edx,132 + push edi + sub edx,eax + push esi + test eax,eax + push edx + movsx eax,BYTE PTR [ecx+FSOUND_SAMPLE.finetune] + mov edi,edx + jz _do_inc + cdq + shl edx,1 +_do_inc: + inc edx +exp2: + fild DWORD PTR [esp] + fmul DWORD PTR [f0_0833] ; /12.0f + fld st0 + frndint + fsub st1,st0 + fxch st1 + f2xm1 + fld1 + faddp st1,st0 + fscale + fstp st1 + fmul DWORD PTR [f13_375] ; *13.375f + fistp DWORD PTR [esp] + test esi,esi + pop ecx + jz exp2_end + sub edi,edx + push edi + xor esi,esi + mov edi,ecx + jmp exp2 +exp2_end: + sub ecx,edi + test edx,edx + jns _do_imul + neg ecx +_do_imul: + imul ecx + and edx,127 ; +2^7-1 + add eax,edx + sar eax,7 + pop esi + add eax,edi + pop edi + ret +endif ; AMIGAPERIODS_ON + +; DESCRIPTION: To carry out a vibrato at a certain depth and speed +if VIBRATO_OR_VOLSLIDE + if TREMOLO_ON + VibratoOrTremolo: + ; cptr+2 = ESI + movzx ecx,BYTE PTR [esi+FMUSIC_CHANNEL.vibpos-2] + mov al,[esi+FMUSIC_CHANNEL.wavecontrol-2] + mov edx,ecx + add dl,[esi+FMUSIC_CHANNEL.vibspeed-2] + and edx,3Fh + and eax,3 ; switch(cptr->wavecontrol&3) + mov [esi+FMUSIC_CHANNEL.vibpos-2],dl + jz vibrato_c0 + ; C2 : Sqare wave + rol ecx,27 + sbb edx,edx + xor edx,7Fh + or edx,1 + dec eax + jnz vibrato_default + ; C1 : Triangle wave (ramp down) + shr edx,24 + ror ecx,23 + add edx,ecx + neg edx + jmp vibrato_default + vibrato_c0: + ; C0 : Sine wave + ; delta = 127 sin(2 Pi x/64) + mov eax,ecx + and ecx,1Fh + shr eax,6 + movzx edx,BYTE PTR [OFFSET sin127+ecx] + sbb eax,eax + xor edx,eax + sub edx,eax + vibrato_default: + movsx eax,BYTE PTR [esi+FMUSIC_CHANNEL.vibdepth-2] + imul edx ; delta *= cptr->vibdepth + sar eax,5 + ret + endif + +Vibrato: +; cptr+2 = ESI + if TREMOLO_ON + call VibratoOrTremolo + sar eax,1 + else + movzx ecx,BYTE PTR [esi+FMUSIC_CHANNEL.vibpos-2] + mov al,[esi+FMUSIC_CHANNEL.wavecontrol-2] + mov edx,ecx + add dl,[esi+FMUSIC_CHANNEL.vibspeed-2] + and edx,3Fh + and eax,3 ; switch(cptr->wavecontrol&3) + mov [esi+FMUSIC_CHANNEL.vibpos-2],dl + jz vibrato_c0 + ; C2 : Sqare wave + rol ecx,27 + sbb edx,edx + xor edx,7Fh + or edx,1 + dec eax + jnz vibrato_default + ; C1 : Triangle wave (ramp down) + shr edx,24 + ror ecx,23 + add edx,ecx + neg edx + jmp vibrato_default + vibrato_c0: + ; C0 : Sine wave + ; delta = 127 sin(2 Pi x/64) + mov eax,ecx + and ecx,1Fh + shr eax,6 + movzx edx,BYTE PTR [OFFSET sin127+ecx] + sbb eax,eax + xor edx,eax + sub edx,eax + vibrato_default: + movsx eax,BYTE PTR [esi+FMUSIC_CHANNEL.vibdepth-2] + imul edx ; delta *= cptr->vibdepth + sar eax,6 + endif + mov [esi+FMUSIC_CHANNEL.freqdelta-2],eax + or BYTE PTR [esi+FMUSIC_CHANNEL.notectrl-2],FMUSIC_FREQ + ret +endif ; VIBRATO_OR_VOLSLIDE + +if TREMOLO_ON +Tremolo: +; cptr+2 = ESI + if VIBRATO_OR_VOLSLIDE + push esi + add esi,FMUSIC_CHANNEL.tremolopos-FMUSIC_CHANNEL.vibpos + call VibratoOrTremolo + pop esi + else + movzx ecx,BYTE PTR [esi+FMUSIC_CHANNEL.tremolopos-2] + mov al,[esi+FMUSIC_CHANNEL.wavecontrol-2] + mov edx,ecx + add dl,[esi+FMUSIC_CHANNEL.tremolospeed-2] + and edx,3Fh + and eax,3 ; switch(cptr->wavecontrol&3) + mov [esi+FMUSIC_CHANNEL.tremolopos-2],dl + jz tremolo_c0 + ; C2 : Sqare wave + rol ecx,27 + sbb edx,edx + xor edx,7Fh + or edx,1 + dec eax + jnz tremolo_default + ; C1 : Triangle wave (ramp down) + shr edx,24 + ror ecx,23 + add edx,ecx + neg edx + jmp tremolo_default + tremolo_c0: + ; C0 : Sine wave + ; delta = 127 sin(2 Pi x/64) + mov eax,ecx + and ecx,1Fh + shr eax,6 + movzx edx,BYTE PTR [OFFSET sin127+ecx] + sbb eax,eax + xor edx,eax + sub edx,eax + tremolo_default: + movsx eax,BYTE PTR [esi+FMUSIC_CHANNEL.tremolodepth-2] + imul edx + sar eax,5 + endif + mov [esi+FMUSIC_CHANNEL.voldelta-2],eax + or BYTE PTR [esi+FMUSIC_CHANNEL.notectrl-2],FMUSIC_VOLUME + ret +endif ; TREMOLO_ON + +if PORTATO_OR_VOLSLIDE +Portamento: +; cptr+2 = ESI + or BYTE PTR [esi+FMUSIC_CHANNEL.notectrl-2],FMUSIC_FREQ + mov eax,[esi+FMUSIC_CHANNEL.freq-2] + mov ecx,[esi+FMUSIC_CHANNEL.portatarget-2] + movzx edx,BYTE PTR [esi+FMUSIC_CHANNEL.portaspeed-2] + shl edx,2 + sub eax,ecx + jg porta_do_sub + add eax,edx + jg _do_trim + jmp _no_trim +porta_do_sub: + sub eax,edx + jl _do_trim +_no_trim: + add ecx,eax +_do_trim: + mov [esi+FMUSIC_CHANNEL.freq-2],ecx + ret +endif ; PORTATO_OR_VOLSLIDE + +if VOLUME_OR_PANENVELOPE +Envelope: +; cptr+2 = ESI +; env_iptr = ECX +; control = AL +env_type equ -4 +envstopped equ -8 +envdelta equ -12 +env_value equ -16 +valfrac equ -20 +; env_tick = -24 +sustain_l2 equ -26 +sustain_l1 equ -27 +sustain_loop equ -28 +env_next equ -32 +env_pos equ -36 + push edi + push ebx + push ebp + mov ebp,esp + ; Initialize local vars with PAN/VOL data + lea edi,[ecx+FMUSIC_INSTRUMENT.PANPoints] + xor ebx,ebx +if PANENVELOPE_ON + mov edx,DWORD PTR [edi+FMUSIC_INSTRUMENT.PANsustain-FMUSIC_INSTRUMENT.PANPoints] + mov [ebp+sustain_loop],edx ; load PANsustain, PANLoopStart and PANLoopEnd + mov cl,BYTE PTR [edi+FMUSIC_INSTRUMENT.PANtype-FMUSIC_INSTRUMENT.PANPoints] + movzx edx,BYTE PTR [edi+FMUSIC_INSTRUMENT.PANnumpoints-FMUSIC_INSTRUMENT.PANPoints] +endif + or [esi+FMUSIC_CHANNEL.notectrl-2],al ; cptr->notectrl |= control +if PANENVELOPE_ON + if VOLUMEENVELOPE_ON + cmp al,FMUSIC_VOLUME ; is it FMUSIC_VOLUME or FMUSIC_PAN? + endif + lea eax,[esi+FMUSIC_CHANNEL.envpanstopped-2] +endif +if VOLUMEENVELOPE_ON + if PANENVELOPE_ON + jnz pan_or_vol_ok + endif + ; control = FMUSIC_VOLUME + add ebx,FMUSIC_CHANNEL.envvol-FMUSIC_CHANNEL.envpan + mov eax,DWORD PTR [edi+FMUSIC_INSTRUMENT.VOLsustain-FMUSIC_INSTRUMENT.PANPoints] + mov [ebp+sustain_loop],eax ; load VOLsustain, VOLLoopStart and VOLLoopEnd + mov cl,BYTE PTR [edi+FMUSIC_INSTRUMENT.VOLtype-FMUSIC_INSTRUMENT.PANPoints] + movzx edx,BYTE PTR [edi+FMUSIC_INSTRUMENT.VOLnumpoints-FMUSIC_INSTRUMENT.PANPoints] + lea eax,[esi+FMUSIC_CHANNEL.envvolstopped-2] + add edi,FMUSIC_INSTRUMENT.VOLPoints-FMUSIC_INSTRUMENT.PANPoints +pan_or_vol_ok: +endif + cmp BYTE PTR [eax],dh + jne goto_envelope_ret + push ecx ; -> env_type + push eax ; -> envstopped + lea ecx,[esi+ebx+FMUSIC_CHANNEL.envpanpos-2] + lea eax,[esi+ebx+FMUSIC_CHANNEL.envpandelta-2] + push eax ; -> envdelta + lea eax,[esi+ebx+FMUSIC_CHANNEL.envpan-2] + push eax ; -> env_value + lea eax,[esi+ebx+FMUSIC_CHANNEL.envpanfrac-2] + lea ebx,[esi+ebx+FMUSIC_CHANNEL.envpantick-2] + push eax ; -> valfrac + mov eax,[ecx] + cmp eax,edx ; if(*pos>=numpoints) envelop out of bound + push ebx ; -> env_tick + jge envelope_done + movzx eax,WORD PTR [edi+eax*4] + cmp [ebx],eax ; if(*tick == points[(*pos)<<1]) we are at the correct tick for the position + jnz add_envdelta + test BYTE PTR [ebp+env_type],FMUSIC_ENVELOPE_LOOP + jz loop_ok + movzx eax,BYTE PTR [ebp+sustain_l2] + cmp [ecx],eax + jnz loop_ok ; if((type&FMUSIC_ENVELOPE_LOOP) && *pos == loopend) handle loop + movzx eax,BYTE PTR [ebp+sustain_l1] + mov [ecx],eax ; *pos = loopstart + movzx eax,WORD PTR [edi+eax*4] + mov [ebx],eax ; *tick = points[(*pos)<<1] +loop_ok: + mov eax,[ecx] + mov [ebp+env_pos],eax + lea eax,[edi+eax*4] + dec edx + movzx ebx,WORD PTR [eax] ; get tick at this point + cmp [ecx],edx + mov edx,[eax+4] + mov edi,edx + movzx eax,WORD PTR [eax+2] + mov [ebp+env_next],edx ; get tick at next point + mov edx,[ebp+env_value] + mov [edx],eax ; *value = points[(currpos<<1)+1] + jne env_continue + ; if it is at the last position, abort the envelope and continue last value + mov eax,[ebp+envstopped] + inc BYTE PTR [eax] ; *envstopped = TRUE +goto_envelope_ret: + jmp Envelope_Ret +env_continue: + shl eax,16 + sub edi,eax + xchg eax,edi + xor ax,ax + ; sustain + test BYTE PTR [ebp+env_type],FMUSIC_ENVELOPE_SUSTAIN + jz not_sustain + movzx edx,BYTE PTR [ebp+sustain_loop] + cmp [ebp+env_pos],edx + jne not_sustain + cmp BYTE PTR [esi+FMUSIC_CHANNEL.keyoff-2],al + je Envelope_Ret +not_sustain: + ; interpolate 2 points to find delta step + inc DWORD PTR [ecx] ; (*pos)++ + mov ecx,[ebp+valfrac] + mov [ecx],edi ; *valfrac = curr + mov edi,[ebp+envdelta] + movzx ecx,WORD PTR [ebp+env_next] + and DWORD PTR [edi],0 ; *envdelta = 0 + sub ecx,ebx + jz envelope_done + cdq + idiv ecx + mov [edi],eax ; *envdelta = (next-curr)/(nexttick-currtick) + jmp envelope_done +add_envdelta: + ; interpolate + mov eax,[ebp+envdelta] + mov ecx,[eax] + mov eax,[ebp+valfrac] + add [eax],ecx ; *valfrac += *envdelta +envelope_done: + pop edx ; <- env_tick + pop eax ; <- valfrac + pop ecx ; <- env_value + mov eax,[eax] + inc DWORD PTR [edx] ; (*tick)++ + sar eax,16 + mov [ecx],eax ; *value = *valfrac >> 16 +Envelope_Ret: + leave + pop ebx + pop edi + ret +endif ; VOLUME_OR_PANENVELOPE + +if VOLUMEBYTE_ON +VolByte: +; volume = EDX +; cptr+2 = ESI + sub edx,16 + jb switch_volume + cmp edx,40h + ja switch_volume + ; if(volume >= 0x10 && volume <= 0x50) + mov [esi+FMUSIC_CHANNEL.volume-2],edx +switch_volume: + mov eax,edx + and edx,0Fh + shr eax,4 ; switch(volume>>4) + sub eax,5 + jz case_6 + dec eax + jz case_7 + dec eax + jz case_6 + dec eax + jz case_7 + sub eax,2 + jbe case_AB + dec eax + jz case_C + dec eax + jz case_D + dec eax + jz case_E + dec eax + jnz vol_default + ; case 0xF + test edx,edx + jz vol_z + shl dl,4 + mov [esi+FMUSIC_CHANNEL.portaspeed-2],dl +vol_z: + and BYTE PTR [esi+FMUSIC_CHANNEL.notectrl-2],NOT_FMUSIC_TRIGGER +if PORTATO_OR_VOLSLIDE + mov eax,[esi+FMUSIC_CHANNEL.period-2] + mov [esi+FMUSIC_CHANNEL.portatarget-2],eax ; cptr->portatarget = cptr->period +endif +vol_default: + ret +case_6: ; / case 8 + neg edx +case_7: ; / case 9 + add [esi+FMUSIC_CHANNEL.volume-2],edx + ret +case_AB: + mov [esi+eax+FMUSIC_CHANNEL.vibspeed-1],dl + ret +case_C: + shl edx,4 + mov [esi+FMUSIC_CHANNEL.pan-2],edx + xchg eax,edx +case_D: + neg edx +case_E: + add [esi+FMUSIC_CHANNEL.pan-2],edx + or BYTE PTR [esi+FMUSIC_CHANNEL.notectrl-2],FMUSIC_PAN + ret +endif ; VOLUMEBYTE_ON + +if TREMOR_ON +Tremor: +; cptr+2 = ESI + or BYTE PTR [esi+FMUSIC_CHANNEL.notectrl-2],FMUSIC_VOLUME + mov dx,WORD PTR [esi+FMUSIC_CHANNEL.tremorpos-2] + cmp dl,dh + jbe inc_pos + mov ecx,[esi+FMUSIC_CHANNEL.volume-2] + neg ecx + mov [esi+FMUSIC_CHANNEL.voldelta-2],ecx +inc_pos: + add dh,[esi+FMUSIC_CHANNEL.tremoroff-2] + cmp dl,dh + jbe Tremor_Ret + mov dl,-1 +Tremor_Ret: + inc dl + mov [esi+FMUSIC_CHANNEL.tremorpos-2],dl + ret +endif ; TREMOR_ON + +SetBPM: +; bpm = ECX + test ecx,ecx + mov eax,FSOUND_MixRate*5/2 + jz SetBPM_Ret + cdq + div ecx +SetBPM_Ret: + mov DWORD PTR [_mod+FMUSIC_MODULE.mixer_samplespertick],eax + ret + +; Loads an XM stream into memory. Returns non-zero on success. +LoadXM: +loadxm_count1 equ -4 +loadxm_numpat equ -8 +loadxm_fnumpat equ -12 +loadxm_count2 equ -16 +loadxm_skip equ -20 +loadxm_s0loopmode equ -38 +loadxm_s0bytes equ -42 +loadxm_s0looplen equ -48 +loadxm_s0loopstart equ -52 +loadxm_sample_2 equ -56 +loadxm_pat_size equ -63 +loadxm_pat equ -68 +loadxm_tmp29 equ -91 +loadxm_tmp27 equ -93 +loadxm_tmp equ -120 + mov eax,OFFSET _mod + push ebp + mov esi,eax + mov ebp,esp + mov edx,FMUSIC_MODULE_size + ; buf : EAX + ; size : EDX + call [uFMOD_fread] + xor ecx,ecx + mov eax,[esi+FMUSIC_MODULE.mixer_samplespertick] + push ecx ; -> loadxm_count1 + ; GOTO PATTERN DATA + lea eax,[eax+60] + ; pos : EAX + ; org : ECX + ; !org : Z + call uFMOD_lseek + add esp,-116 + push ebx + ; SAVE TRACK TITLE +if INFO_API_ON + push 20 ; a title has max. 20 chars + lea edx,[esi+17] + mov edi,OFFSET szTtl +if UCODE + xor eax,eax +endif + pop ecx +loadxm_ttl: + mov al,[edx] + inc edx + cmp al,20h ; copy only printable chars + jl loadxm_ttl_ok +if UCODE + stosw +else + stosb +endif +loadxm_ttl_ok: + dec ecx + jnz loadxm_ttl + xchg eax,ecx + stosd +else + xor eax,eax +endif + ; COUNT NUM. OF PATTERNS + movzx ecx,WORD PTR [esi+FMUSIC_MODULE.numorders] + movzx ebx,WORD PTR [esi+FMUSIC_MODULE.numpatternsmem] + neg ebx + mov edi,esi + sbb edx,edx + and ecx,edx + neg ebx + dec ecx + movzx edx,dl + mov [ebp+loadxm_fnumpat],ebx +if CHK4VALIDITY + cmp ecx,edx + ja loadxm_R +endif +loadxm_for_pats: + mov dl,[esi+FMUSIC_MODULE.orderlist] + cmp edx,eax + jbe loadxm_for_continue + xchg eax,edx +loadxm_for_continue: + inc esi + dec ecx + jns loadxm_for_pats + mov [ebp+loadxm_numpat],eax + inc eax + mov esi,edi + ; ALLOCATE THE PATTERN ARRAY (whatever is bigger: fnumpat or numpat) & CHANNEL POOL + cmp eax,ebx + jae loadxm_pats_ok2 + xchg eax,ebx +loadxm_pats_ok2: + movzx ecx,BYTE PTR [esi+FMUSIC_MODULE.numinsts] + imul ecx,FMUSIC_INSTRUMENT_size + mov [esi+FMUSIC_MODULE.numpatternsmem],ax + lea edi,[ecx+eax*FMUSIC_PATTERN_size] + mov eax,edi + sub edi,ecx + movzx ecx,BYTE PTR [esi+FMUSIC_MODULE.numchannels_xm] +if CHK4VALIDITY + cmp ecx,64 + jle loadxm_numchan_ok + xor ecx,ecx +loadxm_numchan_ok: +endif + mov [esi+FMUSIC_MODULE.numchannels],ecx + mov ebx,ecx + shl ebx,7 ; *FSOUND_CHANNEL_size*2 == *FMUSIC_CHANNEL_size +if CHK4VALIDITY + jz loadxm_R +endif + mov [ebp+loadxm_count2],ecx + lea eax,[eax+ebx*2] + ; numbytes : EAX + call alloc + lea edx,[eax+ebx] + mov [esi+FMUSIC_MODULE.Channels],eax + mov [esi+FMUSIC_MODULE.uFMOD_Ch],edx + mov ebx,FMUSIC_CHANNEL_size ; = FSOUND_CHANNEL_size*2 +loop_2: + mov BYTE PTR [eax+FSOUND_CHANNEL.speedhi],1 + mov [edx+FMUSIC_CHANNEL.cptr],eax + add eax,ebx + add edx,ebx + dec DWORD PTR [ebp+loadxm_count2] + jnz loop_2 + mov [esi],edx ; FMUSIC_MODULE.pattern + add edi,edx + movzx ecx,WORD PTR [esi+FMUSIC_MODULE.defaultbpm] + mov [esi+FMUSIC_MODULE.instrument],edi + mov edi,edx + ; bpm : ECX + call SetBPM + push 64 + movzx ecx,WORD PTR [esi+FMUSIC_MODULE.defaultspeed] + pop DWORD PTR [esi+FMUSIC_MODULE.globalvolume] + mov [esi+FMUSIC_MODULE.speed],ecx + ; ALLOCATE INSTRUMENT ARRAY + mov eax,[ebp+loadxm_fnumpat] + ; READ & UNPACK PATTERNS +loadxm_load_pats: + push 9 + lea eax,[ebp+loadxm_pat] + pop edx + ; buf : EAX + ; size : EDX + call [uFMOD_fread] + ; ALLOCATE PATTERN BUFFER + mov eax,[ebp+loadxm_pat_size] ; length of pattern & packed pattern size + mov ecx,[esi+FMUSIC_MODULE.numchannels] + cmp eax,10000h + mov [edi],eax + movzx eax,ax + jb loadxm_ldpats_continue ; skip an empty pattern +if CHK4VALIDITY + cmp eax,257 + sbb edx,edx + and eax,edx + jz loadxm_R +endif + mul ecx + mov [ebp+loadxm_count2],eax + lea eax,[eax+eax*4] ; x SIZE FMUSIC_NOTE + ; numbytes : EAX + call alloc + mov [edi+FMUSIC_PATTERN.data],eax + xchg eax,ebx +loadxm_for_rowsxchan: + push esi + mov esi,[uFMOD_fread] + xor edx,edx + lea eax,[ebp+loadxm_skip] + inc edx + ; buf : EAX + ; size : EDX + call esi ; uFMOD_fread + movzx edx,BYTE PTR [ebp+loadxm_skip] + test dl,80h + jz loadxm_noskip + and edx,1 + jz loadxm_nonote + mov eax,ebx ; &nptr->note + ; buf : EAX + ; size : EDX + call esi ; uFMOD_fread +loadxm_nonote: + test BYTE PTR [ebp+loadxm_skip],2 + jz loadxm_nonumber + xor edx,edx + lea eax,[ebx+1] ; &nptr->number + inc edx + ; buf : EAX + ; size : EDX + call esi ; uFMOD_fread +loadxm_nonumber: + test BYTE PTR [ebp+loadxm_skip],4 + jz loadxm_novolume + xor edx,edx + lea eax,[ebx+2] ; &nptr->volume + inc edx + ; buf : EAX + ; size : EDX + call esi ; uFMOD_fread +loadxm_novolume: + test BYTE PTR [ebp+loadxm_skip],8 + jz loadxm_noeffect + xor edx,edx + lea eax,[ebx+3] ; &nptr->effect + inc edx + ; buf : EAX + ; size : EDX + call esi ; uFMOD_fread +loadxm_noeffect: + test BYTE PTR [ebp+loadxm_skip],16 + jz loadxm_isnote97 + xor edx,edx + lea eax,[ebx+4] ; &nptr->eparam + inc edx + jmp loadxm_skip_read +loadxm_noskip: + test edx,edx + jz loadxm_skip_z + mov [ebx],dl +loadxm_skip_z: + lea eax,[ebx+1] + mov dl,4 +loadxm_skip_read: + ; buf : EAX + ; size : EDX + call esi ; uFMOD_fread +loadxm_isnote97: + pop esi + inc ebx + mov dl,BYTE PTR [esi+FMUSIC_MODULE.numinsts] + cmp [ebx],dl + jbe loadxm_number_ok + mov BYTE PTR [ebx],0 +loadxm_number_ok: + add ebx,4 + dec DWORD PTR [ebp+loadxm_count2] + jnz loadxm_for_rowsxchan +loadxm_ldpats_continue: + inc DWORD PTR [ebp+loadxm_count1] + mov eax,[ebp+loadxm_fnumpat] + add edi,8 + cmp eax,[ebp+loadxm_count1] + ja loadxm_load_pats + ; allocate and clean out any extra patterns + mov ecx,[ebp+loadxm_numpat] + cmp ecx,eax + jb loadxm_extrapats_ok + mov ebx,[esi] ; FMUSIC_MODULE.pattern + mov eax,[esi+FMUSIC_MODULE.numchannels] + push esi + lea esi,[ebx+ecx*FMUSIC_PATTERN_size] + lea edi,[eax+eax*4] + shl edi,6 ; numchannels*64*SIZE FMUSIC_NOTE +loadxm_for_extrapats: + dec DWORD PTR [ebp+loadxm_numpat] + mov eax,edi + mov BYTE PTR [esi],64 ; pptr->rows = 64 + ; Allocate memory for pattern buffer + ; numbytes : EAX + call alloc + mov [esi+FMUSIC_PATTERN.data],eax + sub esi,FMUSIC_PATTERN_size + mov eax,[ebp+loadxm_fnumpat] + cmp [ebp+loadxm_numpat],eax + jae loadxm_for_extrapats + pop esi +loadxm_extrapats_ok: + xor eax,eax + mov [esi+FMUSIC_MODULE.mixer_samplesleft],eax + mov [esi+FMUSIC_MODULE.tick],eax +if PATTERNDELAY_ON + lea edi,[esi+FMUSIC_MODULE.patterndelay] + stosd +else + lea edi,[esi+FMUSIC_MODULE.nextorder] +endif + stosd + stosd + ; Load instrument information + mov al,BYTE PTR [esi+FMUSIC_MODULE.numinsts] + test al,al + jz loadxm_ret1 + mov [ebp+loadxm_count1],al + mov ebx,[esi+FMUSIC_MODULE.instrument] +loadxm_for_instrs: + push 33 + lea eax,[ebp+loadxm_tmp] + pop edx + ; buf : EAX + ; size : EDX + call [uFMOD_fread] ; instrument size & name + mov esi,[ebp+loadxm_tmp] ; firstsampleoffset = tmp[0] + mov dl,[ebp+loadxm_tmp27] + sub esi,33 + test dl,dl + jz loadxm_inst_ok +if CHK4VALIDITY + xor eax,eax + cmp DWORD PTR [ebp+loadxm_tmp29],41 + sbb ecx,ecx + not ecx + or edx,ecx + cmp dl,16 + ja loadxm_R ; if(numsamples > 16) goto error +endif + mov edx,208 + lea eax,[ebx+FMUSIC_INSTRUMENT.keymap] + sub esi,edx + ; buf : EAX + ; size : EDX + call [uFMOD_fread] +loadxm_inst_ok: + xor ecx,ecx + xchg eax,esi + inc ecx ; SEEK_CUR + ; pos : EAX + ; org : ECX + ; !org : Z + call uFMOD_lseek + lea edx,[ebx+FMUSIC_INSTRUMENT.VOLfade] + xor eax,eax + mov cx,[edx] + shl ecx,1 + cmp BYTE PTR [edx+FMUSIC_INSTRUMENT.VOLnumpoints-FMUSIC_INSTRUMENT.VOLfade],2 + mov [edx],cx ; iptr->VOLfade *= 2 + jnb ladxm_voltype_ok + mov BYTE PTR [edx+FMUSIC_INSTRUMENT.VOLtype-FMUSIC_INSTRUMENT.VOLfade],al +ladxm_voltype_ok: + cmp BYTE PTR [edx+FMUSIC_INSTRUMENT.PANnumpoints-FMUSIC_INSTRUMENT.VOLfade],2 + jnb loadxm_PANtype_ok + mov BYTE PTR [edx+FMUSIC_INSTRUMENT.PANtype-FMUSIC_INSTRUMENT.VOLfade],al +loadxm_PANtype_ok: + cmp [ebp+loadxm_tmp27],al + je loadx_for_loadsamp_end + mov [ebp+loadxm_numpat],eax + mov [ebp+loadxm_fnumpat],ebx ; FMUSIC_INSTRUMENT.sample +loadxm_for_samp: + lea eax,[ebp+loadxm_sample_2] + mov edx,[ebp+loadxm_tmp29] + ; buf : EAX + ; size : EDX + call [uFMOD_fread] + mov esi,[ebp+loadxm_s0loopstart] + mov edi,[ebp+loadxm_s0looplen] + mov al,[ebp+loadxm_s0bytes] + mov ecx,eax + shr eax,4 ; sample[0].bytes >>= 4 + and al,1 ; [b 4] : 8/16 bit sample data + mov [ebp+loadxm_s0bytes],al + jz loadxm_s0bytes_ok + shr DWORD PTR [ebp+loadxm_sample_2],1 + shr esi,1 + shr edi,1 +loadxm_s0bytes_ok: + mov eax,[ebp+loadxm_sample_2] + cmp eax,esi + jg loadxm_loopstart_ok + mov esi,eax +loadxm_loopstart_ok: + lea edx,[esi+edi] + sub edx,eax + js loadxm_looplen_ok + sub edi,edx +loadxm_looplen_ok: + and ecx,3 ; [b 0-1] : loop type + jz loadxm_reset_sample + test edi,edi + jnz loadxm_s0loop_ok +loadxm_reset_sample: + xor esi,esi + xor ecx,ecx + mov edi,eax +loadxm_s0loop_ok: + mov [ebp+loadxm_s0loopstart],esi + mov [ebp+loadxm_s0looplen],edi + mov [ebp+loadxm_s0loopmode],cl + lea eax,[eax+eax+26] ; sample[0].length*2+SIZE FSOUND_SAMPLE+4 + ; numbytes : EAX + call alloc + mov ecx,[ebp+loadxm_fnumpat] + mov [ecx],eax + ; memcpy(iptr->sample[count2],sample,sizeof(FSOUND_SAMPLE)) + inc DWORD PTR [ebp+loadxm_numpat] + add DWORD PTR [ebp+loadxm_fnumpat],4 + push 5 + xchg eax,edi + mov eax,[ebp+loadxm_numpat] + pop ecx + cmp al,[ebp+loadxm_tmp27] + lea esi,[ebp+loadxm_sample_2] + rep movsd + jb loadxm_for_samp + ; Load sample data + mov [ebp+loadxm_numpat],ecx + ; ebx <- FMUSIC_INSTRUMENT.sample +loadx_for_loadsamp: + mov esi,[ebx+ecx*4] + xor eax,eax + mov edx,[esi] + mov ch,[esi+FSOUND_SAMPLE.Resved] + mov cl,[esi+FSOUND_SAMPLE.bytes] +if CHK4VALIDITY + test edx,0FFC00000h + jnz loadxm_R +endif + add esi,FSOUND_SAMPLE.buff +if ADPCM_ON + cmp ch,0ADh ; ModPlug 4-bit ADPCM + jne loadxm_regular_samp + inc edx + mov edi,esi + sar edx,1 + push ebx + push edx + lea edx,[edx+edx*2] + add edi,edx ; ptr = buff+compressed_length*3 + ; Read in the compression table + lea edx,[eax+16] ; edx = 16 + lea eax,[ebp+loadxm_sample_2] + mov ebx,eax + ; buf : EAX + ; size : EDX + call [uFMOD_fread] + ; Read in the sample data + pop edx + mov eax,edi + ; buf : EAX + ; size : EDX + call [uFMOD_fread] + ; Decompress sample data + mov edx,esi + xor ecx,ecx ; delta +loadxm_unpack_loop: + cmp edx,edi + jge loadxm_unpack_ok + mov al,[edi] + mov ah,al + and al,0Fh + xlatb + shr ah,4 + inc edi + add ch,al + mov al,ah + xlatb + add al,ch + shl eax,24 + or ecx,eax + mov [edx],ecx + shr ecx,16 ; ch <- delta + add edx,4 + jmp loadxm_unpack_loop +loadxm_unpack_ok: + pop ebx + jmp loadxm_chk_loop_bidi +loadxm_regular_samp: +endif + shl edx,cl ; sptr->length << sptr->bytes + mov eax,esi + ; buf : EAX + ; size : EDX + call [uFMOD_fread] + mov ecx,DWORD PTR [esi+FSOUND_SAMPLE._length-FSOUND_SAMPLE.buff] + lea edi,[ecx+esi] ; buff = sptr->buff+sptr->length + lea eax,[edi+ecx] ; ptr = buff+sptr->length + xor edx,edx + cmp BYTE PTR [esi+FSOUND_SAMPLE.bytes-FSOUND_SAMPLE.buff],dl + jne loadxm_16bit_ok + ; Promote to 16 bits +loadxm_to16bits: + dec eax + dec edi + dec eax + mov dh,[edi] + cmp eax,edi + mov [eax],dx + ja loadxm_to16bits + xor edx,edx +loadxm_16bit_ok: + mov eax,esi + ; Do delta conversion +loadxm_do_delta_conv: + add dx,[eax] + mov [eax],dx + dec ecx + lea eax,[eax+2] + jg loadxm_do_delta_conv + js loadxm_loops_ok +loadxm_chk_loop_bidi: + mov eax,DWORD PTR [esi+FSOUND_SAMPLE.looplen-FSOUND_SAMPLE.buff] + mov ecx,DWORD PTR [esi+FSOUND_SAMPLE.loopstart-FSOUND_SAMPLE.buff] + add eax,ecx + cmp BYTE PTR [esi+FSOUND_SAMPLE.loopmode-FSOUND_SAMPLE.buff],2 ; LOOP_BIDI + lea eax,[esi+eax*2] + jnz loadxm_chk_loop_normal + mov cx,[eax-2] + jmp loadxm_fix_loop +loadxm_chk_loop_normal: + cmp BYTE PTR [esi+FSOUND_SAMPLE.loopmode-FSOUND_SAMPLE.buff],1 ; LOOP_NORMAL + jnz loadxm_loops_ok + mov cx,WORD PTR [esi+ecx*2] +loadxm_fix_loop: + mov [eax],cx +loadxm_loops_ok: + inc DWORD PTR [ebp+loadxm_numpat] + mov ecx,[ebp+loadxm_numpat] + cmp cl,[ebp+loadxm_tmp27] + jb loadx_for_loadsamp +loadx_for_loadsamp_end: + add ebx,FMUSIC_INSTRUMENT_size + dec BYTE PTR [ebp+loadxm_count1] + jnz loadxm_for_instrs +loadxm_ret1: + inc eax +loadxm_R: + pop ebx +donote_R: + leave + ret + +DoNote: +; mod+36 = ESI +var_mod equ -4 +donote_sptr equ -8 +donote_jumpflag equ -10 +donote_porta equ -12 +donote_oldpan equ -16 +donote_currtick equ -20 +donote_oldfreq equ -24 +donote_iptr equ -28 + ; Point our note pointer to the correct pattern buffer, and to the + ; correct offset in this buffer indicated by row and number of channels + mov eax,[esi+FMUSIC_MODULE.order-36] + push ebp + movzx ebx,BYTE PTR [eax+esi+FMUSIC_MODULE.orderlist-36] + mov ebp,esp + mov eax,[esi+FMUSIC_MODULE.row-36] + lea ebx,[ecx+ebx*FMUSIC_PATTERN_size] + mov ecx,[esi+FMUSIC_MODULE.numchannels-36] +if PATTERNBREAK_ON + if PATTERNJUMP_ON + mov BYTE PTR [ebp+donote_jumpflag],ch + endif +endif + mul ecx + lea edi,[eax+eax*4] ; x SIZE FMUSIC_NOTE + push esi + add edi,[ebx+FMUSIC_PATTERN.data] ; mod->pattern[mod->orderlist[mod->order]].data+(mod->row*mod->numchannels) + sub esp,24 + ; Loop through each channel in the row + shl ecx,7 ; x FMUSIC_CHANNEL_size + jz donote_R + push esi + mov esi,[esi+FMUSIC_MODULE.uFMOD_Ch-36] + push ebx + inc esi + inc esi + add ecx,esi +donote_for_channels: + push ecx + mov bl,[edi+FMUSIC_NOTE.eparam] + mov al,[edi+FMUSIC_NOTE.effect] + and ebx,0Fh + cmp al,FMUSIC_XM_PORTATO + je donote_doporta + cmp al,FMUSIC_XM_PORTATOVOLSLIDE +donote_doporta: + setz [ebp+donote_porta] + ; First store note and instrument number if there was one + mov cl,[edi+FMUSIC_NOTE.number] + jz donote_rem_note + dec cl + js donote_rem_inst + mov [esi+FMUSIC_CHANNEL.inst-2],cl ; remember the instrument # +donote_rem_inst: + mov cl,[edi] ; get current note + dec ecx + cmp cl,96 + jae donote_rem_note + mov [esi+FMUSIC_CHANNEL.note-2],cl ; remember the note +donote_rem_note: + movzx ecx,BYTE PTR [esi+FMUSIC_CHANNEL.inst-2] + mov eax,[ebp+var_mod] + imul ecx,FMUSIC_INSTRUMENT_size + add ecx,[eax+FMUSIC_MODULE.instrument-36] + movzx eax,BYTE PTR [esi+FMUSIC_CHANNEL.note-2] + cdq + mov al,[eax+ecx+FMUSIC_INSTRUMENT.keymap] + cmp al,16 + mov [ebp+donote_iptr],ecx + jae donote_set_sptr + mov edx,[ecx+eax*4+FMUSIC_INSTRUMENT.sample] +donote_set_sptr: + test edx,edx + jnz donote_valid_sptr + mov edx,OFFSET DummySamp +donote_valid_sptr: + mov [ebp+donote_sptr],edx +if NOTEDELAY_ON + mov ecx,[esi+FMUSIC_CHANNEL.freq-2] + mov eax,[esi+FMUSIC_CHANNEL.volume-2] + mov [ebp+donote_oldfreq],ecx + mov ecx,[esi+FMUSIC_CHANNEL.pan-2] + mov [ebp+donote_currtick],eax + mov [ebp+donote_oldpan],ecx +endif +if TREMOLO_ON + ; if there is no more tremolo, set volume to volume + last tremolo delta + mov al,[edi+FMUSIC_NOTE.effect] + cmp al,FMUSIC_XM_TREMOLO + je donote_tremolo_vol + cmp BYTE PTR [esi+FMUSIC_CHANNEL.recenteffect-2],FMUSIC_XM_TREMOLO + jne donote_tremolo_vol + mov ecx,[esi+FMUSIC_CHANNEL.voldelta-2] + add [esi+FMUSIC_CHANNEL.volume-2],ecx +donote_tremolo_vol: + mov [esi+FMUSIC_CHANNEL.recenteffect-2],al +endif + xor ecx,ecx + mov [esi+FMUSIC_CHANNEL.voldelta-2],ecx + mov [esi+FMUSIC_CHANNEL.freqdelta-2],ecx + mov BYTE PTR [esi+FMUSIC_CHANNEL.notectrl-2],FMUSIC_VOLUME_OR_FREQ + ; PROCESS NOTE + mov cl,[edi] ; note + dec ecx + cmp cl,96 + jae donote_note_ok + ; get note according to relative note + movsx eax,BYTE PTR [edx+FSOUND_SAMPLE.relative] + add ecx,eax + mov eax,[ebp+var_mod] + mov [esi+FMUSIC_CHANNEL.realnote-2],cl + ; Get period according to realnote and finetune + test BYTE PTR [eax+FMUSIC_MODULE.flags-36],1 + je donote_flagsn1 + mov eax,[ebp+donote_sptr] + movsx eax,BYTE PTR [eax+FSOUND_SAMPLE.finetune] + cdq + shl ecx,6 + sub eax,edx + sar eax,1 + lea eax,[ecx+eax-7680] + neg eax +if AMIGAPERIODS_ON + jmp donote_chk_porta +donote_flagsn1: + xchg eax,ecx ; note : EAX + mov ecx,[ebp+donote_sptr] ; [sptr] : ECX + ; ESI != 0 + call AmigaPeriod +donote_chk_porta: + mov [esi+FMUSIC_CHANNEL.period-2],eax +else + mov [esi+FMUSIC_CHANNEL.period-2],eax +donote_flagsn1: + mov eax,[esi+FMUSIC_CHANNEL.period-2] +endif + ; Frequency only changes if there are no portamento effects + cmp BYTE PTR [ebp+donote_porta],0 + jne donote_freq_ok + mov [esi+FMUSIC_CHANNEL.freq-2],eax +donote_freq_ok: + or BYTE PTR [esi+FMUSIC_CHANNEL.notectrl-2],FMUSIC_TRIGGER +donote_note_ok: + ; PROCESS INSTRUMENT NUMBER + cmp BYTE PTR [edi+FMUSIC_NOTE.number],0 + je donote_zcptr_ok + mov eax,[ebp+donote_sptr] + ; DESCRIPTION: Reset current channel + push edi + push 9 + movzx ecx,BYTE PTR [eax+FSOUND_SAMPLE.defvol] + mov [esi+FMUSIC_CHANNEL.volume-2],ecx + pop ecx + movzx eax,BYTE PTR [eax+FSOUND_SAMPLE.defpan] + mov [esi+FMUSIC_CHANNEL.pan-2],eax + push 64 + xor eax,eax + pop DWORD PTR [esi+FMUSIC_CHANNEL.envvol-2] + push 32 + lea edi,[esi+FMUSIC_CHANNEL.envvoltick-2] + pop DWORD PTR [esi+FMUSIC_CHANNEL.envpan-2] + mov DWORD PTR [esi+FMUSIC_CHANNEL.fadeoutvol-2],65536 + ; memset(&cptr->envvoltick,0,36) + rep stosd + ; Retrigger tremolo and vibrato waveforms + mov cl,[esi+FMUSIC_CHANNEL.wavecontrol-2] + pop edi + cmp cl,4Fh + jge z_tremolopos_ok + mov BYTE PTR [esi+FMUSIC_CHANNEL.tremolopos-2],al ; = 0 +z_tremolopos_ok: + test cl,0Ch + jnz z_vibpos_ok + mov BYTE PTR [esi+FMUSIC_CHANNEL.vibpos-2],al ; = 0 +z_vibpos_ok: + or BYTE PTR [esi+FMUSIC_CHANNEL.notectrl-2],FMUSIC_VOLUME_OR_PAN +donote_zcptr_ok: +if VOLUMEBYTE_ON + ; PROCESS VOLUME BYTE + movzx edx,BYTE PTR [edi+FMUSIC_NOTE.uvolume] + ; volume : EDX + ; cptr+2 : ESI + call VolByte +endif + ; PROCESS KEY OFF + cmp BYTE PTR [edi],97 ; note + jae donote_set_keyoff + cmp BYTE PTR [edi+FMUSIC_NOTE.effect],FMUSIC_XM_KEYOFF + jne donote_keyoff_ok +donote_set_keyoff: + inc BYTE PTR [esi+FMUSIC_CHANNEL.keyoff-2] +donote_keyoff_ok: + ; PROCESS ENVELOPES +if VOLUMEENVELOPE_ON + mov ecx,[ebp+donote_iptr] + test BYTE PTR [ecx+FMUSIC_INSTRUMENT.VOLtype],1 + jz donote_no_voltype + mov al,FMUSIC_VOLUME + ; cptr+2 : ESI + ; env_iptr : ECX + ; control : AL + call Envelope + jmp donote_volenv_ok +donote_no_voltype: +endif + cmp BYTE PTR [esi+FMUSIC_CHANNEL.keyoff-2],0 + je donote_volenv_ok + and DWORD PTR [esi+FMUSIC_CHANNEL.envvol-2],0 +donote_volenv_ok: +if PANENVELOPE_ON + mov ecx,[ebp+donote_iptr] + test BYTE PTR [ecx+FMUSIC_INSTRUMENT.PANtype],1 + je donote_no_pantype + mov al,FMUSIC_PAN + ; cptr+2 : ESI + ; env_iptr : ECX + ; control : AL + call Envelope +donote_no_pantype: +endif + ; PROCESS VOLUME FADEOUT + cmp BYTE PTR [esi+FMUSIC_CHANNEL.keyoff-2],0 + mov ecx,[ebp+donote_iptr] + je donote_fadevol_ok + movzx eax,WORD PTR [ecx+FMUSIC_INSTRUMENT.VOLfade] + sub [esi+FMUSIC_CHANNEL.fadeoutvol-2],eax + jns donote_fadevol_ok + and DWORD PTR [esi+FMUSIC_CHANNEL.fadeoutvol-2],0 +donote_fadevol_ok: + ; PROCESS TICK 0 EFFECTS + movzx eax,BYTE PTR [edi+FMUSIC_NOTE.effect] + dec eax ; skip FMUSIC_XM_ARPEGGIO + movzx edx,BYTE PTR [edi+FMUSIC_NOTE.eparam] +if EXTRAFINEPORTA_ON + cmp al,32 +else + if TREMOR_ON + cmp al,28 + else + if MULTIRETRIG_ON + cmp al,26 + else + if PANSLIDE_ON + cmp al,24 + else + if SETENVELOPEPOS_ON + cmp al,20 + else + if GLOBALVOLSLIDE_ON + cmp al,16 + else + if SETGLOBALVOLUME_ON + cmp al,15 + else + if SETSPEED_ON + cmp al,14 + else + cmp al,13 + endif + endif + endif + endif + endif + endif + endif +endif + ja donote_s1_brk + test edx,edx + call DWORD PTR [eax*4+S1_TBL] +donote_s1_brk: +if INSTRUMENTVIBRATO_ON + push DWORD PTR [ebp+donote_iptr] +endif + push DWORD PTR [ebp+donote_sptr] + ; cptr+2 : ESI + call DoFlags + sub esi,-(FMUSIC_CHANNEL_size) + pop ecx + add edi,FMUSIC_NOTE_size + cmp esi,ecx + jl donote_for_channels + pop ebx + pop esi + leave +S1_r: + ret +S1_TBL: +if PORTAUP_OR_DOWN_ON + dd S1_C1 + dd S1_C1 +else + dd S1_r + dd S1_r +endif +if PORTATO_ON + dd S1_C3 +else + dd S1_r +endif +if VIBRATO_ON + dd S1_C4 +else + dd S1_r +endif +if PORTATOVOLSLIDE_ON + dd S1_C5 +else + dd S1_r +endif +if VIBRATOVOLSLIDE_ON + dd S1_C6 +else + dd S1_r +endif +if TREMOLO_ON + dd S1_C7 +else + dd S1_r +endif +if SETPANPOSITION_ON + dd S1_C8 +else + dd S1_r +endif +if SETSAMPLEOFFSET_ON + dd S1_C9 +else + dd S1_r +endif +if VOLUMESLIDE_ON + dd S1_C10 +else + dd S1_r +endif +if PATTERNJUMP_ON + dd S1_C11 +else + dd S1_r +endif +if SETVOLUME_ON + dd S1_C12 +else + dd S1_r +endif +if PATTERNBREAK_ON + dd S1_C13 +else + dd S1_r +endif + dd S1_C14 +if EXTRAFINEPORTA_ON + if SETSPEED_ON + dd S1_C15 + else + dd S1_r + endif + if SETGLOBALVOLUME_ON + dd S1_C16 + else + dd S1_r + endif + if GLOBALVOLSLIDE_ON + dd S1_C17 + else + dd S1_r + endif + dd S1_r ; unassigned effect ordinal [18] + dd S1_r ; unassigned effect ordinal [19] + dd S1_r ; skip FMUSIC_XM_KEYOFF + if SETENVELOPEPOS_ON + dd S1_C21 + else + dd S1_r + endif + dd S1_r ; unassigned effect ordinal [22] + dd S1_r ; unassigned effect ordinal [23] + dd S1_r ; unassigned effect ordinal [24] + if PANSLIDE_ON + dd S1_C25 + else + dd S1_r + endif + dd S1_r ; unassigned effect ordinal [26] + if MULTIRETRIG_ON + dd S1_C27 + else + dd S1_r + endif + dd S1_r ; unassigned effect ordinal [28] + if TREMOR_ON + dd S1_C29 + else + dd S1_r + endif + dd S1_r ; unassigned effect ordinal [30] + dd S1_r ; unassigned effect ordinal [31] + dd S1_r ; unassigned effect ordinal [32] + dd S1_C33 +else + if TREMOR_ON + if SETSPEED_ON + dd S1_C15 + else + dd S1_r + endif + if SETGLOBALVOLUME_ON + dd S1_C16 + else + dd S1_r + endif + if GLOBALVOLSLIDE_ON + dd S1_C17 + else + dd S1_r + endif + dd S1_r + dd S1_r + dd S1_r + if SETENVELOPEPOS_ON + dd S1_C21 + else + dd S1_r + endif + dd S1_r + dd S1_r + dd S1_r + if PANSLIDE_ON + dd S1_C25 + else + dd S1_r + endif + dd S1_r + if MULTIRETRIG_ON + dd S1_C27 + else + dd S1_r + endif + dd S1_r + dd S1_C29 + else + if MULTIRETRIG_ON + if SETSPEED_ON + dd S1_C15 + else + dd S1_r + endif + if SETGLOBALVOLUME_ON + dd S1_C16 + else + dd S1_r + endif + if GLOBALVOLSLIDE_ON + dd S1_C17 + else + dd S1_r + endif + dd S1_r + dd S1_r + dd S1_r + if SETENVELOPEPOS_ON + dd S1_C21 + else + dd S1_r + endif + dd S1_r + dd S1_r + dd S1_r + if PANSLIDE_ON + dd S1_C25 + else + dd S1_r + endif + dd S1_r + dd S1_C27 + else + if PANSLIDE_ON + if SETSPEED_ON + dd S1_C15 + else + dd S1_r + endif + if SETGLOBALVOLUME_ON + dd S1_C16 + else + dd S1_r + endif + if GLOBALVOLSLIDE_ON + dd S1_C17 + else + dd S1_r + endif + dd S1_r + dd S1_r + dd S1_r + if SETENVELOPEPOS_ON + dd S1_C21 + else + dd S1_r + endif + dd S1_r + dd S1_r + dd S1_r + dd S1_C25 + else + if SETENVELOPEPOS_ON + if SETSPEED_ON + dd S1_C15 + else + dd S1_r + endif + if SETGLOBALVOLUME_ON + dd S1_C16 + else + dd S1_r + endif + if GLOBALVOLSLIDE_ON + dd S1_C17 + else + dd S1_r + endif + dd S1_r + dd S1_r + dd S1_r + dd S1_C21 + else + if GLOBALVOLSLIDE_ON + if SETSPEED_ON + dd S1_C15 + else + dd S1_r + endif + if SETGLOBALVOLUME_ON + dd S1_C16 + else + dd S1_r + endif + dd S1_C17 + else + if SETGLOBALVOLUME_ON + if SETSPEED_ON + dd S1_C15 + else + dd S1_r + endif + dd S1_C16 + else + if SETSPEED_ON + dd S1_C15 + endif + endif + endif + endif + endif + endif + endif +endif +if PORTAUP_OR_DOWN_ON +S1_C1: + jz donote_xm_porta_end + mov [esi+FMUSIC_CHANNEL.portaupdown-2],dl +donote_xm_porta_end: + ret +endif +if PORTATO_ON +S1_C3: + jz donote_xm_portato_end + mov [esi+FMUSIC_CHANNEL.portaspeed-2],dl +donote_xm_portato_end: +if PORTATOVOLSLIDE_ON + jmp donote_xm_portavolsl_end +else + mov eax,[esi+FMUSIC_CHANNEL.period-2] + and BYTE PTR [esi+FMUSIC_CHANNEL.notectrl-2],NOT_FMUSIC_TRIGGER_OR_FRQ + mov [esi+FMUSIC_CHANNEL.portatarget-2],eax + ret +endif +endif +if PORTATOVOLSLIDE_ON +S1_C5: + jz donote_xm_portavolsl_end + mov [esi+FMUSIC_CHANNEL.volslide-2],dl +donote_xm_portavolsl_end: + mov eax,[esi+FMUSIC_CHANNEL.period-2] + and BYTE PTR [esi+FMUSIC_CHANNEL.notectrl-2],NOT_FMUSIC_TRIGGER_OR_FRQ + mov [esi+FMUSIC_CHANNEL.portatarget-2],eax + ret +endif +if VIBRATO_ON +S1_C4: + shr edx,4 + jz donote_vib_x_ok + mov [esi+FMUSIC_CHANNEL.vibspeed-2],dl +donote_vib_x_ok: + test ebx,ebx + jz donote_vib_y_ok + mov BYTE PTR [esi+FMUSIC_CHANNEL.vibdepth-2],bl +donote_vib_y_ok: +if VIBRATOVOLSLIDE_ON + xor eax,eax +else + ; cptr+2 : ESI + jmp Vibrato +endif +endif +if VIBRATOVOLSLIDE_ON +S1_C6: + jz donote_xm_vibvolsl_end + mov [esi+FMUSIC_CHANNEL.volslide-2],dl +donote_xm_vibvolsl_end: + ; cptr+2 : ESI + jmp Vibrato +endif +if TREMOLO_ON +S1_C7: + shr edx,4 + jz donote_trem_x_ok + mov [esi+FMUSIC_CHANNEL.tremolospeed-2],dl +donote_trem_x_ok: + test ebx,ebx + jz donote_trem_y_ok + mov [esi+FMUSIC_CHANNEL.tremolodepth-2],bl +donote_trem_y_ok: + ret +endif +if SETPANPOSITION_ON +if PANSLIDE_ON +else + S1_C8: + mov [esi+FMUSIC_CHANNEL.pan-2],edx + or BYTE PTR [esi+FMUSIC_CHANNEL.notectrl-2],FMUSIC_PAN + ret + endif +endif +if SETSAMPLEOFFSET_ON +S1_C9: + shl edx,8 + jz donote_soffset_ok + mov [esi+FMUSIC_CHANNEL.sampleoffset-2],edx +donote_soffset_ok: + mov ecx,[ebp+donote_sptr] + mov edx,[ecx+FSOUND_SAMPLE.loopstart] + add edx,[ecx+FSOUND_SAMPLE.looplen] + mov eax,[esi+FMUSIC_CHANNEL.sampleoffset-2] + cmp eax,edx + mov ecx,[esi+FMUSIC_CHANNEL.cptr-2] + jb donote_set_offset + xor eax,eax + and BYTE PTR [esi+FMUSIC_CHANNEL.notectrl-2],NOT_FMUSIC_TRIGGER + mov [ecx+FSOUND_CHANNEL.mixpos],eax + mov [ecx+FSOUND_CHANNEL.mixposlo],eax +donote_set_offset: + mov [ecx+FSOUND_CHANNEL.fsampleoffset],eax + ret +endif +if VOLUMESLIDE_ON +S1_C10: + jz donote_volslide_ok + mov [esi+FMUSIC_CHANNEL.volslide-2],dl +donote_volslide_ok: + ret +endif +if PATTERNJUMP_ON +S1_C11: + mov eax,[ebp+var_mod] + and DWORD PTR [eax+FMUSIC_MODULE.nextrow-36],0 + mov [eax+FMUSIC_MODULE.nextorder-36],edx +if PATTERNBREAK_ON + inc BYTE PTR [ebp+donote_jumpflag] +donote_set_nextord: +endif + movzx ecx,WORD PTR [eax+FMUSIC_MODULE.numorders-36] + cmp [eax+FMUSIC_MODULE.nextorder-36],ecx + jl donote_nextorder_ok + and DWORD PTR [eax+FMUSIC_MODULE.nextorder-36],0 +donote_nextorder_ok: + ret +endif +if PATTERNBREAK_ON +S1_C13: + shr edx,4 + lea edx,[edx+edx*4] + lea ecx,[ebx+edx*2] ; paramx*10+paramy + mov eax,[ebp+var_mod] + mov [eax+FMUSIC_MODULE.nextrow-36],ecx +if PATTERNJUMP_ON + cmp BYTE PTR [ebp+donote_jumpflag],0 + jne donote_nextorder_ok +endif + mov ecx,[eax+FMUSIC_MODULE.order-36] + inc ecx + mov [eax+FMUSIC_MODULE.nextorder-36],ecx +if PATTERNJUMP_ON + jmp donote_set_nextord +else + movzx ecx,WORD PTR [eax+FMUSIC_MODULE.numorders-36] + cmp [eax+FMUSIC_MODULE.nextorder-36],ecx + jl donote_jump_pat + and DWORD PTR [eax+FMUSIC_MODULE.nextorder-36],0 +donote_jump_pat: + ret +endif +endif +if SETSPEED_ON +S1_C15: + cmp dl,20h + mov ecx,edx + jae donote_setbpm + mov eax,[ebp+var_mod] + mov [eax+FMUSIC_MODULE.speed-36],ecx + ret +donote_setbpm: + ; bpm : ECX + jmp SetBPM +endif +if GLOBALVOLSLIDE_ON +S1_C17: + jz donote_globalvsl_ok + mov ecx,[ebp+var_mod] + mov [ecx+FMUSIC_MODULE.globalvsl-36],dl +donote_globalvsl_ok: + ret +endif +if SETENVELOPEPOS_ON +S1_C21: + test BYTE PTR [ecx+FMUSIC_INSTRUMENT.VOLtype],1 + je donote_envelope_r + lea ebx,[ecx+FMUSIC_INSTRUMENT.VOLPoints+4] + ; Search and reinterpolate new envelope position + movzx ecx,BYTE PTR [ecx+FMUSIC_INSTRUMENT.VOLnumpoints] + xor eax,eax + cmp dx,[ebx] + jbe donote_env_endwhile +donote_envwhile: + cmp eax,ecx ; if(currpos == iptr->VOLnumpoints) break + je donote_env_endwhile + inc eax + cmp dx,[ebx+eax*4] ; if(current->eparam > iptr->VOLPoints[(currpos+1)<<1]) break + ja donote_envwhile +donote_env_endwhile: + mov [esi+FMUSIC_CHANNEL.envvolpos-2],eax + ; if it is at the last position, abort the envelope and continue last volume + dec ecx + cmp eax,ecx + setnl [esi+FMUSIC_CHANNEL.envvolstopped-2] + jl donote_env_continue + movzx eax,WORD PTR [ebx+ecx*4-2] + mov [esi+FMUSIC_CHANNEL.envvol-2],eax ; cptr->envvol = iptr->VOLPoints[((iptr->VOLnumpoints-1)<<1)+1] +donote_envelope_r: + ret +donote_env_continue: + mov [esi+FMUSIC_CHANNEL.envvoltick-2],edx + mov ecx,[ebx+eax*4-4] ; get tick at this point + VOL at this point + mov edx,ecx + movzx ecx,cx + mov [ebp+donote_currtick],ecx + mov ecx,[ebx+eax*4] ; get tick at next point + VOL at next point + mov eax,ecx + movzx ecx,cx + xor dx,dx + ; interpolate 2 points to find delta step + sub ecx,[ebp+donote_currtick] + push edx + jz donote_no_tickdiff + xor ax,ax + sub eax,edx + cdq + idiv ecx + xchg eax,ecx +donote_no_tickdiff: + mov [esi+FMUSIC_CHANNEL.envvoldelta-2],ecx + mov eax,[esi+FMUSIC_CHANNEL.envvoltick-2] + sub eax,[ebp+donote_currtick] + imul ecx + pop edx + add eax,edx + mov [esi+FMUSIC_CHANNEL.envvolfrac-2],eax + sar eax,16 + mov [esi+FMUSIC_CHANNEL.envvol-2],eax + inc DWORD PTR [esi+FMUSIC_CHANNEL.envvolpos-2] + ret +endif +if PANSLIDE_ON +S1_C25: + jz donote_panslide_ok + mov [esi+FMUSIC_CHANNEL.panslide-2],dl + or BYTE PTR [esi+FMUSIC_CHANNEL.notectrl-2],FMUSIC_PAN +donote_panslide_ok: + ret +endif +if MULTIRETRIG_ON +S1_C27: + jz donote_multiretrig_ok + shr edx,4 + mov dh,bl + mov WORD PTR [esi+FMUSIC_CHANNEL.retrigx-2],dx +donote_multiretrig_ok: + ret +endif +if TREMOR_ON +S1_C29: + jz donote_do_tremor + shr edx,4 + mov dh,bl + mov WORD PTR [esi+FMUSIC_CHANNEL.tremoron-2],dx +donote_do_tremor: + ; cptr : ESI + jmp Tremor +endif +if EXTRAFINEPORTA_ON +S1_C33: + shr edx,4 + dec edx + jnz donote_paramx_n1 + test ebx,ebx + jz donote_paramy_z1 + mov [esi+FMUSIC_CHANNEL.xtraportaup-2],bl +donote_paramy_z1: + movzx eax,BYTE PTR [esi+FMUSIC_CHANNEL.xtraportaup-2] + sub [esi+FMUSIC_CHANNEL.freq-2],eax +donote_paramx_n1: + dec edx + jnz donote_paramx_n2 + test ebx,ebx + jz donote_paramy_z2 + mov [esi+FMUSIC_CHANNEL.xtraportadown-2],bl +donote_paramy_z2: + movzx eax,BYTE PTR [esi+FMUSIC_CHANNEL.xtraportadown-2] + add [esi+FMUSIC_CHANNEL.freq-2],eax +donote_paramx_n2: +endif +S2_r: + ret +S1_C14: + shr edx,4 + dec edx ; skip FMUSIC_XM_SETFILTER +if FINEPORTAUP_ON +else + dec edx +endif +if PATTERNDELAY_ON + cmp dl,13 +else + if NOTEDELAY_ON + cmp dl,12 + else + if FINEVOLUMESLIDE_ON + cmp dl,10 + else + if SETPANPOSITION16_ON + cmp dl,7 + else + if SETTREMOLOWAVE_ON + cmp dl,6 + else + if PATTERNLOOP_ON + cmp dl,5 + else + cmp dl,4 + endif + endif + endif + endif + endif +endif +if FINEPORTAUP_ON + ja S2_r +else + jae S2_r +endif +donote_do_special: + test ebx,ebx + jmp DWORD PTR [edx*4+S2_TBL] +S2_TBL: +if FINEPORTAUP_ON + dd S2_C1 +endif +if FINEPORTADOWN_ON + dd S2_C2 +else + dd S2_r +endif + dd S2_r ; skip FMUSIC_XM_SETGLISSANDO +if SETVIBRATOWAVE_ON + dd S2_C4 +else + dd S2_r +endif +if SETFINETUNE_ON + dd S2_C5 +else + dd S2_r +endif +if PATTERNDELAY_ON + if PATTERNLOOP_ON + dd S2_C6 + else + dd S2_r + endif + if SETTREMOLOWAVE_ON + dd S2_C7 + else + dd S2_r + endif + if SETPANPOSITION16_ON + dd S2_C8 + else + dd S2_r + endif + dd S2_r ; skip FMUSIC_XM_RETRIG + if FINEVOLUMESLIDE_ON + dd S2_C10 + dd S2_C11 + else + dd S2_r + dd S2_r + endif + dd S2_r ; skip FMUSIC_XM_NOTECUT + if NOTEDELAY_ON + dd S2_C13 + else + dd S2_r + endif + dd S2_C14 +else + if NOTEDELAY_ON + if PATTERNLOOP_ON + dd S2_C6 + else + dd S2_r + endif + if SETTREMOLOWAVE_ON + dd S2_C7 + else + dd S2_r + endif + if SETPANPOSITION16_ON + dd S2_C8 + else + dd S2_r + endif + dd S2_r ; skip FMUSIC_XM_RETRIG + if FINEVOLUMESLIDE_ON + dd S2_C10 + dd S2_C11 + else + dd S2_r + dd S2_r + endif + dd S2_r + dd S2_C13 + else + if FINEVOLUMESLIDE_ON + if PATTERNLOOP_ON + dd S2_C6 + else + dd S2_r + endif + if SETTREMOLOWAVE_ON + dd S2_C7 + else + dd S2_r + endif + if SETPANPOSITION16_ON + dd S2_C8 + else + dd S2_r + endif + dd S2_r + dd S2_C10 + dd S2_C11 + else + if SETPANPOSITION16_ON + if PATTERNLOOP_ON + dd S2_C6 + else + dd S2_r + endif + if SETTREMOLOWAVE_ON + dd S2_C7 + else + dd S2_r + endif + dd S2_C8 + else + if SETTREMOLOWAVE_ON + if PATTERNLOOP_ON + dd S2_C6 + else + dd S2_r + endif + dd S2_C7 + else + if PATTERNLOOP_ON + dd S2_C6 + endif + endif + endif + endif + endif +endif +if FINEPORTAUP_ON +S2_C1: + jz donote_finepup_ok + mov [esi+FMUSIC_CHANNEL.fineportaup-2],bl +donote_finepup_ok: + movzx eax,BYTE PTR [esi+FMUSIC_CHANNEL.fineportaup-2] + shl eax,2 + sub [esi+FMUSIC_CHANNEL.freq-2],eax + ret +endif +if FINEPORTADOWN_ON +S2_C2: + jz donote_finepdown_ok + mov [esi+FMUSIC_CHANNEL.fineportadown-2],bl +donote_finepdown_ok: + movzx eax,BYTE PTR [esi+FMUSIC_CHANNEL.fineportadown-2] + shl eax,2 + add [esi+FMUSIC_CHANNEL.freq-2],eax + ret +endif +if SETVIBRATOWAVE_ON +S2_C4: + and BYTE PTR [esi+FMUSIC_CHANNEL.wavecontrol-2],0F0h + or BYTE PTR [esi+FMUSIC_CHANNEL.wavecontrol-2],bl + ret +endif +if SETFINETUNE_ON +S2_C5: + mov eax,[ebp+donote_sptr] + mov [eax+FSOUND_SAMPLE.finetune],bl + ret +endif +if PATTERNLOOP_ON +S2_C6: + jnz donote_not_paramy + mov eax,[ebp+var_mod] + mov eax,[eax+FMUSIC_MODULE.row-36] + mov [esi+FMUSIC_CHANNEL.patlooprow-2],eax + ret +donote_not_paramy: + mov cl,[esi+FMUSIC_CHANNEL.patloopno-2] + dec cl + jns donote_patloopno_ok + mov ecx,ebx +donote_patloopno_ok: + mov [esi+FMUSIC_CHANNEL.patloopno-2],cl + jz donote_patloopno_end + mov eax,[esi+FMUSIC_CHANNEL.patlooprow-2] + mov ecx,[ebp+var_mod] + mov [ecx+FMUSIC_MODULE.nextrow-36],eax +donote_patloopno_end: + ret +endif +if SETTREMOLOWAVE_ON +S2_C7: + and BYTE PTR [esi+FMUSIC_CHANNEL.wavecontrol-2],0Fh + shl ebx,4 + or BYTE PTR [esi+FMUSIC_CHANNEL.wavecontrol-2],bl + ret +endif +if SETPANPOSITION16_ON +S2_C8: + shl ebx,4 + mov [esi+FMUSIC_CHANNEL.pan-2],ebx + or BYTE PTR [esi+FMUSIC_CHANNEL.notectrl-2],FMUSIC_PAN + ret +endif +if FINEVOLUMESLIDE_ON +S2_C10: + neg ebx +S2_C11: + jz donote_finevols_ok + mov [esi+FMUSIC_CHANNEL.finevslup-2],bl +donote_finevols_ok: + movsx eax,BYTE PTR [esi+FMUSIC_CHANNEL.finevslup-2] + sub [esi+FMUSIC_CHANNEL.volume-2],eax + ret +endif +if NOTEDELAY_ON +S2_C13: + mov eax,[ebp+donote_oldfreq] + mov edx,[ebp+donote_currtick] + mov [esi+FMUSIC_CHANNEL.freq-2],eax + mov eax,[ebp+donote_oldpan] + mov [esi+FMUSIC_CHANNEL.pan-2],eax + mov BYTE PTR [esi+FMUSIC_CHANNEL.notectrl-2],0 +if SETVOLUME_ON +else + mov [esi+FMUSIC_CHANNEL.volume-2],edx + ret +endif +endif +if SETVOLUME_ON +S1_C12: + mov [esi+FMUSIC_CHANNEL.volume-2],edx + ret +endif +if PATTERNDELAY_ON +S2_C14: + mov ecx,[ebp+var_mod] + imul ebx,DWORD PTR [ecx+FMUSIC_MODULE.speed-36] + mov [ecx+FMUSIC_MODULE.patterndelay-36],ebx + ret +endif + +DoEffs: +; mod+36 = ESI +; var_mod equ -4 +doeff_current equ -8 + ; Point our note pointer to the correct pattern buffer, and to the + ; correct offset in this buffer indicated by row and number of channels + mov eax,[esi+FMUSIC_MODULE.order-36] + push ebp + mov bl,[eax+esi+FMUSIC_MODULE.orderlist-36] + mov eax,[esi+FMUSIC_MODULE.row-36] + mul DWORD PTR [esi+FMUSIC_MODULE.numchannels-36] + lea eax,[eax+eax*4] ; x SIZE FMUSIC_NOTE + mov ebp,esp + ; mod->pattern[mod->orderlist[mod->order]].data+(mod->row*mod->numchannels) + add eax,[ecx+ebx*FMUSIC_PATTERN_size+FMUSIC_PATTERN.data] + push esi ; -> var_mod + push eax ; -> doeff_current + mov eax,[esi+FMUSIC_MODULE.numchannels-36] + shl eax,7 ; x FMUSIC_CHANNEL_size + push esi + mov esi,[esi+FMUSIC_MODULE.uFMOD_Ch-36] + inc esi + inc esi + add eax,esi +doeff_for_channels: + push eax + movzx edi,BYTE PTR [esi+FMUSIC_CHANNEL.inst-2] + mov edx,[ebp+var_mod] + imul edi,FMUSIC_INSTRUMENT_size + movzx eax,BYTE PTR [esi+FMUSIC_CHANNEL.note-2] + add edi,[edx+FMUSIC_MODULE.instrument-36] + cdq + mov al,[eax+edi+FMUSIC_INSTRUMENT.keymap] + cmp al,16 + jae doeff_set_sptr + mov edx,[edi+eax*4+FMUSIC_INSTRUMENT.sample] +doeff_set_sptr: + test edx,edx + jnz doeff_valid_sptr + mov edx,OFFSET DummySamp +doeff_valid_sptr: + xor ebx,ebx +if INSTRUMENTVIBRATO_ON + push edi ; iptr +endif + push edx ; sptr + mov [esi+FMUSIC_CHANNEL.voldelta-2],ebx + mov [esi+FMUSIC_CHANNEL.freqdelta-2],ebx + mov [esi+FMUSIC_CHANNEL.notectrl-2],bl + ; PROCESS ENVELOPES +if VOLUMEENVELOPE_ON + test BYTE PTR [edi+FMUSIC_INSTRUMENT.VOLtype],1 + je doeff_no_voltype + mov al,FMUSIC_VOLUME + mov ecx,edi + ; cptr+2 : ESI + ; env_iptr : ECX + ; control : AL + call Envelope +doeff_no_voltype: +endif +if PANENVELOPE_ON + test BYTE PTR [edi+FMUSIC_INSTRUMENT.PANtype],1 + je doeff_no_pantype + mov al,FMUSIC_PAN + mov ecx,edi + ; cptr+2 : ESI + ; env_iptr : ECX + ; control : AL + call Envelope +doeff_no_pantype: +endif + ; PROCESS VOLUME FADEOUT + cmp [esi+FMUSIC_CHANNEL.keyoff-2],bl + je doeff_fadevol_ok + movzx eax,WORD PTR [edi+FMUSIC_INSTRUMENT.VOLfade] + sub [esi+FMUSIC_CHANNEL.fadeoutvol-2],eax + jns doeff_fadevol_ns + mov [esi+FMUSIC_CHANNEL.fadeoutvol-2],ebx +doeff_fadevol_ns: + or BYTE PTR [esi+FMUSIC_CHANNEL.notectrl-2],FMUSIC_VOLUME +doeff_fadevol_ok: +if VOLUMEBYTE_ON + mov eax,[ebp+doeff_current] + movzx eax,BYTE PTR [eax+FMUSIC_NOTE.uvolume] + mov ecx,eax + shr eax,4 + and cl,0Fh + sub al,6 + jz doeff_case_vol6 + dec eax + jz doeff_case_vol7 +if VIBRATO_ON + sub al,4 + jz doeff_case_volB + dec eax + dec eax +else + sub al,6 +endif + jz doeff_case_volD + dec eax +if PORTATO_ON + jz doeff_case_volE + dec eax + jnz doeff_volbyte_end + ; cptr+2 : ESI + call Portamento + jmp doeff_volbyte_end +else + jnz doeff_volbyte_end +endif +doeff_case_volE: + neg ecx +doeff_case_volD: + sub [esi+FMUSIC_CHANNEL.pan-2],ecx + or BYTE PTR [esi+FMUSIC_CHANNEL.notectrl-2],FMUSIC_PAN + jmp doeff_volbyte_end +if VIBRATO_ON +doeff_case_volB: + mov [esi+FMUSIC_CHANNEL.vibdepth-2],cl + ; cptr+2 : ESI + call Vibrato + jmp doeff_volbyte_end +endif +doeff_case_vol6: + neg ecx +doeff_case_vol7: + add [esi+FMUSIC_CHANNEL.volume-2],ecx + or BYTE PTR [esi+FMUSIC_CHANNEL.notectrl-2],FMUSIC_VOLUME +doeff_volbyte_end: +endif ; VOLUMEBYTE_ON + mov edx,[ebp+doeff_current] + movzx ebx,BYTE PTR [edx+FMUSIC_NOTE.eparam] + mov ecx,ebx + and ebx,0Fh ; grab the effect parameter y + movzx eax,BYTE PTR [edx+FMUSIC_NOTE.effect] + shr cl,4 ; grab the effect parameter x +if ARPEGGIO_ON +else + dec eax +endif +if TREMOR_ON + cmp al,29 +else + if MULTIRETRIG_ON + cmp al,27 + else + if PANSLIDE_ON + cmp al,25 + else + if GLOBALVOLSLIDE_ON + cmp al,17 + else + if RETRIG_ON + cmp al,14 + else + if NOTECUT_ON + cmp al,14 + else + if NOTEDELAY_ON + cmp al,14 + else + cmp al,10 + endif + endif + endif + endif + endif + endif +endif +if ARPEGGIO_ON + ja doeff_s3_brk +else + jae doeff_s3_brk +endif + call DWORD PTR [eax*4+S3_TBL] +doeff_s3_brk: + ; cptr+2 : ESI + call DoFlags + sub esi,-(FMUSIC_CHANNEL_size) + pop eax + add DWORD PTR [ebp+doeff_current],FMUSIC_NOTE_size + cmp esi,eax + jl doeff_for_channels + pop esi +doeff_R: + leave +S3_r: + ret +S3_TBL: +if ARPEGGIO_ON + dd S3_C0 +endif +if PORTAUP_ON + dd S3_C1 +else + dd S3_r +endif +if PORTADOWN_ON + dd S3_C2 +else + dd S3_r +endif +if PORTATO_ON + ; cptr+2 : ESI + dd Portamento +else + dd S3_r +endif +if VIBRATO_ON + ; cptr+2 : ESI + dd Vibrato +else + dd S3_r +endif +if PORTATOVOLSLIDE_ON + dd S3_C5 +else + dd S3_r +endif +if VIBRATOVOLSLIDE_ON + dd S3_C6 +else + dd S3_r +endif +if TREMOLO_ON + ; cptr+2 : ESI + dd Tremolo +else + dd S3_r +endif + dd S3_r ; skip FMUSIC_XM_SETPANPOSITION + dd S3_r ; skip FMUSIC_XM_SETSAMPLEOFFSET +if VOLUMESLIDE_ON + dd S3_C10 +else + dd S3_r +endif +if TREMOR_ON + dd S3_r ; skip FMUSIC_XM_PATTERNJUMP + dd S3_r ; slip FMUSIC_XM_SETVOLUME + dd S3_r ; skip FMUSIC_XM_PATTERNBREAK + dd S3_C14 + dd S3_r ; skip FMUSIC_XM_SETSPEED + dd S3_r ; skip FMUSIC_XM_SETGLOBALVOLUME + if GLOBALVOLSLIDE_ON + dd S3_C17 + else + dd S3_r + endif + dd S3_r ; unassigned effect ordinal [18] + dd S3_r ; unassigned effect ordinal [19] + dd S3_r ; skip FMUSIC_XM_KEYOFF + dd S3_r ; skip FMUSIC_XM_SETENVELOPEPOS + dd S3_r ; unassigned effect ordinal [22] + dd S3_r ; unassigned effect ordinal [23] + dd S3_r ; unassigned effect ordinal [24] + if PANSLIDE_ON + dd S3_C25 + else + dd S3_r + endif + dd S3_r ; unassigned effect ordinal [26] + if MULTIRETRIG_ON + dd S3_C27 + else + dd S3_r + endif + dd S3_r ; unassigned effect ordinal [28] + ; case FMUSIC_XM_TREMOR + ; cptr : ESI + dd Tremor +else + if MULTIRETRIG_ON + dd S3_r + dd S3_r + dd S3_r + dd S3_C14 + dd S3_r + dd S3_r + if GLOBALVOLSLIDE_ON + dd S3_C17 + else + dd S3_r + endif + dd S3_r + dd S3_r + dd S3_r + dd S3_r + dd S3_r + dd S3_r + dd S3_r + if PANSLIDE_ON + dd S3_C25 + else + dd S3_r + endif + dd S3_r + dd S3_C27 + else + if PANSLIDE_ON + dd S3_r + dd S3_r + dd S3_r + dd S3_C14 + dd S3_r + dd S3_r + if GLOBALVOLSLIDE_ON + dd S3_C17 + else + dd S3_r + endif + dd S3_r + dd S3_r + dd S3_r + dd S3_r + dd S3_r + dd S3_r + dd S3_r + dd S3_C25 + else + if GLOBALVOLSLIDE_ON + dd S3_r + dd S3_r + dd S3_r + dd S3_C14 + dd S3_r + dd S3_r + dd S3_C17 + else + if RETRIG_ON + dd S3_r + dd S3_r + dd S3_r + dd S3_C14 + else + if NOTECUT_ON + dd S3_r + dd S3_r + dd S3_r + dd S3_C14 + else + if NOTEDELAY_ON + dd S3_r + dd S3_r + dd S3_r + dd S3_C14 + endif + endif + endif + endif + endif + + endif +endif +if VIBRATOVOLSLIDE_ON +S3_C6: + ; cptr+2 : ESI + call Vibrato +if VOLUMESLIDE_ON + if PORTATOVOLSLIDE_ON + jmp S3_C10 + endif +else + mov cl,[esi+FMUSIC_CHANNEL.volslide-2] + mov eax,ecx + and eax,0Fh + shr ecx,4 + jz doeff_volslide_ok + ; Slide up takes precedence over down + xchg eax,ecx + neg eax +doeff_volslide_ok: + sub [esi+FMUSIC_CHANNEL.volume-2],eax + or BYTE PTR [esi+FMUSIC_CHANNEL.notectrl-2],FMUSIC_VOLUME + ret +endif +endif +if PORTATOVOLSLIDE_ON +S3_C5: + ; cptr+2 : ESI + call Portamento +if VOLUMESLIDE_ON + xor ecx,ecx +else + movzx ecx,BYTE PTR [esi+FMUSIC_CHANNEL.volslide-2] + mov eax,ecx + and eax,0Fh + shr ecx,4 + jz doeff_volslide_ok + ; Slide up takes precedence over down + xchg eax,ecx + neg eax +doeff_volslide_ok: + sub [esi+FMUSIC_CHANNEL.volume-2],eax + or BYTE PTR [esi+FMUSIC_CHANNEL.notectrl-2],FMUSIC_VOLUME + ret +endif +endif +if VOLUMESLIDE_ON +S3_C10: + mov cl,[esi+FMUSIC_CHANNEL.volslide-2] + mov eax,ecx + and eax,0Fh + shr ecx,4 + jz doeff_volslide_ok + ; Slide up takes precedence over down + xchg eax,ecx + neg eax +doeff_volslide_ok: + sub [esi+FMUSIC_CHANNEL.volume-2],eax + or BYTE PTR [esi+FMUSIC_CHANNEL.notectrl-2],FMUSIC_VOLUME + ret +endif +if PORTADOWN_ON +S3_C2: + mov al,[esi+FMUSIC_CHANNEL.portaupdown-2] +DoFreqSlide: + mov ecx,[esi+FMUSIC_CHANNEL.freq-2] + lea ecx,[ecx+eax*4] + cmp ecx,1 + jge DoFreqSlide_ok + push 1 + pop ecx +DoFreqSlide_ok: + mov [esi+FMUSIC_CHANNEL.freq-2],ecx + and DWORD PTR [esi+FMUSIC_CHANNEL.freqdelta-2],0 + or BYTE PTR [esi+FMUSIC_CHANNEL.notectrl-2],FMUSIC_FREQ + ret +endif +if PORTAUP_ON +S3_C1: + mov al,[esi+FMUSIC_CHANNEL.portaupdown-2] +if PORTADOWN_ON + neg eax + jmp DoFreqSlide +else + shl eax,2 + mov ecx,[esi+FMUSIC_CHANNEL.freq-2] + sub ecx,eax + cmp ecx,1 + jge DoFreqSlide_ok + push 1 + pop ecx +DoFreqSlide_ok: + mov [esi+FMUSIC_CHANNEL.freq-2],ecx + and DWORD PTR [esi+FMUSIC_CHANNEL.freqdelta-2],0 + or BYTE PTR [esi+FMUSIC_CHANNEL.notectrl-2],FMUSIC_FREQ + ret +endif +endif +if ARPEGGIO_ON +S3_C0: + cmp BYTE PTR [edx+FMUSIC_NOTE.eparam],ch + jbe doeff_arpeggio_ok + mov eax,[ebp+var_mod] + mov eax,[eax+FMUSIC_MODULE.tick-36] + push 3 + cdq + pop edi + idiv edi + dec edx + jz doeff_arpeg_c1 + dec edx + jne doeffs_enable_freq + mov ecx,ebx +doeff_arpeg_c1: + mov eax,[ebp+var_mod] + test BYTE PTR [eax+FMUSIC_MODULE.flags-36],1 + je doeffs_flagsn1 +doeffs_arpeggio_freqd: + shl ecx,6 + mov [esi+FMUSIC_CHANNEL.freqdelta-2],ecx +if AMIGAPERIODS_ON + jmp doeffs_enable_freq +endif +doeffs_flagsn1: +if AMIGAPERIODS_ON + movzx eax,BYTE PTR [esi+FMUSIC_CHANNEL.realnote-2] + mov edi,eax + add eax,ecx ; note : EAX + mov ecx,[esp+4] ; [sptr] : ECX + ; ESI != 0 + call AmigaPeriod + xchg eax,edi ; note : EAX + mov ecx,[esp+4] ; [sptr] : ECX + ; ESI != 0 + call AmigaPeriod + sub edi,eax + mov [esi+FMUSIC_CHANNEL.freqdelta-2],edi +endif +doeffs_enable_freq: + or BYTE PTR [esi+FMUSIC_CHANNEL.notectrl-2],FMUSIC_FREQ +doeff_arpeggio_ok: + ret +endif +if MULTIRETRIG_ON +S3_C27: + mov cl,[esi+FMUSIC_CHANNEL.retrigy-2] + test ecx,ecx + jz doeff_multiretrig_ok + mov eax,[ebp+var_mod] + mov eax,[eax+FMUSIC_MODULE.tick-36] + cdq + idiv ecx + test edx,edx + jnz doeff_multiretrig_ok + mov cl,[esi+FMUSIC_CHANNEL.retrigx-2] + and ecx,0Fh + or BYTE PTR [esi+FMUSIC_CHANNEL.notectrl-2],FMUSIC_TRIGGER + dec ecx + mov eax,[esi+FMUSIC_CHANNEL.volume-2] + js doeff_multiretrig_ok + or BYTE PTR [esi+FMUSIC_CHANNEL.notectrl-2],FMUSIC_VOLUME + call DWORD PTR [ecx*4+S4_TBL] + mov [esi+FMUSIC_CHANNEL.volume-2],eax +S4_C2: + dec eax +S4_C1: + dec eax +doeff_multiretrig_ok: + ret +S4_C5: + sub eax,8 +S4_C4: + sub eax,4 +S4_C3: + sub eax,4 + ret +S4_C6: + shl eax,1 + push 3 + cdq + pop ecx + idiv ecx + ret +S4_C14: + lea eax,[eax+eax*2] +S4_C7: + sar eax,1 + ret +S4_C10: + inc eax +S4_C9: + inc eax + ret +S4_C13: + add eax,8 +S4_C12: + add eax,4 +S4_C11: + add eax,4 + ret +S4_C15: + shl eax,1 +S4_r: + ret +S4_TBL: + dd S4_C1 + dd S4_C2 + dd S4_C3 + dd S4_C4 + dd S4_C5 + dd S4_C6 + dd S4_C7 + dd S4_r + dd S4_C9 + dd S4_C10 + dd S4_C11 + dd S4_C12 + dd S4_C13 + dd S4_C14 + dd S4_C15 +endif +if PANSLIDE_ON +S3_C25: + mov cl,[esi+FMUSIC_CHANNEL.panslide-2] + mov eax,ecx + and eax,0Fh + mov edx,[esi+FMUSIC_CHANNEL.pan-2] + shr ecx,4 + ; Slide right takes precedence over left + jnz doeff_pan_slide_right + sub edx,eax + jns doeff_panslide_ok + mov edx,ecx + jmp doeff_panslide_ok +doeff_pan_slide_right: + add ecx,edx + cdq + dec dl ; edx = 255 + cmp ecx,edx + jg doeff_panslide_ok + mov edx,ecx +doeff_panslide_ok: +if SETPANPOSITION_ON +S1_C8: +endif + mov [esi+FMUSIC_CHANNEL.pan-2],edx + or BYTE PTR [esi+FMUSIC_CHANNEL.notectrl-2],FMUSIC_PAN + ret +endif +if GLOBALVOLSLIDE_ON +S3_C17: + mov ecx,[ebp+var_mod] + mov edx,[ecx+FMUSIC_MODULE.globalvolume-36] + mov al,[ecx+FMUSIC_MODULE.globalvsl-36] + test al,0F0h + ; Slide up takes precedence over down + jnz doeff_gvsl_slide_up + and eax,0Fh + sub edx,eax + jns set_global_vol + cdq + xor eax,eax +doeff_gvsl_slide_up: + shr eax,4 + add edx,eax +set_global_vol: + if SETGLOBALVOLUME_ON + else + cmp edx,64 + jle doeff_setglobalvol + push 64 + pop edx +doeff_setglobalvol: + mov [ecx+FMUSIC_MODULE.globalvolume-36],edx + ret + endif +endif +if SETGLOBALVOLUME_ON +S1_C16: + cmp edx,64 + mov ecx,[ebp+var_mod] + jle do_setglobalvol + push 64 + pop edx +do_setglobalvol: + mov [ecx+FMUSIC_MODULE.globalvolume-36],edx + ret +endif +S3_C14: +if RETRIG_ON + cmp cl,FMUSIC_XM_RETRIG + je doeff_do_retrig +endif +if NOTECUT_ON + sub cl,FMUSIC_XM_NOTECUT + jz doeff_do_notecut +endif +if NOTEDELAY_ON + if NOTECUT_ON + dec cl + else + cmp cl,FMUSIC_XM_NOTEDELAY + endif + jne doeff_special_r + mov ecx,[ebp+var_mod] + xor eax,eax + cmp [ecx+FMUSIC_MODULE.tick-36],ebx + jne doeff_notectrl_z + mov edx,[esp+4] + mov DWORD PTR [esi+FMUSIC_CHANNEL.fadeoutvol-2],65536 + movzx edx,BYTE PTR [edx+FSOUND_SAMPLE.defvol] + lea ecx,[eax+9] + mov [esi+FMUSIC_CHANNEL.volume-2],edx + mov edx,[esi+FMUSIC_CHANNEL.envvol-2] + lea edi,[esi+FMUSIC_CHANNEL.envvoltick-2] + cmp ecx,edx + sbb edx,edx + and edx,64 + mov [esi+FMUSIC_CHANNEL.envvol-2],edx + ; memset(&cptr->envvoltick,0,36) + rep stosd + ; Retrigger tremolo and vibrato waveforms + mov cl,[esi+FMUSIC_CHANNEL.wavecontrol-2] + cmp cl,4Fh + jge z2_tremolopos_ok + mov BYTE PTR [esi+FMUSIC_CHANNEL.tremolopos-2],al ; = 0 +z2_tremolopos_ok: + test cl,0Ch + jnz z2_vibpos_ok + mov BYTE PTR [esi+FMUSIC_CHANNEL.vibpos-2],al ; = 0 +z2_vibpos_ok: + mov eax,[esi+FMUSIC_CHANNEL.period-2] + or BYTE PTR [esi+FMUSIC_CHANNEL.notectrl-2],FMUSIC_VOL_OR_FREQ_OR_TR + mov [esi+FMUSIC_CHANNEL.freq-2],eax +if VOLUMEBYTE_ON + mov eax,[ebp+doeff_current] + movzx edx,BYTE PTR [eax+FMUSIC_NOTE.uvolume] + ; volume : EDX + ; cptr : ESI + jmp VolByte +else + ret +endif +doeff_notectrl_z: + mov BYTE PTR [esi+FMUSIC_CHANNEL.notectrl-2],al +doeff_special_r: +endif + ret +if NOTECUT_ON +doeff_do_notecut: + mov ecx,[ebp+var_mod] + cmp [ecx+FMUSIC_MODULE.tick-36],ebx + jne doeff_notecut_ok + and DWORD PTR [esi+FMUSIC_CHANNEL.volume-2],0 + or BYTE PTR [esi+FMUSIC_CHANNEL.notectrl-2],FMUSIC_VOLUME +doeff_notecut_ok: + ret +endif +if RETRIG_ON +doeff_do_retrig: + test ebx,ebx + jz doeff_retrig_ok + mov eax,[ebp+var_mod] + mov eax,[eax+FMUSIC_MODULE.tick-36] + cdq + idiv ebx + test edx,edx + jne doeff_retrig_ok + or BYTE PTR [esi+FMUSIC_CHANNEL.notectrl-2],FMUSIC_VOL_OR_FREQ_OR_TR +doeff_retrig_ok: + ret +endif + +; DESCRIPTION: Process sample flags +DoFlags: +; cptr+2 - ESI +flags_iptr equ 16 +flags_sptr equ 12 + mov ebx,esi + push esi + push edi +if INSTRUMENTVIBRATO_ON + ; THIS GETS ADDED TO PREVIOUS FREQDELTAS + mov esi,[esp+flags_iptr] + xor eax,eax + movzx edx,BYTE PTR [esi+FMUSIC_INSTRUMENT.VIBtype] + lea edi,[ebx+FMUSIC_CHANNEL.ivibpos-2] + dec edx ; switch(iptr->VIBtype) + js ivib_case_0 + mov al,128 + jz ivib_case_1 + dec edx + jz ivib_case_2 + ; case 3 / default + mov dl,-128 ; -128 = 384 on a 1byte scope + sub edx,[edi] +trim: + movzx edx,dl + sub eax,edx + sar eax,1 ; delta = (128-((384-cptr->ivibpos)&0xFF))>>1 (case 3) + ; delta = (128-((cptr->ivibpos+128)&0xFF))>>1 (case 2) + jmp ivib_end_switch +ivib_case_2: + mov edx,[edi] + sub edx,eax + jmp trim +ivib_case_1: + cmp [edi],eax + sbb edx,edx + and edx,eax + lea eax,[edx-64] ; delta = +/- 64 + jmp ivib_end_switch +ivib_case_0: + ; delta = 64 sin(2 Pi x/256) + mov ecx,[edi] + mov edx,ecx + and ecx,7Fh + cmp ecx,65 + adc eax,-1 + xor ecx,eax + and eax,129 + add ecx,eax + shr edx,8 + movzx eax,BYTE PTR [OFFSET sin64+ecx] + sbb edx,edx + xor eax,edx + sub eax,edx +ivib_end_switch: + movzx edx,BYTE PTR [esi+FMUSIC_INSTRUMENT.iVIBdepth] + imul edx,eax ; delta *= iptr->iVIBdepth + movzx eax,BYTE PTR [esi+FMUSIC_INSTRUMENT.VIBsweep] + test eax,eax + jz sweep_ok + push edi + xchg eax,edi + mov eax,[ebx+FMUSIC_CHANNEL.ivibsweeppos-2] + imul edx + idiv edi + xchg eax,edx ; delta *= cptr->ivibsweeppos/iptr->VIBsweep + xchg eax,edi ; iptr->VIBsweep + pop edi +sweep_ok: + sar edx,6 + add [ebx+FMUSIC_CHANNEL.freqdelta-2],edx ; cptr->freqdelta += delta>>6 + mov edx,[ebx+FMUSIC_CHANNEL.ivibsweeppos-2] + inc edx + cmp edx,eax + jle sweeppos_ok + xchg eax,edx +sweeppos_ok: + movzx eax,BYTE PTR [esi+FMUSIC_INSTRUMENT.VIBrate] + add eax,[edi] ; cptr->ivibpos += iptr->VIBrate + mov [ebx+FMUSIC_CHANNEL.ivibsweeppos-2],edx + neg ah + or BYTE PTR [ebx+FMUSIC_CHANNEL.notectrl-2],FMUSIC_FREQ + neg ah + sbb ah,0 ; ivibpos - 256 + mov [edi],eax +endif ; INSTRUMENTVIBRATO_ON + mov esi,[ebx+FMUSIC_CHANNEL.cptr-2] + test BYTE PTR [ebx+FMUSIC_CHANNEL.notectrl-2],FMUSIC_TRIGGER + jz no_trig + ; TRIGGER: allocate a channel + cmp DWORD PTR [esi+FSOUND_CHANNEL.fsptr],0 + jz no_swap + ; Swap between channels to avoid sounds cutting each other off and causing a click + mov ecx,esi + sub ecx,DWORD PTR [OFFSET _mod+FMUSIC_MODULE.Channels] + shr ecx,7 ; /FSOUND_CHANNEL_size + 1 + sbb edi,edi + and edi,-FSOUND_CHANNEL_size*2 + lea edi,[esi+edi+FSOUND_CHANNEL_size] + push edi + ; Copy the whole channel except it's trailing data + push (FSOUND_CHANNEL_size-20)/4 + mov [ebx+FMUSIC_CHANNEL.cptr-2],edi + pop ecx + rep movsd + ; This should cause the old channel to ramp out nicely + mov [esi+FSOUND_CHANNEL.actualvolume-FSOUND_CHANNEL.fsptr],ecx + mov [esi+FSOUND_CHANNEL.leftvolume-FSOUND_CHANNEL.fsptr],ecx + mov [esi+FSOUND_CHANNEL.rightvolume-FSOUND_CHANNEL.fsptr],ecx + pop esi +no_swap: + lea edi,[esi+FSOUND_CHANNEL.fsptr] + mov eax,[esp+flags_sptr] + stosd ; fsptr + ; START THE SOUND! + xor eax,eax + mov edx,[esi+FSOUND_CHANNEL.fsampleoffset] + stosd ; mixposlo + stosd ; ramp_leftvolume + stosd ; ramp_rightvolume + stosd ; ramp_count, speeddir + mov [esi+FSOUND_CHANNEL.fsampleoffset],eax + mov [esi+FSOUND_CHANNEL.mixpos],edx +no_trig: + mov eax,[ebx+FMUSIC_CHANNEL.volume-2] + xor ecx,ecx + cdq + not edx + and eax,edx + cmp eax,64 + jle volume_le64 + lea eax,[ecx+64] +volume_le64: + dec cl ; ecx <- 255 + mov [ebx+FMUSIC_CHANNEL.volume-2],eax + test BYTE PTR [ebx+FMUSIC_CHANNEL.notectrl-2],FMUSIC_VOLUME + jz no_volume + add eax,[ebx+FMUSIC_CHANNEL.voldelta-2] ; 6 bit (64) + imul DWORD PTR [ebx+FMUSIC_CHANNEL.envvol-2] ; 6 bit (64) + imul DWORD PTR [_mod+FMUSIC_MODULE.globalvolume] ; 6 bit (64) + imul ecx ; eax *= 255 + imul DWORD PTR [ebx+FMUSIC_CHANNEL.fadeoutvol-2] ; 16 bit (65536) + ; eax:edx /= (2 * 64 * 64 * 64 * 65536) + sar edx,3 + mov eax,[esi+FSOUND_CHANNEL.actualpan] + mov [esi+FSOUND_CHANNEL.actualvolume],edx + mov edi,edx + imul edx + idiv ecx + mov [esi+FSOUND_CHANNEL.leftvolume],eax ; leftvolume <- volume*actualpan/255 + mov eax,ecx + sub eax,[esi+FSOUND_CHANNEL.actualpan] + imul edi + idiv ecx + mov [esi+FSOUND_CHANNEL.rightvolume],eax +no_volume: + test BYTE PTR [ebx+FMUSIC_CHANNEL.notectrl-2],FMUSIC_PAN + jz no_pan + mov edi,128 + mov eax,[ebx+FMUSIC_CHANNEL.pan-2] + sub eax,edi + cdq + xor eax,edx + sub eax,edx + sub edi,eax + mov eax,[ebx+FMUSIC_CHANNEL.envpan-2] + sar edi,5 + sub eax,32 + imul edi + add eax,[ebx+FMUSIC_CHANNEL.pan-2] + cdq + not edx + and eax,edx ; if(pan < 0) pan = 0 + mov edi,[esi] ; actualvolume + cmp eax,ecx + mov edx,ecx + xchg eax,edi + jae pan_ae255 + imul edi + idiv ecx + mov edx,edi +pan_ae255: + xchg eax,edx + mov [esi+FSOUND_CHANNEL.actualpan],eax + mov [esi+FSOUND_CHANNEL.leftvolume],edx + not al ; 255 - pan + imul DWORD PTR [esi] ; actualvolume + idiv ecx + mov [esi+FSOUND_CHANNEL.rightvolume],eax +no_pan: + test BYTE PTR [ebx+FMUSIC_CHANNEL.notectrl-2],FMUSIC_FREQ + jz no_freq + mov ecx,[ebx+FMUSIC_CHANNEL.freq-2] + xor edx,edx + add ecx,[ebx+FMUSIC_CHANNEL.freqdelta-2] + lea eax,[edx+40] ; f = 40 Hz + jle freq_bounds_ok + test BYTE PTR [_mod+FMUSIC_MODULE.flags],1 + jnz modflags_n1 + mov eax,0DA7790h + div ecx + xor edx,edx + jmp freq_bounds_ok +modflags_n1: + mov eax,4608 + sub eax,[ebx+FMUSIC_CHANNEL.freq-2] + add eax,[ebx+FMUSIC_CHANNEL.freqdelta-2] + push eax + fild DWORD PTR [esp] + fmul DWORD PTR [f0_0013] + fld st0 + frndint + fsub st1,st0 + fxch st1 + f2xm1 + fld1 + faddp st1,st0 + fscale + fstp st1 + fmul DWORD PTR [f8363_0] + fistp DWORD PTR [esp] + pop eax ; freq = 625.271028*exp2((4608-period)/64) +freq_bounds_ok: + mov ebx,FSOUND_MixRate + div ebx + mov [esi+FSOUND_CHANNEL.speedhi],eax + div ebx + mov [esi+FSOUND_CHANNEL.speedlo],eax +no_freq: + pop edi + pop esi +if INSTRUMENTVIBRATO_ON + ret 8 +else + ret 4 +endif \ No newline at end of file diff --git a/programs/develop/libraries/ufmod/eff.inc b/programs/develop/libraries/ufmod/eff.inc new file mode 100644 index 0000000000..28b6d3b4c0 --- /dev/null +++ b/programs/develop/libraries/ufmod/eff.inc @@ -0,0 +1,60 @@ +; XM-file: full.xm + +; uFMOD optional features: +INFO_API_ON equ 1 ; enable InfoAPI +NOLOOP_ON equ 1 ; enable XM_NOLOOP +PAUSE_RESUME_ON equ 1 ; enable pause/resume and XM_SUSPENDED +VOL_CONTROL_ON equ 1 ; enable volume control +JUMP_TO_PAT_ON equ 1 ; enable Jump2Pattern +XM_FILE_ON equ 1 ; enable file loading + +; Special flags: +INSTRUMENTVIBRATO_ON equ 1 +VOLUMEENVELOPE_ON equ 1 +PANENVELOPE_ON equ 1 +VOLUMEBYTE_ON equ 1 +ADPCM_ON equ 1 +AMIGAPERIODS_ON equ 1 + +; XM effects: +ARPEGGIO_ON equ 1 +PORTAUP_ON equ 1 +PORTADOWN_ON equ 1 +PORTATO_ON equ 1 +VIBRATO_ON equ 1 +PORTATOVOLSLIDE_ON equ 1 +VIBRATOVOLSLIDE_ON equ 1 +TREMOLO_ON equ 1 +SETPANPOSITION_ON equ 1 +SETSAMPLEOFFSET_ON equ 1 +VOLUMESLIDE_ON equ 1 +PATTERNJUMP_ON equ 1 +SETVOLUME_ON equ 1 +PATTERNBREAK_ON equ 1 +SETSPEED_ON equ 1 +SETGLOBALVOLUME_ON equ 1 +GLOBALVOLSLIDE_ON equ 1 +KEYOFF_ON equ 1 +SETENVELOPEPOS_ON equ 1 +PANSLIDE_ON equ 1 +MULTIRETRIG_ON equ 1 +TREMOR_ON equ 1 +EXTRAFINEPORTA_ON equ 1 +FINEPORTAUP_ON equ 1 +FINEPORTADOWN_ON equ 1 +SETVIBRATOWAVE_ON equ 1 +SETFINETUNE_ON equ 1 +PATTERNLOOP_ON equ 1 +SETTREMOLOWAVE_ON equ 1 +SETPANPOSITION16_ON equ 1 +RETRIG_ON equ 1 +NOTECUT_ON equ 1 +NOTEDELAY_ON equ 1 +PATTERNDELAY_ON equ 1 +PORTAUP_OR_DOWN_ON equ 1 +VIBRATO_OR_VOLSLIDE equ 1 +VIBRATO_OR_TREMOLO equ 1 +PORTATO_OR_VOLSLIDE equ 1 +VOLUME_OR_PANENVELOPE equ 1 +ROWCOMMANDS_ON equ 1 +FINEVOLUMESLIDE_ON equ 1 diff --git a/programs/develop/libraries/ufmod/fasm.asm b/programs/develop/libraries/ufmod/fasm.asm new file mode 100644 index 0000000000..d489fe459c --- /dev/null +++ b/programs/develop/libraries/ufmod/fasm.asm @@ -0,0 +1,342 @@ +; FASM.ASM +; -------- +; uFMOD public source code release. Provided as-is. + +; *** This stub allows compiling uFMOD sources using FASM. + +; *** CONSTANTS *** + +if UF_FREQ eq 44100 + FSOUND_MixRate = 44100 + FREQ_40HZ_p = 1DB8Bh + FREQ_40HZ_f = 3B7160h + PCM_format = 3 ; PCM_2_16_44 +else + if UF_FREQ eq 22050 + FSOUND_MixRate = 22050 + FREQ_40HZ_p = 3B716h + FREQ_40HZ_f = 76E2C0h + PCM_format = 9 ; PCM_2_16_22 + else + if UF_FREQ eq 48000 + else + display 'UF_FREQ not specified (defaulting to 48KHz)',13,10 + end if + FSOUND_MixRate = 48000 + FREQ_40HZ_p = 1B4E8h + FREQ_40HZ_f = 369D00h + PCM_format = 1 ; PCM_2_16_48 + end if +end if + +if UF_RAMP eq NONE + RAMP_NONE = 1 + RAMP_WEAK = 0 + RAMP_STRONG = 0 +else + if UF_RAMP eq WEAK + RAMP_NONE = 0 + RAMP_WEAK = 1 + RAMP_STRONG = 0 + else + if UF_RAMP eq STRONG + else + display 'UF_RAMP not specified (defaulting to STRONG)',13,10 + end if + RAMP_NONE = 0 + RAMP_WEAK = 0 + RAMP_STRONG = 1 + end if +end if + +UCODE equ 0 + +if UF_MODE eq UNSAFE + display 'WARNING! Unsafe mod is ON. Library may crash while loading damaged XM tracks!',13,10 + CHK4VALIDITY = 0 + AC97SND_ON = 0 +else + CHK4VALIDITY = 1 + if UF_MODE eq AC97SND + AC97SND_ON = 1 + else + AC97SND_ON = 0 + end if +end if + +if NOLINKER +else + format MS COFF + section '.text' code readable executable +end if + +; *** STRUCTS *** + +; Sample type - contains info on sample +struc FSOUND_SAMPLE{ + + ; Don't change order .:. + ._length dd ? ; sample length + .loopstart dd ? ; loop start + .looplen dd ? ; loop length + .defvol db ? ; default volume + .finetune db ? ; finetune value from -128 to 127 + .bytes db ? ; type [b 0-1] : 0 - no loop + ; 1 - forward loop + ; 2 - bidirectional loop (aka ping-pong) + ; [b 4] : 0 - 8-bit sample data + ; 1 - 16-bit sample data + .defpan db ? ; default pan value from 0 to 255 + .relative db ? ; relative note (signed value) + .Resved db ? ; reserved, known values: 00h - regular delta packed sample data + ; ADh - ModPlug 4-bit ADPCM packed sample data + ; .:. + + .loopmode db ? + ._align db ? + .buff db ?,? ; sound data +} +virtual at 0 +FSOUND_SAMPLE FSOUND_SAMPLE +FSOUND_SAMPLE_size = $-FSOUND_SAMPLE +end virtual + + +; Channel type - contains information on a mixing channel +struc FSOUND_CHANNEL{ + .actualvolume dd ? ; driver level current volume + .actualpan dd ? ; driver level panning value + .fsampleoffset dd ? ; sample offset (sample starts playing from here) + .leftvolume dd ? ; mixing information. adjusted volume for left channel (panning involved) + .rightvolume dd ? ; mixing information. adjusted volume for right channel (panning involved) + .mixpos dd ? ; mixing information. high part of 32:32 fractional position in sample + .speedlo dd ? ; mixing information. playback rate - low part fractional + .speedhi dd ? ; mixing information. playback rate - high part fractional + .ramp_lefttarget dw ? + .ramp_righttarget dw ? + .ramp_leftspeed dd ? + .ramp_rightspeed dd ? + + ; Don't change order .:. + .fsptr dd ? ; pointer to FSOUND_SAMPLE currently playing sample + .mixposlo dd ? ; mixing information. low part of 32:32 fractional position in sample + .ramp_leftvolume dd ? + .ramp_rightvolume dd ? + .ramp_count dw ? + .speeddir db ?,? ; mixing information. playback direction - forwards or backwards + ; .:. + +} +virtual at 0 +FSOUND_CHANNEL FSOUND_CHANNEL +FSOUND_CHANNEL_size = $-FSOUND_CHANNEL +end virtual + +; Single note type - contains info on 1 note in a pattern +struc FMUSIC_NOTE{ + .unote db ? ; note to play at (0-97) 97=keyoff + .number db ? ; sample being played (0-128) + .uvolume db ? ; volume column value (0-64) 255=no volume + .effect db ? ; effect number (0-1Ah) + .eparam db ? ; effect parameter (0-255) +} +virtual at 0 +FMUSIC_NOTE FMUSIC_NOTE +FMUSIC_NOTE_size = $-FMUSIC_NOTE +end virtual + +; Pattern data type +struc FMUSIC_PATTERN{ + .rows dw ? + .patternsize dw ? + .data dd ? ; pointer to FMUSIC_NOTE +} +virtual at 0 +FMUSIC_PATTERN FMUSIC_PATTERN +FMUSIC_PATTERN_size = $-FMUSIC_PATTERN +end virtual + +; Multi sample extended instrument +struc FMUSIC_INSTRUMENT{ + .sample rd 16 ; 16 pointers to FSOUND_SAMPLE per instrument + + ; Don't change order .:. + .keymap rb 96 ; sample keymap assignments + .VOLPoints rw 24 ; volume envelope points + .PANPoints rw 24 ; panning envelope points + .VOLnumpoints db ? ; number of volume envelope points + .PANnumpoints db ? ; number of panning envelope points + .VOLsustain db ? ; volume sustain point + .VOLLoopStart db ? ; volume envelope loop start + .VOLLoopEnd db ? ; volume envelope loop end + .PANsustain db ? ; panning sustain point + .PANLoopStart db ? ; panning envelope loop start + .PANLoopEnd db ? ; panning envelope loop end + .VOLtype db ? ; type of envelope,bit 0:On 1:Sustain 2:Loop + .PANtype db ? ; type of envelope,bit 0:On 1:Sustain 2:Loop + .VIBtype db ? ; instrument vibrato type + .VIBsweep db ? ; time it takes for vibrato to fully kick in + .iVIBdepth db ? ; depth of vibrato + .VIBrate db ? ; rate of vibrato + .VOLfade dw ? ; fade out value + ; .:. + +} +virtual at 0 +FMUSIC_INSTRUMENT FMUSIC_INSTRUMENT +FMUSIC_INSTRUMENT_size = $-FMUSIC_INSTRUMENT +end virtual + +; Channel type - contains information on a mod channel +struc FMUSIC_CHANNEL{ + .note db ? ; last note set in channel + .samp db ? ; last sample set in channel + .notectrl db ? ; flags for DoFlags proc + .inst db ? ; last instrument set in channel + .cptr dd ? ; pointer to FSOUND_CHANNEL system mixing channel + .freq dd ? ; current mod frequency period for this channel + .volume dd ? ; current mod volume for this channel + .voldelta dd ? ; delta for volume commands... tremolo/tremor, etc + .freqdelta dd ? ; delta for frequency commands... vibrato/arpeggio, etc + .pan dd ? ; current mod pan for this channel + + ; Don't change order .:. + .envvoltick dd ? ; tick counter for envelope position + .envvolpos dd ? ; envelope position + .envvoldelta dd ? ; delta step between points + .envpantick dd ? ; tick counter for envelope position + .envpanpos dd ? ; envelope position + .envpandelta dd ? ; delta step between points + .ivibsweeppos dd ? ; instrument vibrato sweep position + .ivibpos dd ? ; instrument vibrato position + .keyoff db ?,? ; flag whether keyoff has been hit or not + .envvolstopped db ? ; flag to say whether envelope has finished or not + .envpanstopped db ? ; flag to say whether envelope has finished or not + ; .:. + + .envvolfrac dd ? ; fractional interpolated envelope volume + .envvol dd ? ; final interpolated envelope volume + .fadeoutvol dd ? ; volume fade out + .envpanfrac dd ? ; fractional interpolated envelope pan + .envpan dd ? ; final interpolated envelope pan + .period dd ? ; last period set in channel + .sampleoffset dd ? ; sample offset for this channel in SAMPLES + .portatarget dd ? ; note to porta to + .patloopno db ?,?,?,? ; pattern loop variables for effect E6x + .patlooprow dd ? + .realnote db ? ; last realnote set in channel + .recenteffect db ? ; previous row's effect... used to correct tremolo volume + .portaupdown db ? ; last porta up/down value + db ? ; unused + .xtraportadown db ? ; last porta down value + .xtraportaup db ? ; last porta up value + .volslide db ? ; last volume slide value + .panslide db ? ; pan slide parameter + .retrigx db ? ; last retrig volume slide used + .retrigy db ? ; last retrig tick count used + .portaspeed db ? ; porta speed + .vibpos db ? ; vibrato position + .vibspeed db ? ; vibrato speed + .vibdepth db ? ; vibrato depth + .tremolopos db ? ; tremolo position + .tremolospeed db ? ; tremolo speed + .tremolodepth db ? ; tremolo depth + .tremorpos db ? ; tremor position + .tremoron db ? ; remembered parameters for tremor + .tremoroff db ? ; remembered parameters for tremor + .wavecontrol db ? ; waveform type for vibrato and tremolo (4bits each) + .finevslup db ? ; parameter for fine volume slide down + .fineportaup db ? ; parameter for fine porta slide up + .fineportadown db ? ; parameter for fine porta slide down +} +virtual at 0 +FMUSIC_CHANNEL FMUSIC_CHANNEL +FMUSIC_CHANNEL_size = $-FMUSIC_CHANNEL +end virtual + +; Song type - contains info on song +struc FMUSIC_MODULE{ + + ; Don't change order .:. + .pattern dd ? ; pointer to FMUSIC_PATTERN array for this song + .instrument dd ? ; pointer to FMUSIC_INSTRUMENT array for this song + .mixer_samplesleft dd ? + .globalvolume dd ? ; global mod volume + .tick dd ? ; current mod tick + .speed dd ? ; speed of song in ticks per row + .order dd ? ; current song order position + .row dd ? ; current row in pattern + .patterndelay dd ? ; pattern delay counter + .nextorder dd ? ; current song order position + .nextrow dd ? ; current row in pattern + .unused1 dd ? + .numchannels dd ? ; number of channels + .Channels dd ? ; channel pool + .uFMOD_Ch dd ? ; channel array for this song + .mixer_samplespertick dd ? + .numorders dw ? ; number of orders (song length) + .restart dw ? ; restart position + .numchannels_xm db ? + .globalvsl db ? ; global mod volume + .numpatternsmem dw ? ; number of allocated patterns + .numinsts dw ? ; number of instruments + .flags dw ? ; flags such as linear frequency, format specific quirks, etc + .defaultspeed dw ? + .defaultbpm dw ? + .orderlist rb 256 ; pattern playing order list + ; .:. + +} +virtual at 0 +FMUSIC_MODULE FMUSIC_MODULE +FMUSIC_MODULE_size = $-FMUSIC_MODULE +end virtual + +OFFSET equ +PTR equ +endif equ end if + +include 'ufmod.asm' +include 'core.asm' + +if NOLINKER + uFMOD_IMG_END: ; End of uFMOD's code. BSS follows. + align 16 +else + section '.bss' readable writeable align 16 +end if + +; Don't change order! +_mod rb FMUSIC_MODULE_size ; currently playing track +mmt rd 3 +ufmod_heap dd ? + dd ? ; unused +if AC97SND_ON + extrn hSound + dd ? +else + hSound dd ? +endif +hBuff dd ? +SW_Exit dd ? +; mix buffer memory block (align 16!) +MixBuf rb FSOUND_BlockSize*8 +ufmod_noloop db ? +ufmod_pause_ db ? +mix_endflag rb 2 +mmf rd 4 +ufmod_vol dd ? ; global volume scale +; * LPCALLBACKS * +uFMOD_fopen dd ? +uFMOD_fread dd ? +file_struct rd 7 +cache_offset dd ? +if INFO_API_ON + time_ms dd ? + L_vol dw ? ; L channel RMS volume + R_vol dw ? ; R channel RMS volume + s_row dw ? + s_order dw ? + szTtl rb 24 +end if +DummySamp rb FSOUND_SAMPLE_size diff --git a/programs/develop/libraries/ufmod/makeobj.bat b/programs/develop/libraries/ufmod/makeobj.bat new file mode 100644 index 0000000000..4506a493bb --- /dev/null +++ b/programs/develop/libraries/ufmod/makeobj.bat @@ -0,0 +1,68 @@ +@echo off +rem Make the uFMOD libraries in COFF object format +rem Target OS: KolibriOS + +rem *** CONFIG START +rem *** Check the Readme docs for a complete reference +rem *** on configuring the following options + +rem Pathes: +SET UF_MASM=\masm32\bin +SET UF_NASM=\nasm +SET UF_FASM=\fasm + +rem Select compiler: MASM, NASM or FASM +SET UF_ASM=FASM + +rem Select mixing rate: 22050, 44100 or 48000 (22.05 KHz, 44.1 KHz or 48 KHz) +SET UF_FREQ=48000 + +rem Set volume ramping mode (interpolation): NONE, WEAK or STRONG +SET UF_RAMP=STRONG + +rem Set build mode: NORMAL, UNSAFE or AC97SND +SET UF_MODE=NORMAL +rem *** CONFIG END + +if %UF_ASM%==MASM goto MASM +if %UF_ASM%==NASM goto NASM +if %UF_ASM%==FASM goto FASM +echo %UF_ASM% not supported +goto TheEnd + +:MASM +if not exist "%UF_MASM%\ml.exe" goto Err1 +"%UF_MASM%\ml" /c /coff /nologo /Df%UF_FREQ% /D%UF_RAMP% /D%UF_MODE% /Fo ufmod.obj src\masm.asm +goto TheEnd + +:NASM +if not exist "%UF_NASM%\nasmw.exe" goto Err2 +"%UF_NASM%\nasmw" -O4 -t -fwin32 -dNODEBUG -df%UF_FREQ% -d%UF_RAMP% -d%UF_MODE% -isrc\ -oufmod.obj src\nasm.asm +goto TheEnd + +:FASM +if not exist "%UF_FASM%\fasm.exe" goto Err3 +echo UF_FREQ equ %UF_FREQ% >tmp.asm +echo UF_RAMP equ %UF_RAMP% >>tmp.asm +echo UF_MODE equ %UF_MODE% >>tmp.asm +echo DEBUG equ 0 >>tmp.asm +echo NOLINKER equ 0 >>tmp.asm +echo include 'src\eff.inc' >>tmp.asm +echo include 'src\fasm.asm' >>tmp.asm +"%UF_FASM%\fasm" tmp.asm ufmod.obj +del tmp.asm +goto TheEnd + +:Err1 +echo Couldn't find ml.exe in %UF_MASM%\ +goto TheEnd +:Err2 +echo Couldn't find nasmw.exe in %UF_NASM%\ +goto TheEnd +:Err3 +echo Couldn't find fasm.exe in %UF_FASM%\ + +:TheEnd +pause +@echo on +cls diff --git a/programs/develop/libraries/ufmod/masm.asm b/programs/develop/libraries/ufmod/masm.asm new file mode 100644 index 0000000000..7c3fd0cabe --- /dev/null +++ b/programs/develop/libraries/ufmod/masm.asm @@ -0,0 +1,270 @@ +; MASM.ASM +; -------- +; uFMOD public source code release. Provided as-is. + +; *** This stub allows compiling uFMOD sources using MASM32. + +.386 +.model flat + +ifdef f44100 + FSOUND_MixRate = 44100 + FREQ_40HZ_p = 1DB8Bh + FREQ_40HZ_f = 3B7160h + PCM_format = 3 +else + ifdef f22050 + FSOUND_MixRate = 22050 + FREQ_40HZ_p = 3B716h + FREQ_40HZ_f = 76E2C0h + PCM_format = 9 + else + ifndef f48000 + echo UF_FREQ not specified (defaulting to 48KHz) + endif + FSOUND_MixRate = 48000 + FREQ_40HZ_p = 1B4E8h + FREQ_40HZ_f = 369D00h + PCM_format = 1 + endif +endif + +RAMP_NONE = 0 +RAMP_WEAK = 0 +RAMP_STRONG = 0 +ifdef NONE + RAMP_NONE = 1 +else + ifdef WEAK + RAMP_WEAK = 1 + else + ifndef STRONG + echo UF_RAMP not specified (defaulting to STRONG) + endif + RAMP_STRONG = 1 + endif +endif + +UCODE = 0 +DEBUG = 0 + +CHK4VALIDITY = 1 +ifdef UNSAFE + echo WARNING! Unsafe mod is ON. Library may crash while loading damaged XM tracks! + CHK4VALIDITY = 0 +endif + +AC97SND_ON = 0 +ifdef AC97SND + AC97SND_ON = 1 +endif + +include eff.inc + +FSOUND_SAMPLE STRUC + _length dd ? + loopstart dd ? + looplen dd ? + defvol db ? + finetune db ? + bytes db ? + defpan db ? + relative db ? + Resved db ? + loopmode db ? + _align db ? + buff db ?,? +FSOUND_SAMPLE ENDS + +FSOUND_CHANNEL STRUC + actualvolume dd ? + actualpan dd ? + fsampleoffset dd ? + leftvolume dd ? + rightvolume dd ? + mixpos dd ? + speedlo dd ? + speedhi dd ? + ramp_lefttarget dw ? + ramp_righttarget dw ? + ramp_leftspeed dd ? + ramp_rightspeed dd ? + fsptr dd ? + mixposlo dd ? + ramp_leftvolume dd ? + ramp_rightvolume dd ? + ramp_count dw ? + speeddir db ?,? +FSOUND_CHANNEL ENDS + +FMUSIC_NOTE STRUC + unote db ? + number db ? + uvolume db ? + effect db ? + eparam db ? +FMUSIC_NOTE ENDS + +FMUSIC_PATTERN STRUC + rows dw ? + patternsize dw ? + data dd ? +FMUSIC_PATTERN ENDS + +FMUSIC_INSTRUMENT STRUC + sample dd 16 dup (?) + keymap db 96 dup (?) + VOLPoints dw 24 dup (?) + PANPoints dw 24 dup (?) + VOLnumpoints db ? + PANnumpoints db ? + VOLsustain db ? + VOLLoopStart db ? + VOLLoopEnd db ? + PANsustain db ? + PANLoopStart db ? + PANLoopEnd db ? + VOLtype db ? + PANtype db ? + VIBtype db ? + VIBsweep db ? + iVIBdepth db ? + VIBrate db ? + VOLfade dw ? +FMUSIC_INSTRUMENT ENDS + +FMUSIC_CHANNEL STRUC + note db ? + samp db ? + notectrl db ? + inst db ? + cptr dd ? + freq dd ? + volume dd ? + voldelta dd ? + freqdelta dd ? + pan dd ? + envvoltick dd ? + envvolpos dd ? + envvoldelta dd ? + envpantick dd ? + envpanpos dd ? + envpandelta dd ? + ivibsweeppos dd ? + ivibpos dd ? + keyoff db ?,? + envvolstopped db ? + envpanstopped db ? + envvolfrac dd ? + envvol dd ? + fadeoutvol dd ? + envpanfrac dd ? + envpan dd ? + period dd ? + sampleoffset dd ? + portatarget dd ? + patloopno db ?,?,?,? + patlooprow dd ? + realnote db ? + recenteffect db ? + portaupdown db ? + db ? + xtraportadown db ? + xtraportaup db ? + volslide db ? + panslide db ? + retrigx db ? + retrigy db ? + portaspeed db ? + vibpos db ? + vibspeed db ? + vibdepth db ? + tremolopos db ? + tremolospeed db ? + tremolodepth db ? + tremorpos db ? + tremoron db ? + tremoroff db ? + wavecontrol db ? + finevslup db ? + fineportaup db ? + fineportadown db ? +FMUSIC_CHANNEL ENDS + +FMUSIC_MODULE STRUC + pattern dd ? + instrument dd ? + mixer_samplesleft dd ? + globalvolume dd ? + tick dd ? + speed dd ? + order dd ? + row dd ? + patterndelay dd ? + nextorder dd ? + nextrow dd ? + unused1 dd ? + numchannels dd ? + Channels dd ? + uFMOD_Ch dd ? + mixer_samplespertick dd ? + numorders dw ? + restart dw ? + numchannels_xm db ? + globalvsl db ? + numpatternsmem dw ? + numinsts dw ? + flags dw ? + defaultspeed dw ? + defaultbpm dw ? + orderlist db 256 dup (?) +FMUSIC_MODULE ENDS + +FMUSIC_MODULE_size = SIZE FMUSIC_MODULE +FSOUND_CHANNEL_size = SIZE FSOUND_CHANNEL +FMUSIC_CHANNEL_size = SIZE FMUSIC_CHANNEL +FMUSIC_INSTRUMENT_size = SIZE FMUSIC_INSTRUMENT +FMUSIC_PATTERN_size = SIZE FMUSIC_PATTERN +FMUSIC_NOTE_size = SIZE FMUSIC_NOTE + +; FPU register stack +st0 TEXTEQU +st1 TEXTEQU + +.CODE +include ufmod.asm +include core.asm + +.DATA? +_mod = $ + FMUSIC_MODULE<> +mmt dd ?,?,? +ufmod_heap dd ?,? +if AC97SND_ON + EXTERN hSound:DWORD + dd ? +else + hSound dd ? +endif +hBuff dd ? +SW_Exit dd ? +MixBuf db FSOUND_BlockSize*8 dup (?) +ufmod_noloop db ? +ufmod_pause_ db ? +mix_endflag db ?,? +mmf dd ?,?,?,? +ufmod_vol dd ? +uFMOD_fopen dd ? +uFMOD_fread dd ? +file_struct dd 7 dup (?) +cache_offset dd ? +if INFO_API_ON + time_ms dd ? + L_vol dw ? + R_vol dw ? + s_row dw ? + s_order dw ? + szTtl db 24 dup (?) +endif +DummySamp FSOUND_SAMPLE<> +end diff --git a/programs/develop/libraries/ufmod/media/BLITZXMK.XM b/programs/develop/libraries/ufmod/media/BLITZXMK.XM new file mode 100644 index 0000000000..c4d34bee46 Binary files /dev/null and b/programs/develop/libraries/ufmod/media/BLITZXMK.XM differ diff --git a/programs/develop/libraries/ufmod/media/blitz.eff.inc b/programs/develop/libraries/ufmod/media/blitz.eff.inc new file mode 100644 index 0000000000..3e0592fc7a --- /dev/null +++ b/programs/develop/libraries/ufmod/media/blitz.eff.inc @@ -0,0 +1,60 @@ +; XM-file: BLITZXMK.XM + +; uFMOD optional features: +INFO_API_ON equ 0 ; disable InfoAPI +NOLOOP_ON equ 0 ; disable XM_NOLOOP +PAUSE_RESUME_ON equ 1 ; enable pause/resume and XM_SUSPENDED +VOL_CONTROL_ON equ 0 ; disable volume control +JUMP_TO_PAT_ON equ 1 ; enable Jump2Pattern +XM_FILE_ON equ 0 ; disable file loading + +; Special flags: +INSTRUMENTVIBRATO_ON equ 1 +VOLUMEENVELOPE_ON equ 1 +PANENVELOPE_ON equ 0 +VOLUMEBYTE_ON equ 1 +ADPCM_ON equ 0 +AMIGAPERIODS_ON equ 0 + +; XM effects: +ARPEGGIO_ON equ 0 +PORTAUP_ON equ 0 +PORTADOWN_ON equ 0 +PORTATO_ON equ 1 +VIBRATO_ON equ 1 +PORTATOVOLSLIDE_ON equ 0 +VIBRATOVOLSLIDE_ON equ 0 +TREMOLO_ON equ 0 +SETPANPOSITION_ON equ 0 +SETSAMPLEOFFSET_ON equ 0 +VOLUMESLIDE_ON equ 0 +PATTERNJUMP_ON equ 1 +SETVOLUME_ON equ 0 +PATTERNBREAK_ON equ 0 +SETSPEED_ON equ 1 +SETGLOBALVOLUME_ON equ 0 +GLOBALVOLSLIDE_ON equ 0 +KEYOFF_ON equ 0 +SETENVELOPEPOS_ON equ 0 +PANSLIDE_ON equ 0 +MULTIRETRIG_ON equ 0 +TREMOR_ON equ 0 +EXTRAFINEPORTA_ON equ 0 +FINEPORTAUP_ON equ 0 +FINEPORTADOWN_ON equ 0 +SETVIBRATOWAVE_ON equ 0 +SETFINETUNE_ON equ 0 +PATTERNLOOP_ON equ 0 +SETTREMOLOWAVE_ON equ 0 +SETPANPOSITION16_ON equ 0 +RETRIG_ON equ 0 +NOTECUT_ON equ 1 +NOTEDELAY_ON equ 1 +PATTERNDELAY_ON equ 0 +PORTAUP_OR_DOWN_ON equ 0 +VIBRATO_OR_VOLSLIDE equ 1 +VIBRATO_OR_TREMOLO equ 1 +PORTATO_OR_VOLSLIDE equ 1 +VOLUME_OR_PANENVELOPE equ 1 +ROWCOMMANDS_ON equ 1 +FINEVOLUMESLIDE_ON equ 0 \ No newline at end of file diff --git a/programs/develop/libraries/ufmod/media/mini.eff.inc b/programs/develop/libraries/ufmod/media/mini.eff.inc new file mode 100644 index 0000000000..403a2b6a7a --- /dev/null +++ b/programs/develop/libraries/ufmod/media/mini.eff.inc @@ -0,0 +1,60 @@ +; XM-file: mini.xm + +; uFMOD optional features: +INFO_API_ON equ 0 ; disable InfoAPI +NOLOOP_ON equ 0 ; disable XM_NOLOOP +PAUSE_RESUME_ON equ 0 ; disable pause/resume and XM_SUSPENDED +VOL_CONTROL_ON equ 0 ; disable volume control +JUMP_TO_PAT_ON equ 0 ; disable Jump2Pattern +XM_FILE_ON equ 0 ; disable file loading + +; Special flags: +INSTRUMENTVIBRATO_ON equ 0 +VOLUMEENVELOPE_ON equ 1 +PANENVELOPE_ON equ 0 +VOLUMEBYTE_ON equ 0 +ADPCM_ON equ 0 +AMIGAPERIODS_ON equ 0 + +; XM effects: +ARPEGGIO_ON equ 0 +PORTAUP_ON equ 0 +PORTADOWN_ON equ 0 +PORTATO_ON equ 0 +VIBRATO_ON equ 0 +PORTATOVOLSLIDE_ON equ 0 +VIBRATOVOLSLIDE_ON equ 0 +TREMOLO_ON equ 0 +SETPANPOSITION_ON equ 0 +SETSAMPLEOFFSET_ON equ 0 +VOLUMESLIDE_ON equ 1 +PATTERNJUMP_ON equ 0 +SETVOLUME_ON equ 0 +PATTERNBREAK_ON equ 0 +SETSPEED_ON equ 0 +SETGLOBALVOLUME_ON equ 0 +GLOBALVOLSLIDE_ON equ 0 +KEYOFF_ON equ 0 +SETENVELOPEPOS_ON equ 0 +PANSLIDE_ON equ 0 +MULTIRETRIG_ON equ 0 +TREMOR_ON equ 0 +EXTRAFINEPORTA_ON equ 0 +FINEPORTAUP_ON equ 0 +FINEPORTADOWN_ON equ 0 +SETVIBRATOWAVE_ON equ 0 +SETFINETUNE_ON equ 0 +PATTERNLOOP_ON equ 1 +SETTREMOLOWAVE_ON equ 0 +SETPANPOSITION16_ON equ 0 +RETRIG_ON equ 0 +NOTECUT_ON equ 0 +NOTEDELAY_ON equ 0 +PATTERNDELAY_ON equ 0 +PORTAUP_OR_DOWN_ON equ 0 +VIBRATO_OR_VOLSLIDE equ 0 +VIBRATO_OR_TREMOLO equ 0 +PORTATO_OR_VOLSLIDE equ 0 +VOLUME_OR_PANENVELOPE equ 1 +ROWCOMMANDS_ON equ 1 +FINEVOLUMESLIDE_ON equ 0 \ No newline at end of file diff --git a/programs/develop/libraries/ufmod/media/mini.xm b/programs/develop/libraries/ufmod/media/mini.xm new file mode 100644 index 0000000000..951c54de21 Binary files /dev/null and b/programs/develop/libraries/ufmod/media/mini.xm differ diff --git a/programs/develop/libraries/ufmod/nasm.asm b/programs/develop/libraries/ufmod/nasm.asm new file mode 100644 index 0000000000..f3e8d96962 --- /dev/null +++ b/programs/develop/libraries/ufmod/nasm.asm @@ -0,0 +1,278 @@ +; NASM.ASM +; -------- +; uFMOD public source code release. Provided as-is. + +; *** This stub allows compiling uFMOD sources using NASM. +; Everything documented in fasm stub! + +; %error directive in NASM causes multiple prompts to appear due to +; multiple passes :( So, we'd better avoid using %error. + +ifdef f44100 + FSOUND_MixRate equ 44100 + FREQ_40HZ_p equ 1DB8Bh + FREQ_40HZ_f equ 3B7160h + PCM_format equ 3 +else + ifdef f22050 + FSOUND_MixRate equ 22050 + FREQ_40HZ_p equ 3B716h + FREQ_40HZ_f equ 76E2C0h + PCM_format equ 9 + else + FSOUND_MixRate equ 48000 + FREQ_40HZ_p equ 1B4E8h + FREQ_40HZ_f equ 369D00h + PCM_format equ 1 + endif +endif + +ifdef NONE + RAMP_NONE equ 1 + RAMP_WEAK equ 0 + RAMP_STRONG equ 0 +else + ifdef WEAK + RAMP_NONE equ 0 + RAMP_WEAK equ 1 + RAMP_STRONG equ 0 + else + RAMP_NONE equ 0 + RAMP_WEAK equ 0 + RAMP_STRONG equ 1 + endif +endif + +UCODE equ 0 + +ifdef NODEBUG + DEBUG equ 0 +else + DEBUG equ 1 +endif + +ifdef UNSAFE + CHK4VALIDITY equ 0 + AC97SND_ON equ 0 +else + CHK4VALIDITY equ 1 + ifdef AC97SND + AC97SND_ON equ 1 + else + AC97SND_ON equ 0 + endif +endif + +ifndef NOLINKER + %include "eff.inc" + + [segment .text align=4] +endif + +STRUC FSOUND_SAMPLE + FSOUND_SAMPLE._length resd 1 + FSOUND_SAMPLE.loopstart resd 1 + FSOUND_SAMPLE.looplen resd 1 + FSOUND_SAMPLE.defvol resb 1 + FSOUND_SAMPLE.finetune resb 1 + FSOUND_SAMPLE.bytes resb 1 + FSOUND_SAMPLE.defpan resb 1 + FSOUND_SAMPLE.relative resb 1 + FSOUND_SAMPLE.Resved resb 1 + FSOUND_SAMPLE.loopmode resb 1 + FSOUND_SAMPLE._align resb 1 + FSOUND_SAMPLE.buff resb 2 +ENDSTRUC + +STRUC FSOUND_CHANNEL + FSOUND_CHANNEL.actualvolume resd 1 + FSOUND_CHANNEL.actualpan resd 1 + FSOUND_CHANNEL.fsampleoffset resd 1 + FSOUND_CHANNEL.leftvolume resd 1 + FSOUND_CHANNEL.rightvolume resd 1 + FSOUND_CHANNEL.mixpos resd 1 + FSOUND_CHANNEL.speedlo resd 1 + FSOUND_CHANNEL.speedhi resd 1 + FSOUND_CHANNEL.ramp_lefttarget resw 1 + FSOUND_CHANNEL.ramp_righttarget resw 1 + FSOUND_CHANNEL.ramp_leftspeed resd 1 + FSOUND_CHANNEL.ramp_rightspeed resd 1 + FSOUND_CHANNEL.fsptr resd 1 + FSOUND_CHANNEL.mixposlo resd 1 + FSOUND_CHANNEL.ramp_leftvolume resd 1 + FSOUND_CHANNEL.ramp_rightvolume resd 1 + FSOUND_CHANNEL.ramp_count resw 1 + FSOUND_CHANNEL.speeddir resb 2 +ENDSTRUC + +STRUC FMUSIC_NOTE + FMUSIC_NOTE.unote resb 1 + FMUSIC_NOTE.number resb 1 + FMUSIC_NOTE.uvolume resb 1 + FMUSIC_NOTE.effect resb 1 + FMUSIC_NOTE.eparam resb 1 +ENDSTRUC + +STRUC FMUSIC_PATTERN + FMUSIC_PATTERN.rows resw 1 + FMUSIC_PATTERN.patternsize resw 1 + FMUSIC_PATTERN.data resd 1 +ENDSTRUC + +STRUC FMUSIC_INSTRUMENT + FMUSIC_INSTRUMENT.sample resd 16 + FMUSIC_INSTRUMENT.keymap resb 96 + FMUSIC_INSTRUMENT.VOLPoints resw 24 + FMUSIC_INSTRUMENT.PANPoints resw 24 + FMUSIC_INSTRUMENT.VOLnumpoints resb 1 + FMUSIC_INSTRUMENT.PANnumpoints resb 1 + FMUSIC_INSTRUMENT.VOLsustain resb 1 + FMUSIC_INSTRUMENT.VOLLoopStart resb 1 + FMUSIC_INSTRUMENT.VOLLoopEnd resb 1 + FMUSIC_INSTRUMENT.PANsustain resb 1 + FMUSIC_INSTRUMENT.PANLoopStart resb 1 + FMUSIC_INSTRUMENT.PANLoopEnd resb 1 + FMUSIC_INSTRUMENT.VOLtype resb 1 + FMUSIC_INSTRUMENT.PANtype resb 1 + FMUSIC_INSTRUMENT.VIBtype resb 1 + FMUSIC_INSTRUMENT.VIBsweep resb 1 + FMUSIC_INSTRUMENT.iVIBdepth resb 1 + FMUSIC_INSTRUMENT.VIBrate resb 1 + FMUSIC_INSTRUMENT.VOLfade resw 1 +ENDSTRUC + +STRUC FMUSIC_CHANNEL + FMUSIC_CHANNEL.note resb 1 + FMUSIC_CHANNEL.samp resb 1 + FMUSIC_CHANNEL.notectrl resb 1 + FMUSIC_CHANNEL.inst resb 1 + FMUSIC_CHANNEL.cptr resd 1 + FMUSIC_CHANNEL.freq resd 1 + FMUSIC_CHANNEL.volume resd 1 + FMUSIC_CHANNEL.voldelta resd 1 + FMUSIC_CHANNEL.freqdelta resd 1 + FMUSIC_CHANNEL.pan resd 1 + FMUSIC_CHANNEL.envvoltick resd 1 + FMUSIC_CHANNEL.envvolpos resd 1 + FMUSIC_CHANNEL.envvoldelta resd 1 + FMUSIC_CHANNEL.envpantick resd 1 + FMUSIC_CHANNEL.envpanpos resd 1 + FMUSIC_CHANNEL.envpandelta resd 1 + FMUSIC_CHANNEL.ivibsweeppos resd 1 + FMUSIC_CHANNEL.ivibpos resd 1 + FMUSIC_CHANNEL.keyoff resb 2 + FMUSIC_CHANNEL.envvolstopped resb 1 + FMUSIC_CHANNEL.envpanstopped resb 1 + FMUSIC_CHANNEL.envvolfrac resd 1 + FMUSIC_CHANNEL.envvol resd 1 + FMUSIC_CHANNEL.fadeoutvol resd 1 + FMUSIC_CHANNEL.envpanfrac resd 1 + FMUSIC_CHANNEL.envpan resd 1 + FMUSIC_CHANNEL.period resd 1 + FMUSIC_CHANNEL.sampleoffset resd 1 + FMUSIC_CHANNEL.portatarget resd 1 + FMUSIC_CHANNEL.patloopno resb 4 + FMUSIC_CHANNEL.patlooprow resd 1 + FMUSIC_CHANNEL.realnote resb 1 + FMUSIC_CHANNEL.recenteffect resb 1 + FMUSIC_CHANNEL.portaupdown resb 2 + FMUSIC_CHANNEL.xtraportadown resb 1 + FMUSIC_CHANNEL.xtraportaup resb 1 + FMUSIC_CHANNEL.volslide resb 1 + FMUSIC_CHANNEL.panslide resb 1 + FMUSIC_CHANNEL.retrigx resb 1 + FMUSIC_CHANNEL.retrigy resb 1 + FMUSIC_CHANNEL.portaspeed resb 1 + FMUSIC_CHANNEL.vibpos resb 1 + FMUSIC_CHANNEL.vibspeed resb 1 + FMUSIC_CHANNEL.vibdepth resb 1 + FMUSIC_CHANNEL.tremolopos resb 1 + FMUSIC_CHANNEL.tremolospeed resb 1 + FMUSIC_CHANNEL.tremolodepth resb 1 + FMUSIC_CHANNEL.tremorpos resb 1 + FMUSIC_CHANNEL.tremoron resb 1 + FMUSIC_CHANNEL.tremoroff resb 1 + FMUSIC_CHANNEL.wavecontrol resb 1 + FMUSIC_CHANNEL.finevslup resb 1 + FMUSIC_CHANNEL.fineportaup resb 1 + FMUSIC_CHANNEL.fineportadown resb 1 +ENDSTRUC + +STRUC FMUSIC_MODULE + FMUSIC_MODULE.pattern resd 1 + FMUSIC_MODULE.instrument resd 1 + FMUSIC_MODULE.mixer_samplesleft resd 1 + FMUSIC_MODULE.globalvolume resd 1 + FMUSIC_MODULE.tick resd 1 + FMUSIC_MODULE.speed resd 1 + FMUSIC_MODULE.order resd 1 + FMUSIC_MODULE.row resd 1 + FMUSIC_MODULE.patterndelay resd 1 + FMUSIC_MODULE.nextorder resd 1 + FMUSIC_MODULE.nextrow resd 1 + FMUSIC_MODULE.unused1 resd 1 + FMUSIC_MODULE.numchannels resd 1 + FMUSIC_MODULE.Channels resd 1 + FMUSIC_MODULE.uFMOD_Ch resd 1 + FMUSIC_MODULE.mixer_samplespertick resd 1 + FMUSIC_MODULE.numorders resw 1 + FMUSIC_MODULE.restart resw 1 + FMUSIC_MODULE.numchannels_xm resb 1 + FMUSIC_MODULE.globalvsl resb 1 + FMUSIC_MODULE.numpatternsmem resw 1 + FMUSIC_MODULE.numinsts resw 1 + FMUSIC_MODULE.flags resw 1 + FMUSIC_MODULE.defaultspeed resw 1 + FMUSIC_MODULE.defaultbpm resw 1 + FMUSIC_MODULE.orderlist resb 256 +ENDSTRUC + +%macro PUBLIC 1 + ifndef NOLINKER + GLOBAL %1 + endif +%endmacro +%define OFFSET +%define PTR + +include "ufmod.asm" +include "core.asm" + +ifdef NOLINKER + uFMOD_IMG_END: ; End of uFMOD's code. BSS follows. + align 16 + [segment .bss] +else + [segment .bss align=16] +endif + +_mod resb FMUSIC_MODULE_size +mmt resd 3 +ufmod_heap resd 2 +if AC97SND_ON + extern hSound + resd 1 +else + hSound resd 1 +endif +hBuff resd 1 +SW_Exit resd 1 +MixBuf resb FSOUND_BlockSize*8 +ufmod_noloop resb 1 +ufmod_pause_ resb 1 +mix_endflag resb 2 +mmf resd 4 +ufmod_vol resd 1 +uFMOD_fopen resd 1 +uFMOD_fread resd 1 +file_struct resd 7 +cache_offset resd 1 +if INFO_API_ON + time_ms resd 1 + L_vol resw 1 + R_vol resw 1 + s_row resw 1 + s_order resw 1 + szTtl resb 24 +endif +DummySamp resb FSOUND_SAMPLE_size diff --git a/programs/develop/libraries/ufmod/readme_en.htm b/programs/develop/libraries/ufmod/readme_en.htm new file mode 100644 index 0000000000..d842b771b1 --- /dev/null +++ b/programs/develop/libraries/ufmod/readme_en.htm @@ -0,0 +1,33 @@ +uFMOD
[ English | Español | Pyccκuú ]

μFMOD v1.25 for KolibriOS

uFMOD is an XM player library written in assembly language. It's perfect for size- and speed-critical applications, click free, highly reliable, easy to use, open source, multiplatform. File and direct memory playback supported. It is able to play even damaged and non-standard XM tracks. Usage examples available for the following compilers: FASM, MASM32 and NASM.

KolibriOS port should run correctly on any PC meeting the following requirements:

  1. KolibriOS SVN revision 574 or later. You can download the latest official distro and the SVN binaries from www.kolibrios.org.
  2. A sound card supported by the Infinity Sound audio library. It is the default KolibriOS sound driver. Currently it supports many AC'97 compatible sound cards.
If your machine meets the above requirements but uFMOD doesn't run properly, let us know (check the contact information near the bottom of this page).

 
Getting started

KolibriOS is an operating system written is assembly language. That's why it is so small and extremely fast. It is very powerful as well, as you've probably found out already. That's also uFMOD's spirit ;)

Most of the tasks described below can be accomplished directly in Kolibri. However, since many novice Kolibri coders prefer compiling their programs in Windows and then moving them to Kolibri for testing, we'll use crosscompilation here for the sake of universality.

 
Tools

There are 2 freeware tools currently available to use along with uFMOD: XMStrip and Eff. None of them has been ported to KolibriOS yet. So, you should pick one of the other platform-specific distros (Win32, Linux or Unix/BSD) and use XMStrip and Eff crossplatformly. No matter which platform-specific version you choose, both utilities have dual interface: terminal and GUI. GUI is rather self-explanatory. Next, we'll explain the terminal interface.

 SVN  Complete source code available 

XMStrip expects an XM filename on input, repacks the file contents to make it smaller, without losing sound quality. What it does exactly is removing overhead data (dummy instruments, patterns and junk metadata), stripping reserved and currently unused bytes, repacking pattern data. Running xmstrip /h produces the following output:

 USAGE:  xmstrip [options] file [output]
+         file   - input file name.
+         output - optional output file name.          
+ options:
+  /c - clean only (don't strip)
+ When [output] is not specified, XMSTRIP
+ attempts to overwrite the input. If file
+ name contains spaces, enclose it in "".

Keep in mind, that other XM players whould probably reject a 'stripped' XM file. The /c option is useful for 'recovering' such a stripped file or just cleaning a regular file intended to be played with any other XM player.

Eff is useful for advanced users, willing to squeeze every single byte out of their applications. The general idea is to extract only those features you do mean to use in your application, recompile the uFMOD library from source and obtain the smallest possible footprint. Running eff /h requests the following prompt:

 USAGE:  eff [options] file
+         file - input file name
+         options:
+          /Dm - generate a masm32/tasm dump
+          /Dd - generate a Pascal (Delphi) dump
+          /Dc - generate a C/C++ dump
+          /Ds - generate an RCDATA resource dump
+          /Di - disable infoAPI:
+                    uFMOD_GetStats, uFMOD_GetRowOrder,
+                    uFMOD_GetTitle and uFMOD_GetTime
+          /Dp - disable uFMOD_Pause, uFMOD_Resume
+                    and XM_SUSPENDED
+          /Dv - disable volume control
+          /Dj - disable Jump2Pattern
+          /Df - disable loading XM from file
+          /Dl - disable XM_NOLOOP
+          /M  - mark & clear unused chunks of
+                data in a masm32/tasm compatible dump

As you can see, the last parameter is expected to be a filename, specifying the XM file you plan to use in your application. Additional options:

  • /Dm produces a hex dump from the given XM file, suitable for MASM32 and TASM. The syntax is also suitable for FASM and NASM. However, both FASM and NASM support embedding binary content from a file directly. This option should be enabled to use the /M key (see below).
  • /Dd and /Dc produce hex dumps in Pascal (Delphi, Kylix, FreePascal) or C/C++ format respectively.
  • /Ds generates a hex dump in RCDATA format, used in resource scripts. No real use in KolibriOS.
  • Specify /Di to disable all the information functions: uFMOD_GetStats, uFMOD_GetRowOrder, uFMOD_GetTitle and uFMOD_GetTime. Removing them reduces the library size and makes it somewhat faster.
  • /Dp removes uFMOD_Pause and uFMOD_Resume functions and makes uFMOD ignore the XM_SUSPENDED flag. If you don't plan to use pause/resume features, add this option to the command line and save some more bytes.
  • uFMOD_SetVolume not only makes the library bigger, but also consumes some additional CPU time. Just use /Dv to turn it off and recover some bytes and clock cycles ;)
  • /Dj disables the Jump2Pattern feature. This is an advanced feature, not used in most applications. Check the "Reducing the executable file size" section for more information on using Jump2Pattern.
  • Not going to play files - only memory arrays? Then, you'd probably like to use /Df in order to make the library smaller.
  • /Dl (lower-case letter L) makes uFMOD ignore the XM_NOLOOP flag (and makes the library smaller and faster, as expected).
  • Finally, a really insane optimization option is available only for assembly coders. There are some byte chunks inside every XM file, which are reserved for future use or just holding metadata (comments, adds and so on). /M marks all such 'holes' in the hex dump and makes them available for something more useful, like storing code and/or data right inside the XM track. src/Masm32/ example uses this feature.
On success, Eff produces a file EFF.INC and a hex dump, if requested. Some examples (all correct):

eff /Dmpvjfl /M example.xm
eff /M /Dm /Dp /Dv /Dj /Df /Dl example.xm
eff -M -Dmpvjfl example.xm

The above commands produce an assembly dump with all the 'holes' properly outlined and cleared by default. The EFF.INC header contains the XM effects actually used in the given XM file and some additional flags to disable pause/resume, volume control, Jump2Pattern, loading files and XM_NOLOOP. Copy EFF.INC to src/ufmodlib/src/ and recompile the library (check the following section for a quick guide). Enjoy your own extremely optimized uFMOD build, but keep in mind that it contains a subset of XM effects. So, it will play normally only the given XM file!

 
Compiling the library

Recompiling the library sources is required after using Eff or to enable some special features in a regular build (check the Options table below). Some people might consider modifying the source code of the library just to practice assembly programming or for any other reason. Well, the following information is intended only for those interested in the subject.

The complete source code is included in src/ufmodlib/src/:

  • eff.inc is a header file. The Eff tool generates this file. Modifying it directly is not recommended, though an assembly coder never pays attention to this kind of recommendations anyway :)
  • ufmod.inc contains a detailed uFMOD API description suitable for ASM/C/C++ development.
  • ufmod-codec.h describes the special AC97SND mode API. This API is intended for codec-oriented audio file players like Serge's AC'97 player.
  • core.asm contains most of the uFMOD source code. The exactly same file is used in KolibriOS, Unix/BSD, Linux and Win32 packages. Loading an XM track, mixing sound channels, XM effects handling and other common algorithms are all implemented in this file.
  • ufmod.asm contains platform-specific implementation: file I/O, driver I/O, etc. The contents of this file vary in KolibriOS, Unix/BSD, Linux and Win32.
  • fasm.asm contains Flat Assembler (FASM) specific definitions. This stub file makes it possible to compile uFMOD using FASM.
  • masm.asm contains MASM32 specific definitions. This stub file makes it possible to compile uFMOD using MASM32.
  • nasm.asm contains Netwide Assembler (NASM) specific definitions. This stub file makes it possible to compile uFMOD using NASM.

Once you are done modifying the uFMOD sources, if you want to rebuild ufmod.obj, open src/ufmodlib/makeobj.bat in a plain text editor. Everything contained between the following lines:

rem *** CONFIG START
and
rem *** CONFIG END
is configurable. First, check the Pathes section. There is an option saying:
SET UF_NASM=\nasm
If you do have NASM installed, make sure the above path points exactly to where nasmw.exe is located. Let's say NASM has been installed to D:\TOOLS\NASM. Then, you'd have to modify the above entry as follows:
SET UF_NASM=D:\TOOLS\NASM
Not all of the pathes are used to recompile the library. For example, if you prefer using FASM as your default assembler, you don't need to setup the NASM path. Make sure all the pathes actually required to build the library have been set correctly. Then, procede modifying the available options, according to the following table:

OptionDescriptionValues
UF_RAMPThis option controls the volume ramping (interpolation) mechanism. Ramping is intended to suppress audible clicks, but sometimes it may introduce distortion. STRONG is the default value recommended for most applications. It detects volume changes during sample playback and softens them using a 128-stage linear ramp. WEAK involves only 16 stages. It is less effective than STRONG, but distortion is also less likely to happen. NONE doesn't perform ramping at all. No ramping = no distortion, but most XM samples will produce clicks, unless the samples are well balanced enough.NONE, WEAK, STRONG
UF_FREQMixing rate (in Hz). 48KHz is the value recommended for most applications.22050, 44100, 48000
UF_ASMAssembler. Yes, the uFMOD library is compilable with various assemblers. Choose your favorite :)MASM, NASM, FASM
UF_MODENORMAL is the default value. Nothing special. UNSAFE disables checking an XM track for validity at load time. When you are sure all XM tracks are valid (you can use Eff or XMStrip to verify an XM file), you can recompile uFMOD in UNSAFE mode to reduce the file size and maximize the loading speed. Keep in mind that a damaged XM file can actually crash uFMOD while in UNSAFE mode! When AC97SND mode is on, ufmod.obj will contain a special codec-like uFMOD version, used in Serge's AC'97 player. It features a different API (if interested, check the ufmod-plugin.h header file for additional info).NORMAL, UNSAFE, AC97SND

Run the batch file to build the library. That's all!

 
Examples

There are currently 2 examples available: mini and jmp2pat. Precompiled executables are located in bin/. None of the executables are packed/compressed.

  • mini is the simplest example showing how to play an XM track from memory with proper error handling.
  • jmp2pat is a bit more complex example showing how to use the Jump2Pattern feature and pause/resume. It uses a composite XM tracked by Kim (aka norki). For more information on composite XM files and the Jump2Pattern feature, refer to the following section.

 
Reducing the executable file size

Use Eff to optimize the library and make it smaller.

When embedding the XM track directly into the executable or attaching it as a raw binary resource, it's sometimes worth it trying to optimize the XM itself. Modplug Player features ADPCM compression, which makes the XM somewhat smaller, but it's a lossy compression! Use XMStrip for lossless (in terms of sound quality) size optimization.

If you're sure all XM tracks your application is going to use are valid (not damaged or otherwise modified), rebuild the library in UNSAFE mode.

Packers, such as mtappack by diamond, make executables smaller. Anyway, to make things fair, the sample executables are not compressed at all!

Let's talk some more about optimizing the XM file size:

An advanced XM file size optimization method involves merging various XM tracks in a single composite file. Since you can share the instruments in a composite file, the resulting file size could be a lot smaller than the sum of the separate file sizes before merging. Even without sharing the instruments it will be smaller because of the XM file header being declared only once. Let's see an example with 3 XM files:

+File 1 : XM1_HEADER P11 P12 P13     I11 I12
+File 2 : XM2_HEADER P21 P22 P23 P24 I21 I22 I23 I24
+File 3 : XM3_HEADER P31             I31
+
Legend: XMn_HEADER is the header of the n-th XM file. Pni is the i-th pattern of the n-th XM file. Ini is the i-th instrument of the n-th XM file.

First, let's merge them in a single file without sharing the instruments:

+File 4 : XM4_HEADER P11 P12 P13 P21 P22 P23 P24 P31 I11 I12 I21 I22 I23 I24 I31
+
Now, let's say I12 is very similar or even equal to I23 and I24 is the same as I31. So, we can modify P2n to make them use I12 instead of I23 and P31 to use I24, so that we can remove I23 and I31:
+File 4 : XM4_HEADER P11 P12 P13 P21 P22 P23 P24 P31 I11 I12 I21 I22 I24
+
You'll have to modify the looping and pattern jumping commands and the references to instruments in "files" 2 and 3. Obviously, you can merge just 2 files or more than 3. XM file format limits the amount of patterns and instruments in a single file. That's the general idea. You'd have to learn how to use a tracker in order to perform this kind of optimization. Once you've got all your files merged, you can issue a single uFMOD_PlaySong function call and trigger all the "files" with uFMOD_Jump2Pattern. For example, uFMOD_Jump2Pattern(3) will start playing the 2nd "file", uFMOD_Jump2Pattern(7) will launch the 3rd and uFMOD_Jump2Pattern(0) will reset to the first "file". The exact indexes depend on your pattern layout. The jmp2pat example uses this feature.

Using Jump2Pattern has another advantage: switching is done much faster (practically in no time), as opposed to stopping and reloading a track. So, you can use this feature when quickly switching the background music is required.

 
FAQ

Q: Is uFMOD free for any type of use? Even commercial?
A: Yes, currently it is.

Q: Where can I get XM files from?
A: Try visiting The Mod Archive. They have a huge archive of free tracker music in XM, IT, S3M and MOD format. You can use Open Modplug Tracker to convert IT, S3M and MOD tracks to XM format without apparent degradation. There are many talented composers out there in the web sharing their music at no cost. Just don't forget the copyright!

Q: Is uFMOD related in some way to Firelight Technologies® FMOD and miniFMOD sound libraries?
A: Not any more. Up until 2004 uFMOD was based on the latest miniFMOD public source code release. Since then, library sources had been completely rewritten, introducing many new features. So, uFMOD is in no way representative of FMOD and miniFMOD sources.

Q: Some XM player libraries claim to add only N kilobytes to the executable file. How many Kb does uFMOD add exactly to the executable's size?
A: It is impossible to tell an exact value, because it depends on library features used (especially, when using the Eff utility), test program code layout, XM file size (when embedding the XM into the executable file). It also depends on the linker options. For example, bin/mini is 4.768 bytes without compression.

Q: Where can I get the official XM file format specification from?
A: No official and up to date specification exists. However, you can take a look at "The Unofficial XM File Format Specification: FastTracker II, ADPCM and Stripped Module Subformats". This document covers most aspects of the original XM file format and all the non-standard extensions currently supported by uFMOD. ModePlug's public source code (it's C++) also serves as reference material on module file formats.

 
Thanks go out to

antarman, Barracuda, bogrus, chris_b, cresta, dododo, flaith, Four-F, GL#0M, norki, q_q, SofT MANiAC, S_T_A_S_, voodooattack and yoxola for reporting bugs, requesting interesting features, submitting usage examples and otherwise helping us improve uFMOD.

[WASM.RU] and SourceForge.net for support and hosting.

 
Copyright

uFMOD sources, binaries and utility programs © 2005 - 2007 Asterix and Quantum.
All rights reserved.

Sample tunes:

  • Minimal III © 2006 - 2007 SofT MANiAC (CoolPHat).
  • BlitzXMK.XM from Jump2Pat example © 2007 Kim (aka norki).
Found a bug? Got a question or a suggestion?
Starting to develop a cool application using uFMOD? Please, let us know: ufmod@users.sf.net
\ No newline at end of file diff --git a/programs/develop/libraries/ufmod/readme_es.htm b/programs/develop/libraries/ufmod/readme_es.htm new file mode 100644 index 0000000000..c257d3d99f --- /dev/null +++ b/programs/develop/libraries/ufmod/readme_es.htm @@ -0,0 +1,34 @@ +uFMOD
[ English | Español | Pyccκuú ]

μFMOD v1.25 para KolibriOS

uFMOD es una librería para reproducir música en formato XM, desarrollada completamente en lenguaje ensamblador. Es perfecta para aplicaciones de tamaño y tiempo crítico, libre de defectos audibles, altamente confiable, fácil de usar, de código fuente abierto y multiplataforma. Permite reproducir ficheros y arreglos estáticos en memoria. Es capaz de reproducir inclusive ficheros corruptos y/o modificados. Ejemplos de uso disponibles para los siguientes compiladores: FASM, MASM32 y NASM.

La versión para KolibriOS debe funcionar en cualquier PC, siempre y cuando se cumplan los siguientes requisitos:

  1. KolibriOS revisión SVN 574 ó posterior. Puede descargar el último distributivo oficial y las actualizaciones SVN de www.kolibrios.org.
  2. Una tarjeta de sonido soportada por la librería Infinity Sound. Este controlador de audio viene preinstalado en KolibriOS. Actualmente soporta muchas tarjetas de sonido compatibles con AC'97.
Si su máquina cumple con dichos requisitos pero uFMOD no funciona correctamente, por favor, repórtenos este hecho. (Ver información de contacto en la parte inferior de esta página.)

 
Antes de comenzar

KolibriOS es un sistema operativo desarrollado en lenguaje ensamblador. Por eso es tan compacto y rápido. Además, es bastante versátil, como probablemente ya se habrá podido dar cuenta. Ese también es el espíritu de uFMOD ;)

La mayor parte de los pasos descritos a continuación se puede llevar a cabo diréctamente en Kolibri. Sin embargo, como muchos programadores principiantes en Kolibri prefieren compilar sus programas en Windows y luego transferirlos a Kolibri para hacer pruebas, vamos a usar compilación cruzada en esta guía en aras de generalizar la metodología.

 
Herramientas

Hay 2 herramientas gratuitas para usar con uFMOD: XMStrip y Eff. Ninguna de las dos ha sido portada a KolibriOS aún. Por lo tanto, se recomienda descargar alguno de los otros distributivos uFMOD (Win32, Linux o Unix/BSD) y usar Eff y XMStrip desde otra plataforma. Sin importar la plataforma elegida, ambas herramientas poseen interfaz de usuario dual: consola y gráfica (GUI). El modo GUI es bastante intuitivo. Vamos a exponer el modo de consola.

 SVN  Código fuente completo disponible 

XMStrip recibe un fichero XM como entrada, modifica su contenido para minimizar el tamaño, sin que esto afecte la calidad del sonido. Concretamente, XMStrip elimina los datos no utilizados (instrumentos y patrones redundantes, comentarios, etc.) y agrupa los patrones de notas para optimizar el tiempo de lectura y procesamiento del XM. Al ingresar xmstrip /h obtenemos la siguiente respuesta:

 USAGE:  xmstrip [options] file [output]
+         file   - input file name.
+         output - optional output file name.          
+ options:
+  /c - clean only (don't strip)
+ When [output] is not specified, XMSTRIP
+ attempts to overwrite the input. If file
+ name contains spaces, enclose it in "".

Si no especifica el nombre del fichero de salida, XMStrip intentará sobreescribir el fichero de entrada. Si el nombre del fichero contiene espacios, enciérrelo entre comillas dobles ("").

Tenga presente, que otros reproductores de XM, probablemente, rechacen los ficheros producidos por XMStrip. Especifique /c para 'recuperar' un fichero así o símplemente para procesar un fichero normal que desea poder usar en otros reproductores de XM.

Eff es útil para los usuarios avanzados, que desean ahorrar hasta el último byte en sus aplicaciones. La idea es extraer sólo aquellas opciones que realmente se van a usar en la aplicación, recompilar la librería uFMOD y obtener el menor tamaño posible. Comencemos abriendo una sesión de terminal y escribiendo eff /h para obtener el siguiente resúmen:

 USAGE:  eff [options] file
+         file - input file name
+         options:
+          /Dm - generate a masm32/tasm dump
+          /Dd - generate a Pascal (Delphi) dump
+          /Dc - generate a C/C++ dump
+          /Ds - generate an RCDATA resource dump
+          /Di - disable infoAPI:
+                    uFMOD_GetStats, uFMOD_GetRowOrder,
+                    uFMOD_GetTitle and uFMOD_GetTime
+          /Dp - disable uFMOD_Pause, uFMOD_Resume
+                    and XM_SUSPENDED
+          /Dv - disable volume control
+          /Dj - disable Jump2Pattern
+          /Df - disable loading XM from file
+          /Dl - disable XM_NOLOOP
+          /M  - mark & clear unused chunks of
+                data in a masm32/tasm compatible dump

Como puede ver, el último parametro es el nombre del fichero XM que se va a usar con la aplicación. Opciones adicionales:

  • /Dm genera un volcado hexadecimal a partir del fichero XM dado, para ser usado en MASM32 o TASM. La sintaxis es compatible con FASM y NASM. Sin embargo, tanto FASM como NASM permiten incluir el contenido de un fichero binario directamente. Es necesario especificar esta opción para poder usar /M (ver mas adelante).
  • /Dd y /Dc generan volcados para Pascal (Delphi, Kylix, FreePascal) y C/C++ respectivamente.
  • /Ds produce un volcado en formato RCDATA, usado en scripts de recursos. No tiene utilidad en KolibriOS.
  • Especifique /Di para deshabilitar todas las funciones informativas: uFMOD_GetStats, uFMOD_GetRowOrder, uFMOD_GetTitle y uFMOD_GetTime. Eliminarlas reduce el tamaño de la librería y mejora un poco el rendimiento.
  • /Dp elimina las funciones uFMOD_Pause y uFMOD_Resume y hace que uFMOD ignore la bandera XM_SUSPENDED. Si no necesita pausar/reanudar, agregue esta opción a la linea de comandos para ahorrar otros tantos bytes.
  • uFMOD_SetVolume no sólo hace la librería más grande, sino que también consume tiempo adicional de CPU. Utilice /Dv para deshabilitar esta función y recuperar algunos bytes y ciclos de reloj ;)
  • /Dj deshabilita la función Jump2Pattern. Esta es una función avanzada, no usada en la mayoria de las aplicaciones. Consulte la sección "Cómo lograr un ejecutable más compacto" para mayor información sobre el uso de Jump2Pattern.
  • ¿No va a reproducir ficheros - sólo arreglos estáticos? Entonces, querrá aprovechar la opción /Df para minimizar el tamaño de la librería.
  • /Dl (L minúscula) hace que la librería ignore la bandera XM_NOLOOP (esto también reduce el tamaño y mejora la eficiencia).
  • Por último, hay una opción de optimización realmente extrema, disponible sólo para los programadores en lenguaje ensamblador. En cada fichero XM hay secuencias de bytes que se encuentran reservadas para versiones futuras del formato XM o contienen metadatos (comentarios, publicidad, etc.) /M delimita y resalta estos 'huecos' en el volcado y los habilita para ser usados con mayor utilidad. Por ejemplo, puede almacenar datos y hasta código ejecutable en dichos 'huecos'. El ejemplo src/Masm32/ utiliza esta opción.
Si no se presenta error alguno, Eff debe generar un fichero EFF.INC y un volcado hexadecimal, si éste fue solicitado. Algunos ejemplos (todos son correctos):

eff /Dmpvjfl /M ejemplo.xm
eff /M /Dm /Dp /Dv /Dj /Df /Dl ejemplo.xm
eff -M -Dmpvjfl ejemplo.xm

Cualquiera de estas sentencias produce un volcado en lenguaje ensamblador con todos los 'huecos' delimitados y rellenos con ceros por defecto. El fichero de cabecera EFF.INC recopila los efectos XM que realmente se utilizan en el fichero XM dado, más algunas banderas adicionales para deshabilitar las funciones de pausar/reanudar, control de volumen, Jump2Pattern, soporte para ficheros y XM_NOLOOP. Copie EFF.INC en src/ufmodlib/src/ y recompile la librería. Consulte la siguiente sección para mayor información sobre cómo recompilar uFMOD. Acaba de compilar su propia versión ultraoptimizada de uFMOD, pero recuerde que ésta contiene un subconjunto de efectos XM. Entonces, ¡sólo servirá para reproducir correctamente el fichero XM especificado como parámetro! +

 
Compilando la librería

Es necesario volver a compilar la librería luego de usar Eff y para habilitar ciertas funciones especiales (ver tabla Opciones mas adelante). Algunas personas querrán modificar el código fuente de la librería, para poner a prueba sus conocimientos en lenguaje ensamblador o por cualquier otra razón. Bueno, la siguiente información les servirá.

El código fuente completo se encuentra en el subdirectorio src/ufmodlib/src/:

  • eff.inc es un fichero de cabecera. La herramienta Eff genera este fichero. No se recomienda modificarlo directamente, aunque un programador en lenguaje ensamblador rara vez presta atención a esta clase de recomendaciones :)
  • ufmod.inc describe detalladamente la API de uFMOD para uso en aplicaciones desarrolladas en ASM/C/C++.
  • ufmod-codec.h describe la API en modo AC97SND, el cual resulta útil en reproductores de ficheros de audio, como el AC'97 MP3 player de Serge.
  • core.asm contiene la mayor parte del código fuente de uFMOD. Exactamente el mismo fichero aparece en los paquetes de KolibriOS, Unix/BSD, Linux y Win32. Cargar un fichero XM, mezclar los canales de audio, procesar los efectos XM y otras tareas comunes se encuentran implementadas en este fichero.
  • ufmod.asm contiene todo el código dependiente del sistema operativo: E/S de ficheros, comunicación con el controlador de audio, etc. El contenido de este fichero varía en KolibriOS, Unix/BSD, Linux y Win32.
  • fasm.asm define las estructuras de datos, constantes y demás, usando la sintaxis de Flat Assembler (FASM). Este fichero permite compilar uFMOD con FASM.
  • masm.asm define las estructuras de datos, constantes y demás, usando la sintaxis de MASM32. Este fichero permite compilar uFMOD con MASM32.
  • nasm.asm define las estructuras de datos, constantes y demás, usando la sintaxis de Netwide Assembler (NASM). Este fichero permite compilar uFMOD con NASM.
Una vez terminada la modificación del código fuente, si desea recompilar ufmod.obj, abra el fichero de procesamiento por lotes src/ufmodlib/makeobj.bat en un editor de texto plano. Todo lo contenido entre las siguientes líneas:
rem *** CONFIG START
y
rem *** CONFIG END
es configurable. Verifique los valores en la sección Pathes. Una de las opciones dice:
SET UF_NASM=\nasm
Si Ud. tiene instalado NASM, asegúrese de que el camino allí especificado apunte exactamente a la ubicación de nasmw.exe. Supongamos que NASM se encuentra instalado en D:\TOOLS\NASM. Entonces, vamos a modificar el camino de la siguiente forma:
SET UF_NASM=D:\TOOLS\NASM
No todos los caminos deben ser configurados correctamente para recompilar la librería. Por ejemplo, si Ud. prefiere usar FASM como el ensamblador por defecto, no es necesario configurar el camino de NASM. Asegúrese de que todos los caminos necesarios para recompilar la librería sean correctos. Luego, configure las opciones disponibles, de acuerdo con la siguiente tabla:

OpciónDescripciónValores disponibles
UF_RAMPEsta opción controla el acoplador de volúmen (interpolación). Esto sirve para suprimir cierto tipo de defecto de sonido conocido como clic, común en música sintetizada. Sin embargo, la interpolación en algunos casos puede generar distorsión. STRONG (fuerte) es el valor por defecto, recomendado para la mayoría de las aplicaciones. En este modo, el acoplador detecta variaciones de volúmen y las suaviza mediante interpolación lineal de 128 etapas. En modo WEAK (débil) hay sólo 16 etapas. WEAK es menos efectivo que STRONG, pero la probabilidad de distorsión también es menor. NONE deshabilita el acoplador. Si no hay interpolación, no puede haber distorsión, pero los clics quedarían al descubierto, a menos que el contenido del XM se encuentre perfectamente balanceado.NONE, WEAK, STRONG
UF_FREQFrecuencia de muestreo (en Hz). 48KHz es el valor recomendado para la mayoría de las aplicaciones.22050, 44100, 48000
UF_ASMEnsamblador. La librería uFMOD se puede compilar con diferentes ensambladores. Escoja su favorito :)MASM, NASM, FASM
UF_MODENORMAL es el valor por defecto. No tiene nada de especial. UNSAFE deshabilida la comprobación de validez del formato XM a la hora de cargar el fichero. Si Ud. está seguro de que todos los ficheros XM están correctos (puede verificarlos con Eff o con XMStrip), puede recompilar uFMOD en modo UNSAFE para reducir el tamaño de la librería y el tiempo de carga. Tenga en cuenta que ¡Un XM corrupto podría ocasionar un fallo en modo UNSAFE! El modo AC97SND hace que ufmod.obj contenga una versión especial de la librería uFMOD. Esta versión se usa en el reproductor de MP3 de Serge que viene preinstalado en KolibriOS. También puede servir en otras aplicaciones orientadas al uso de codecs. Para mayor información puede consultar el fichero ufmod-codec.h.NORMAL, UNSAFE, AC97SND

Ejecute el fichero de procesamiento por lotes para generar la librería. ¡Eso es todo!

 
Ejemplos

Hay 2 ejemplos disponibles actualmente: mini y jmp2pat. Los ejecutables precompilados se encuentran en bin/. Estos ejecutables no estan empaquetados ni comprimidos.

  • mini es el ejemplos más simple. Presenta cómo reproducir una pista XM en memoria, con manejo adecuado de errores.
  • jmp2pat es un ejemplo de uso de la función Jump2Pattern. Utiliza un XM compuesto de varias pistas, proporcionado por Kim (también conocido como norki). Consulte la sección siguiente para mayor información sobre pistas compuestas y la función Jump2Pattern.

 
Cómo lograr un ejecutable más compacto

Utilice Eff para optimizar la librería uFMOD y hacerla mas pequeña.

Si desea embeber la pista XM directamente en el ejecutable, puede intentar primero optimizar el fichero XM. Modplug Player permite comprimir un fichero XM usando ADPCM, ¡pero es un tipo de compresión con pérdidas! Utilice XMStrip para optimización sin pérdidas (en términos de calidad del sonido.)

Si tiene plena certeza de que todos los XM que va a reproducir la aplicación son válidos (ninguno puede estar corrupto o modificado), recompile la librería en modo UNSAFE.

Empaquetadores, como mtappack desarrollado por diamond, logran reducir el tamaño del ejecutable. Sin embargo, para mayor objetividad, los ejecutables de ejemplo ¡no estan comprimidos!

Ahora veamos otra técnica interesante para minimizar el tamaño de los ficheros XM:

Es posible unir varias pistas XM en un solo fichero compuesto. Ya que es posible compartir los instrumentos entre las pistas en el fichero compuesto, el tamaño resultante de este fichero puede ser mucho menor que la suma de los tamaños de los ficheros por separado. Aún sin compartir los instrumentos el tamaño debe ser menor, puesto que sólo se usa un único ejemplar de cabecera XM. Veamos un ejemplo con 3 ficheros XM:

+Fichero 1 : CABECERA_XM1 P11 P12 P13     I11 I12
+Fichero 2 : CABECERA_XM2 P21 P22 P23 P24 I21 I22 I23 I24
+Fichero 3 : CABECERA_XM3 P31             I31
+
Leyenda: CABECERA_XMn es la cabecera del n-ésimo fichero. Pni es el i-ésimo patrón del n-ésimo fichero. Ini es el i-ésimo instrumento del n-ésimo fichero.

Primero, vamos a unir los 3 ficheros en uno solo sin compartir los instrumentos:

+Fichero 4 : CABECERA_XM4 P11 P12 P13 P21 P22 P23 P24 P31 I11 I12 I21 I22 I23 I24 I31
+
Supongamos que I12 es muy similar o identico a I23 y que I24 es igual que I31. Podemos modificar P2n para hacer que utilicen I12 en vez de I23 y enlazar P31 con I24. Entonces, podremos eliminar I23 e I31:
+Fichero 4 : CABECERA_XM4 P11 P12 P13 P21 P22 P23 P24 P31 I11 I12 I21 I22 I24
+
Al final tendrá que modificar los comandos de ciclos y saltos de patrones y las referencias a instrumentos en los "ficheros" 2 y 3. Obviamente, también es posible unir apenas 2 o más de 3 ficheros, aunque el formato XM establece límites para el número máximo de patrones e instrumentos en un fichero. Esa es, en general, la idea. Es necesario saber usar un tracker para realizar todas estas operaciones sobre un fichero XM. Una vez unidas todas las pistas en un fichero compuesto, podrá iniciar la reproducción con un único llamado a uFMOD_PlaySong y "activar" las pistas por separado con llamados a uFMOD_Jump2Pattern. Por ejemplo, uFMOD_Jump2Pattern(3) pasaria a reproducir la segunda pista, uFMOD_Jump2Pattern(7) saltaria a la tercera y uFMOD_Jump2Pattern(0) regresaria a la primera. Los indices exactos en cada caso dependen de los patrones asignados con el tracker. El ejemplo jmp2pat emplea esta clase de optimización.

El uso de Jump2Pattern tiene otra ventaja: la conmutación de pistas se realiza mucho más rápido (casi de inmediato) que el proceso habitual de parar la pista actual y cargar una pista nueva. Por lo tanto, puede usar esta técnica cuando necesite conmutar las pistas de audio de la manera más rápida posible.

 
Preguntas frecuentes

P: ¿Es uFMOD gratis para cualquier tipo de uso, incluido el comercial?
R: Si, actualmente así es.

P: ¿En donde puedo conseguir música en formato XM?
R: En The Mod Archive hay un enorme archivo de música gratis en formatos XM, IT, S3M y MOD. Puede usar el Open Modplug Tracker para convertir ficheros IT, S3M y MOD a formato XM sin perder la calidad. Hay muchos compositores talentosos en la red dispuestos a compartir su música de manera gratuita. ¡Pero no olvide el copyright!

P: ¿Existe alguna relación entre el proyecto uFMOD y las librerías FMOD y/o miniFMOD de Firelight Technologies®?
R: Ya no. Hasta el año 2004 uFMOD se basó en el código fuente abierto de la librería miniFMOD. Desde ese entonces, el código fuente de uFMOD fue completamente reescrito, optimizado y depurado. También fueron agregadas muchas funciones nuevas. Por lo tanto, la versión actual de uFMOD no tiene relación alguna con FMOD o miniFMOD.

P: Algunas librerías aseguran incrementar el tamaño del ejecutable en N kilobytes. ¿En cuanto se incrementa el tamaño de un ejecutable con uFMOD?
R: No es posible establecer un valor exacto, ya que el tamaño depende de muchos factores, a saber: funciones usadas (en especial, si se utiliza la herramienta EFF), código de la aplicación principal, tamaño del fichero XM (cuando el XM es embebido en el ejecutable). También depende de las opciones del linker. El ejemplo bin/mini ocupa únicamente 4.768 bytes sin compresión.

P: ¿En dónde puedo conseguir la especificación oficial del formato XM?
R: No existe especificación oficial actualizada. En cambio, puede estudiar el documento "The Unofficial XM File Format Specification: FastTracker II, ADPCM and Stripped Module Subformats" (sólo en inglés). Este documento describe la mayoría de los aspectos relevantes del formato XM original, al igual que todas las extensiones extraoficiales actualmente soportadas por uFMOD. Los códigos fuente de ModPlug (en C++) sirven de referencia sobre formatos derivados del MOD, XM, IT, S3M y muchos otros.

 
Agradecemos a

antarman, Barracuda, bogrus, chris_b, cresta, dododo, flaith, Four-F, GL#0M, norki, q_q, SofT MANiAC, S_T_A_S_, voodooattack y yoxola por reportar errores, sugerir nuevas ideas, aportar ejemplos de uso y de otras formas ayudarnos a mejorar uFMOD.

[WASM.RU] y SourceForge.net por el soporte y hosting.

 
Copyright

Los códigos fuente y ficheros binarios de uFMOD © 2005 - 2007 Asterix y Quantum.
Todos los derechos reservados.

Pistas de ejemplo:

  • Minimal III © 2006 - 2007 SofT MANiAC (CoolPHat).
  • BlitzXMK.XM del ejemplo Jump2Pat © 2007 Kim (también conocido como norki).
¿Desea reportar un error? ¿Tiene preguntas o sugerencias?
¿Está desarrollando un gran proyecto usando uFMOD? Por favor, mantenganos al tanto: ufmod@users.sf.net
\ No newline at end of file diff --git a/programs/develop/libraries/ufmod/readme_ru.htm b/programs/develop/libraries/ufmod/readme_ru.htm new file mode 100644 index 0000000000..7e9dc81864 --- /dev/null +++ b/programs/develop/libraries/ufmod/readme_ru.htm @@ -0,0 +1,33 @@ +uFMOD
[ English | Español | Pyccκuú ]

μFMOD v1.25 для KolibriOS

uFMOD - это компактная, шустрая, надёжная, мультиплатформенная библиотека для качественного воспроизведения аудио в формате XM, разработанная полностью на ассемблере. XM может храниться в отдельном файле или предварительно загружен в память. Поддерживаются также битые и не совсем стандартные файлы. Библиотека распространяется с открытым исходным кодом и примерами для следующих компиляторов: FASM, MASM32 и NASM.

Версия для KolibriOS должна исправно работать на любых конфигурациях, удовлетворяющих следующим требованиям:

  1. Минимальная ревизия SVN ядра и драйверов KolibriOS: 574. Последние версии дистрибутивов и обновления SVN можно скачать с официального сайта: www.kolibrios.org.
  2. Звуковая карточка, поддерживаемая аудио-библиотекой Infinity Sound - это встроенный звуковой драйвер KolibriOS. На данный момент драйвер поддерживает многие встроенные кодеки совместимые со стандартом AC'97.
Ваша конфигурация удовлетворяет вышеперечисленным требованиям, но uFMOD на ней отказывается работать? - Пожалуйста, сообщите нам об этом (контактные данные приводятся в низу данной страницы).

 
Прежде чем начать

KolibriOS - это операционная система, написанная на ассемблере. Поэтому у неё такой маленький размер и такая высокая производительность, но только не в ущерб возможностям этой полноценной операционки, как Вы, возможно, уже убедились. Аналогичная идеология поддерживается и в проекте uFMOD ;)

Основную часть данного руководства можно успешно выполнить прямо в самой системе Kolibri. Тем не менее, так как многие новички в программировании Kolibri предпочитают компилировать свои проекты в Windows и потом переносить их в Kolibri для тестирования, в данном руководстве мы применим кросскомпиляцию.

 
Утилиты

Есть пара бесплатных утилит для использования совместно с uFMOD: XMStrip и Eff. Пока что ни одна из них не портирована в Kolibri. Поэтому можете скачать любой из других дистрибутивов uFMOD (для Win32, Linux или Unix/BSD) и использовать утилиты кроссплатформенно. Независимо от выбранного дистрибутива, обе утилиты совмещают в себе консольный и графический (GUI) интерфейсы. Режим GUI интуитивно понятен. Давайте рассмотрим консольный интерфейс.

 SVN  Исходники доступны через SVN 

XMStrip обрабатывает заданный XM файл с целью уменьшения размера, без потери качества звучания. В процессе обработки, XMStrip удаляет неиспользующиеся инструменты и партитуры, вырезает комментарии и перепаковывает внутренние структуры файла для уменьшения размера и повышения скорости загрузки. Для начала, откроем консоль и введём xmstrip /h, чтобы получить следующее сообщение:

 USAGE:  xmstrip [options] file [output]
+         file   - input file name.
+         output - optional output file name.          
+ options:
+  /c - clean only (don't strip)
+ When [output] is not specified, XMSTRIP
+ attempts to overwrite the input. If file
+ name contains spaces, enclose it in "".

Если имя сохраняемого файла не указано, XMStrip перезапишет заданный файл. Если имя файла содержит пробелы, его необходимо заключить в кавычки ("").

Имейте в виду, что другие проигрыватели XM файлов, возможно, откажутся воспроизводить файл, обработанный XMStrip. Опция /c предусмотрена для восстановления таких файлов или обработки обычных XM файлов, которые планируется проигрывать не только в uFMOD.

Eff предназначается для опытных кодеров, желающих сэкономить каждый байт в своих приложениях. Для этого нужно выделить только те функции uFMOD, которые действительно будут использованы в целевом приложении, перекомпилировать библиотеку и получить наименьший возможный размер. Вот, что выдаёт eff /h :

 USAGE:  eff [options] file
+         file - input file name
+         options:
+          /Dm - generate a masm32/tasm dump
+          /Dd - generate a Pascal (Delphi) dump
+          /Dc - generate a C/C++ dump
+          /Ds - generate an RCDATA resource dump
+          /Di - disable infoAPI:
+                    uFMOD_GetStats, uFMOD_GetRowOrder,
+                    uFMOD_GetTitle and uFMOD_GetTime
+          /Dp - disable uFMOD_Pause, uFMOD_Resume
+                    and XM_SUSPENDED
+          /Dv - disable volume control
+          /Dj - disable Jump2Pattern
+          /Df - disable loading XM from file
+          /Dl - disable XM_NOLOOP
+          /M  - mark & clear unused chunks of
+                data in a masm32/tasm compatible dump

Последний параметр - имя XM файла, на использование которого ориентируются следующие параметры оптимизации:

  • /Dm создаёт текстовый дамп данного XM файла для использования в исходнике на MASM32 или TASM. Синтаксис совместим с FASM и NASM. Вместе с тем, и FASM и NASM позволяют включать в исполнимый образ содержимое произвольных файлов напрямую. Данную опцию необходимо включить при использовании ключа /M (см. ниже).
  • /Dd и /Dc создают подобные дампы, но для Pascal (Delphi, Kylix, FreePascal) или C/C++ соответственно.
  • /Ds создаёт аналогичный дамп в формате RCDATA, который используется в скриптах описания ресурсов (*.rc). В KolibriOS данная опция бесполезна.
  • Ключ /Di отключает все информативные функции: uFMOD_GetStats, uFMOD_GetRowOrder, uFMOD_GetTitle и uFMOD_GetTime. Это даёт выигрыш не только в размере библиотеки, но и в скорости.
  • /Dp отключает функции uFMOD_Pause и uFMOD_Resume и поддержку флага XM_SUSPENDED. Если Вы не собираетесь использовать pause/resume, включите этот ключ в параметры командной строки для экономии ещё нескольких байт.
  • uFMOD_SetVolume увеличивает не только размер библиотеки, но ещё и загрузку CPU. Добавьте ключ /Dv, чтобы отключить эту функцию и сэкономить несколько байт и тактов ;)
  • /Dj отключает функцию Jump2Pattern. Данная функция предназначена для очень специфичных задач и, поэтому, используется редко. В разделе "Дополнительное уменьшение размера" подробно описано использование Jump2Pattern.
  • Вам не нужна поддержка загрузки файлов - только проигрывание из памяти? Тогда, не задумываясь, включайте /Df для дополнительного уменьшения размера.
  • Ключ /Dl (маленькая L) отключает поддержку флага XM_NOLOOP (и, следовательно, благотворно влияет на размер и скорость).
  • Совсем "экстремальный" вид оптимизации предусмотрен специально для ассемблерщиков. В каждом XM файле есть цепочки байт, которые зарезервированы на будущее, или просто содержат комментарии или любую другую, не имеющую отношения к проигрыванию, информацию. /M выделяет эти "дыры", предоставляя возможность заполнить их полезной информацией (разместить данные или даже код). Пример src/Masm32/ как раз использует эту опцию.
Eff создаёт файл EFF.INC и, согласно заданным опциям, текстовый дамп. Рассмотрим несколько одинаковых примеров (все верны):

eff /Dmpvjfl /M file.xm
eff /M /Dm /Dp /Dv /Dj /Df /Dl file.xm
eff -M -Dmpvjfl file.xm

Любой из предыдущих примеров создаст ассемблерный дамп, выделит "дыры" и предварительно заполнит их нулями. Файл EFF.INC содержит список эффектов XM, которые действительно используются в заданном файле, и некоторые дополнительные флаги для отключения pause/resume, регулятора громкости, Jump2Pattern, поддержки файлов и XM_NOLOOP. Поместите этот новый EFF.INC в src/ufmodlib/src/ и перекомпилируйте библиотеку (в следующем разделе освещены некоторые вопросы касающиеся компиляции исходников библиотеки). Теперь у Вас имеется свой собственный оптимизированный билд uFMOD, но имейте в виду, что в этом билде включены не все эффекты XM. Поэтому, не следует использовать его для проигрывания других XM файлов, отличных от обработанного утилитой Eff!

 
Компиляция библиотеки

Необходимость компиляции исходников библиотеки uFMOD возникает при использовании утилиты Eff, а также для включения особых режимов и опций, которые отключены в сборке по умолчанию (см. таблицу Опции ниже). Для тех, кто хочет попрактиковаться в ассемблере и/или разобраться в библиотеке на самом низком уровне, и предназначен данный раздел.

Исходники uFMOD расположены в src/ufmodlib/src/:

  • eff.inc - этот заголовочный файл создаётся утилитой Eff. Не рекомендуется редактировать данный файл вручную! Настоящие кодеры не обращают внимания на подобные предупреждения, но всё же... :)
  • ufmod.inc содержит подробное описание API uFMOD для использования в проектах на ASM/C/C++.
  • ufmod-codec.h содержит описание альтернативного API uFMOD, которое предоставляется библиотекой в режиме AC97SND - этот режим предназначен для использования в плеерах аудио-файлов, вроде AC'97 MP3 Player Serge'я.
  • В core.asm находится большая часть исходного кода uFMOD. Этот самый файл присутствует во всех дистрибутивах библиотеки: KolibriOS, Unix/BSD, Linux и Win32. Загрузка XM-файла, смешивание каналов, наложение эффектов и многие другие общие для всех дистрибутивов алгоритмы реализованы в данном файле.
  • ufmod.asm содержит платформозависимые процедуры: файловый В/В, общение со звуковым драйвером и т.д. Поэтому, содержимое данного файла различается в дистрибутивах для разных ОСей.
  • fasm.asm хранит определения констант, структур и т.д. под синтаксис Flat Assembler (FASM). Этот файл позволяет собирать библиотеку с помощью FASM.
  • masm.asm хранит определения констант, структур и т.д. под синтаксис MASM32. Этот файл позволяет собирать библиотеку с помощью MASM32.
  • nasm.asm хранит определения констант, структур и т.д. под синтаксис Netwide Assembler (NASM). Этот файл позволяет собирать библиотеку с помощью NASM.
Следующим после внесения изменений в исходники шагом является компиляция. Чтобы пересобрать ufmod.obj, сначала откройте в текстовом редакторе батник src/ufmodlib/makeobj.bat. Всё, что находится между следующих строк:
rem *** CONFIG START
и
rem *** CONFIG END
подлежит настройке. Обратите внимание на секцию Pathes. Там есть такая опция:
SET UF_NASM=\nasm
Если у Вас установлен NASM, удостоверьтесь, что путь в данной опции указывает точно туда, где находится nasmw.exe. Допустим, NASM установлен в D:\TOOLS\NASM. В таком случае, необходимо скорректировать опцию следующим образом:
SET UF_NASM=D:\TOOLS\NASM
Не все пути необходимы для успешной компиляции. Например, если Вы намерены использовать FASM, не нужно настраивать UF_NASM. Проверьте правильность всех путей, необходимых для компиляции. Теперь настройте параметры конфигурации, согласно следующей таблице:

ОпцияОписаниеЗначения
UF_RAMPДанная опция позволяет настроить механизм интерполяции, который предназначен для погашения щелчков - резкие перепады амплитуды сигнала, характерные для трекерской музыки. С другой стороны, интерполяция вносит искажение в высокочастотные спектральные составляющие сигнала, что иногда бывает заметно. STRONG - это значение по умолчанию, рекомендуемое для большинства приложений. В данном режиме миксер сглаживает резкие перепады амплитуды, применяя линейную 128-ступенчатую интерполяцию. WEAK накладывает лишь 16 ступеней - этот режим менее эффективен, чем STRONG, но зато вероятность деградации сигнала в этом режиме ниже. NONE вообще отключает интерполяцию. Без сглаживания не будет и деградации, но большинство композиций без сглаживания будет звучать заметно хуже из-за наложения щелчков. Особым образом сбалансированные композиции без сглаживания могут звучать лучше.NONE, WEAK, STRONG
UF_FREQЧастота дискретизации (в Гц). 48КГц является значением, рекомендуемым для большинства приложений.22050, 44100, 48000
UF_ASMАссемблер. Да, uFMOD можно собирать разными ассемблерами - выбирайте тот, который больше нравится :)MASM, NASM, FASM
UF_MODENORMAL - это значение по умолчанию. Ничего особенного. UNSAFE отключает проверку правильности формата XM перед загрузкой композиции. Если Вы уверены, что все композиции, которые будут проигрываться в вашем приложении, корректны (правильность формата XM-файла можно проверить в Eff или XMStrip), можете пересобрать библиотеку в режиме UNSAFE, чтобы выйграть в размере и скорости загрузки. Имейте в виду, что загрузка неправильного XM-файла в режиме UNSAFE может привести к краху! В режиме AC97SND библиотека представляет собой кодек для проигрывателя AC'97 MP3 Player Serge'я. Данный режим может быть полезен и для других проектов сходной направленности. Описание API данного режима можно найти в ufmod-codec.h.NORMAL, UNSAFE, AC97SND

Запустите батник, чтобы собрать библиотеку. Вот и всё!

 
Примеры

В данный дистрибутив вошли 2 примера: mini и jmp2pat. Откомпилированные экзешники находятся в bin/. Заметьте, что экзешники представлены без сжатия.

  • mini - это простейший пример фонового проигрывания музыки из памяти.
  • jmp2pat - это пример использования функции Jump2Pattern. В данном примере проигрывается композитный XM, любезно предоставленный товарищем Kim (он же norki). Описание техники создания и использования подобных композиций можно найти в следующем разделе.

 
Дополнительное уменьшение размера

Утилита Eff предназначена для оптимизации и уменьшения размера библиотеки uFMOD.

Если Вы собираетесь включить XM статически в экзешник, можете попробовать оптимизировать сначала сам XM. Modplug Player умеет сжимать XM-композиции по схеме APDCM, но учтите, что этот тип сжатия пагубно влияет на качество звучания! Утилита XMStrip перепаковывает XM файл без потери качества.

Если Вы уверены в корректности формата всех композиций, которые будут использованы в приложении, можете пересобрать библиотеку в режиме UNSAFE.

Упаковщики, вроде mtappack diamond'а, умеют ужимать экзешники. Тем не менее, для наглядности, все примеры предоставлены без сжатия!

Есть ещё один хитрый способ оптимизации размера XM-файлов, который заключается в совмещении сразу нескольких композиций в одном файле. При этом можно удалять лишние экземпляры повторяющихся инструментов, если таковые имеются, что очень заметно сказывается на размере конечного файла. Даже без оптимизации инструментов размер композитного файла должен получиться меньше суммы размеров отдельных файлов, так как заголовки всех файлов заменяются одним общим. Давайте рассмотрим пример с тремя файлами:

+Файл 1 : XM1_HEADER P11 P12 P13     I11 I12
+Файл 2 : XM2_HEADER P21 P22 P23 P24 I21 I22 I23 I24
+Файл 3 : XM3_HEADER P31             I31
+
Пояснение: XMn_HEADER - это заголовок n-ного файла. Pnm - это m-ная партитура n-ного файла. Inm - это m-ный инструмент n-ного файла.

Для начала, совместим все 3 композиции без оптимизации инструментов:

+Файл 4 : XM4_HEADER P11 P12 P13 P21 P22 P23 P24 P31 I11 I12 I21 I22 I23 I24 I31
+
Представьте, что I12 очень похож или идентичен I23; I24 и I31 тоже практически одинаковы. Мы можем заставить партитуры P2n использовать I12 вместо I23, а P31 переключить на I24. Тогда мы сможем удалить I23 и I31:
+Файл 4 : XM4_HEADER P11 P12 P13 P21 P22 P23 P24 P31 I11 I12 I21 I22 I24
+
Необходимо скорректировать команды зацикливания и ссылки на партитуры в композициях 2 и 3 после совмещения. Конечно, можно совмещать и большее количество композиций, но формат XM имеет ограничения на максимальное количество партитур и инструментов в файле. Все операции над XM-файлами надлежит проводить в специальном ПО - трекере. Для проигрывания композитного файла достаточно одного вызова функции uFMOD_PlaySong. Далее нужно использовать функцию uFMOD_Jump2Pattern для проигрывания отдельных композиций из общего файла в произвольной последовательности. Например, uFMOD_Jump2Pattern(3) переключится на вторую композицию, uFMOD_Jump2Pattern(7) начнёт проигрывать третью, а uFMOD_Jump2Pattern(0) вернётся обратно к первой. Точные значения индексов стартовых партитур каждой композиции после совмещения легко вычислить в уме, но можно и в трекере подсмотреть :) Пример jmp2pat использует данный способ оптимизации.

В использовании Jump2Pattern есть ещё один плюс - переключение происходит гораздо быстрее (практически моментально) чем при остановке текущей композиции и последующей загрузке новой. Можете брать на вооружение эту фишку для реализации решений, требующих максимально быстрого переключения музыкального фона.

 
ЧаВо

В: Библиотека uFMOD действительно бесплатна для любого использования, включая коммерческое?
О: Да, текущая версия абсолютно бесплатна для использования в любых целях.

В: Где бы достать композиции в формате XM?
О: The Mod Archive содержит внушительных размеров архив с бесплатными трекерскими композициями в формате XM, IT, S3M и MOD. Open Modplug Tracker умеет конвертировать IT, S3M и MOD в XM без потери качества. В сети много талантливых композиторов, которые бесплатно выкладывают свои работы. Не забывайте указывать соответствующие пометки об авторстве!

В: Существует ли какая-либо связь между проектом uFMOD и разработками Firelight Technologies®: FMOD и miniFMOD?
О: На данный момент никакой связи уже нет. До 2004го года включительно проект uFMOD основывался на исходных кодах библиотеки miniFMOD. С тех пор, код uFMOD был полностью переписан, оснащён новыми функциями, оптимизирован и отлажен. Таким образом, uFMOD более не связан ни с FMOD, ни с miniFMOD.

В: Некоторые разработчики утверждают, что их библиотеки увеличивают экзешники на N-ное количество килобайт. На сколько килобайт увеличивается размер экзешника при использовании uFMOD?
О: Точного ответа на этот вопрос нет, так как этот размер зависит от многих факторов: используемые характеристики библиотеки (особенно при использовании утилиты Eff), основной код программы, размер XM-файла (если XM включается в образ экзешника). Размер также зависит от опций линкера. Пример bin/mini занимает 4768 байт без сжатия.

В: Где можно раздобыть официальное описание формата XM?
О: Полного официального описания современного формата XM нет. Позвольте предложить взамен этот документ: "The Unofficial XM File Format Specification: FastTracker II, ADPCM and Stripped Module Subformats" (только на англ.). В данном документе описаны многие тонкости работы с форматом XM, включая все нестандартные расширения, которые на данный момент поддерживает uFMOD. К тому же, из исходников ModPlug (на C++) можно почерпнуть массу полезной информации по трекерским форматам файлов.

 
Благодарности

antarman, Barracuda, bogrus, chris_b, cresta, dododo, flaith, Four-F, GL#0M, norki, q_q, SofT MANiAC, S_T_A_S_, voodooattack и yoxola за помощь в устранении ошибок, предложения по улучшению библиотеки, примеры использования под разные компиляторы и всё остальное, так или иначе помогающее нам в развитии проекта.

[WASM.RU] и SourceForge.net за поддержку и хостинг.

 
Автор©тво

Исходные коды uFMOD и сопровождающие утилитарные приложения © 2005 - 2007 Asterix и Quantum.
Все права защищены.

Композиции:

  • Minimal III © 2006 - 2007 SofT MANiAC (CoolPHat).
  • BlitzXMK.XM из примера Jump2Pat © 2007 Kim (он же - norki).
Нашли ошибку? Желаете задать вопрос разработчикам или высказать предложение по улучшению библиотеки?
Разрабатываете интересный проект с использованием uFMOD? Вам сюда: ufmod@users.sf.net
\ No newline at end of file diff --git a/programs/develop/libraries/ufmod/ufmod-codec.h b/programs/develop/libraries/ufmod/ufmod-codec.h new file mode 100644 index 0000000000..d01b16d75b --- /dev/null +++ b/programs/develop/libraries/ufmod/ufmod-codec.h @@ -0,0 +1,89 @@ +/* + uFMOD API reference (AC97SND mode) + ========================================================= + + NOTE: ufmod.obj should be rebuilt setting UF_MODE=AC97SND + in order to make it usable in AC97SND player. + + The Infinity Sound driver handle should be available as + a public symbol named hSound. It is so when using Serge's + sound.lib. +*/ + +#ifdef __cplusplus + extern "C" { +#endif + +/* HANDLE uFMOD_LoadSong(char *lpXM); + --- + Description: + --- + Loads the given XM song and starts playing it as soon as you + call uFMOD_WaveOut for the first time. It will stop any + currently playing song before loading the new one. Heap should + be initialized before calling this function! + --- + Parameters: + --- + lpXM + Specifies the filename of the song to load. + --- + Return Values: + --- + On success, returns a non zero value. Returns 0 on failure. +*/ +int __cdecl uFMOD_LoadSong(char*); + +/* int uFMOD_WaveOut(SNDBUF hBuff) + --- + Description: + --- + Updates the internal playback buffer. + --- + Parameters: + --- + hBuff + The Infinity Sound buffer to update. + --- + Remarks: + --- + Playback doesn't actually begin when calling uFMOD_LoadSong, + but when calling uFMOD_WaveOut after a successful uFMOD_LoadSong + call. Afterwards, you should call uFMOD_WaveOut repeatedly at + least once every 250 ms to prevent "buffer underruns". + uFMOD_WaveOut is a non-blocking function. + --- + Return Values: + --- + Returns non zero on error. +*/ +int __cdecl uFMOD_WaveOut(unsigned int); + +/* void uFMOD_StopSong(void) + --- + Description: + --- + Stops the currently playing song, freeing the associated + resources. + --- + Remarks: + --- + Does nothing if no song is playing at the time the call is made. +*/ +void __cdecl uFMOD_StopSong(); + +/* unsigned char* _uFMOD_GetTitle(void) + --- + Description: + --- + Returns the current song's title. + --- + Remarks: + --- + Not every song has a title, so be prepared to get an empty string. +*/ +unsigned char* __cdecl uFMOD_GetTitle(); + +#ifdef __cplusplus + } +#endif diff --git a/programs/develop/libraries/ufmod/ufmod.asm b/programs/develop/libraries/ufmod/ufmod.asm new file mode 100644 index 0000000000..26f684187c --- /dev/null +++ b/programs/develop/libraries/ufmod/ufmod.asm @@ -0,0 +1,1104 @@ +; 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 diff --git a/programs/develop/libraries/ufmod/ufmod.inc b/programs/develop/libraries/ufmod/ufmod.inc new file mode 100644 index 0000000000..a69a461714 --- /dev/null +++ b/programs/develop/libraries/ufmod/ufmod.inc @@ -0,0 +1,253 @@ +; uFMOD API reference (NORMAL/UNSAFE mode) +; ==================================================================== + +; NOTE: All functions follow the cdecl calling convention! +; _uFMOD_LoadSong +; _uFMOD_WaveOut +; _uFMOD_StopSong +; _uFMOD_Jump2Pattern +; _uFMOD_Pause +; _uFMOD_Resume +; _uFMOD_GetStats +; _uFMOD_GetRowOrder +; _uFMOD_GetTime +; _uFMOD_GetTitle +; _uFMOD_SetVolume + +; ==================================================================== +; HANDLE _uFMOD_LoadSong( +; void *lpXM, +; void *param, +; int fdwSong +; ) +; --- +; Description: +; --- +; Loads the given XM song and starts playing it as soon as you +; call _uFMOD_WaveOut for the first time. Playback won't begin +; if XM_SUSPENDED flag is specified. It will stop any currently +; playing song before loading the new one. +; --- +; Parameters: +; --- +; lpXM +; Specifies the song to load. If this parameter is 0, any +; currently playing song is stopped. In such a case, function +; does not return a meaningful value. fdwSong parameter +; determines whether this value is interpreted as a filename +; or as a pointer to an image of the song in memory. +; param +; If XM_MEMORY is specified, this parameter should be the size +; of the image of the song in memory. +; If XM_FILE is specified, this parameter is ignored. +; fdwSong +; Flags for playing the song. The following values are defined: +; XM_FILE lpXM points to filename. param is ignored. +; XM_MEMORY lpXM points to an image of a song in memory. +; param is the image size. Once, _uFMOD_LoadSong +; returns, it's safe to free/discard the memory +; buffer. +; XM_NOLOOP An XM track plays repeatedly by default. Specify +; this flag to play it only once. +; XM_SUSPENDED The XM track is loaded in a suspended state, +; and will not play until the _uFMOD_Resume function +; is called. This is useful for preloading a song +; or testing an XM track for validity. +; --- +; Return Values: +; --- +; On success, returns the handle of the Infinity Sound driver. +; Returns 0 on failure. + +; ==================================================================== +; int _uFMOD_WaveOut(void) +; --- +; Description: +; --- +; Updates the internal playback buffer. +; --- +; Remarks: +; --- +; This function should be called from the same thread +; _uFMOD_LoadSong was previously called. Playback doesn't actually +; begin when calling _uFMOD_LoadSong, but when calling _uFMOD_WaveOut +; after a successful _uFMOD_LoadSong call. Afterwards, you should +; call _uFMOD_WaveOut repeatedly at least once every 250 ms to +; prevent "buffer underruns". +; _uFMOD_WaveOut is a non-blocking function. The accuracy of the +; InfoAPI functions (_uFMOD_GetStats, _uFMOD_GetRowOrder and +; _uFMOD_GetTime) depends on the periodicity of this function being +; invoked. +; --- +; Return Values: +; --- +; Returns non zero on error. + +; ==================================================================== +; void _uFMOD_StopSong(void) +; --- +; Description: +; --- +; Stops the currently playing song, freeing the associated +; resources. +; --- +; Remarks: +; --- +; Does nothing if no song is playing at the time the call is made. + +; ==================================================================== +; void _uFMOD_Jump2Pattern( +; unsigned int pat +; ) +; --- +; Description: +; --- +; Jumps to the specified pattern index. +; --- +; Parameters: +; --- +; pat +; Next zero based pattern index. +; --- +; Remarks: +; --- +; uFMOD doesn't automatically perform Note Off effects before jumping +; to the target pattern. In other words, the original pattern will +; remain in the mixer until it fades out. You can use this feature to +; your advantage. If you don't like it, just insert leading Note Off +; commands in all patterns intended to be used as _uFMOD_Jump2Pattern +; targets. +; if the pattern index lays outside of the bounds of the pattern order +; table, calling this function jumps to pattern 0, effectively +; rewinding playback. + +; ==================================================================== +; void _uFMOD_Pause(void) +; --- +; Description: +; --- +; Pauses the currently playing song, if any. +; --- +; Remarks: +; --- +; While paused you can still control the volume (_uFMOD_SetVolume) and +; the pattern order (_uFMOD_Jump2Pattern). The RMS volume coefficients +; (_uFMOD_GetStats) will go down to 0 and the progress tracker +; (_uFMOD_GetTime) will "freeze" while the song is paused. +; _uFMOD_Pause doesn't perform the request immediately. Instead, it +; signals to pause when playback reaches next chunk of data. +; This way, _uFMOD_Pause performs asynchronously and returns very fast. +; It is not cumulative. So, calling _uFMOD_Pause many times in a row +; has the same effect as calling it once. +; You shouldn't stop calling _uFMOD_WaveOut while the song is paused! + +; ==================================================================== +; void _uFMOD_Resume(void) +; --- +; Description: +; --- +; Resumes the currently paused song, if any. +; --- +; Remarks: +; --- +; _uFMOD_Resume doesn't perform the request immediately. Instead, it +; signals to resume when _uFMOD_WaveOut is called again. _uFMOD_Resume +; is not cumulative. So, calling it many times in a row has the same +; effect as calling it once. + +; ==================================================================== +; unsigned int _uFMOD_GetStats(void) +; --- +; Description: +; --- +; Returns the current RMS volume coefficients in (L)eft and (R)ight +; channels. +; low-order word: RMS volume in R channel +; hi-order word: RMS volume in L channel +; Range from 0 (silence) to $7FFF (maximum) on each channel. +; --- +; Remarks: +; --- +; This function is useful for updating a VU meter. It's recommended +; to rescale the output to log10 (decibels or dB for short), because +; human ears track volume changes in a dB scale. You may call +; _uFMOD_GetStats() as often as you like, but take in mind that uFMOD +; updates both channel RMS volumes at the same rate _uFMOD_WaveOut +; function is called. In other words, you should call _uFMOD_WaveOut +; more often to increase the accuracy of _uFMOD_GetStats. + +; ==================================================================== +; unsigned int _uFMOD_GetRowOrder(void) +; --- +; Description: +; --- +; Returns the currently playing row and order. +; low-order word: row +; hi-order word: order +; --- +; Remarks: +; --- +; This function is useful for synchronization. uFMOD updates both +; row and order values at the same rate _uFMOD_WaveOut function is +; called. In other words, you should call _uFMOD_WaveOut more often +; to increase the accuracy of _uFMOD_GetRowOrder. + +; ==================================================================== +; unsigned int _uFMOD_GetTime(void) +; --- +; Description: +; --- +; Returns the time in milliseconds since the song was started. +; --- +; Remarks: +; --- +; This function is useful for synchronizing purposes. Multimedia +; applications can use _uFMOD_GetTime to synchronize GFX to sound, +; for example. An XM player can use this function to update a progress +; meter. + +; ==================================================================== +; unsigned char* _uFMOD_GetTitle(void) +; --- +; Description: +; --- +; Returns the current song's title. +; --- +; Remarks: +; --- +; Not every song has a title, so be prepared to get an empty string. + +; ==================================================================== +; void _uFMOD_SetVolume( +; unsigned int vol +; ) +; --- +; Description: +; --- +; Sets the global volume. The volume scale is linear. +; --- +; Parameters: +; --- +; vol +; New volume. Range: from uFMOD_MIN_VOL (muting) to uFMOD_MAX_VOL +; (maximum volume). Any value above uFMOD_MAX_VOL maps to maximum +; volume. +; --- +; Remarks: +; --- +; uFMOD internally converts the given values to a logarithmic scale (dB). +; Maximum volume is set by default. The volume value is preserved across +; _uFMOD_LoadSong calls. You can set the desired volume level before +; actually starting to play a song. +; You can use Infinity Sound API to control the L and R channels volumes +; separately. It also has a wider range than _uFMOD_SetVolume, sometimes +; allowing to amplify the sound volume as well, as opposed to +; _uFMOD_SetVolume only being able to attenuate it. + +XM_MEMORY equ 1 +XM_FILE equ 2 +XM_NOLOOP equ 8 +XM_SUSPENDED equ 16 +uFMOD_MIN_VOL equ 0 +uFMOD_MAX_VOL equ 25 +uFMOD_DEFAULT_VOL equ 25