bits 32 %define WRK ebp ; // alias for unit workspace %define VAL esi ; // alias for unit values (transformed/untransformed) %define COM ebx ; // alias for instrument opcodes %include "4klang.inc" ;// conditional defines %ifdef GO4K_USE_VCO_SHAPE %define INCLUDE_WAVESHAPER %endif %ifdef GO4K_USE_DST %define INCLUDE_WAVESHAPER %endif %ifdef GO4K_USE_VCO_MOD_PM %define PHASE_RENORMALIZE %endif %ifdef GO4K_USE_VCO_PHASE_OFFSET %define PHASE_RENORMALIZE %endif %ifdef GO4K_USE_ENVELOPE_RECORDINGS %define GO4K_USE_BUFFER_RECORDINGS %endif %ifdef GO4K_USE_NOTE_RECORDINGS %define GO4K_USE_BUFFER_RECORDINGS %endif ; //======================================================================================== ; // .bss section ; //======================================================================================== %ifdef USE_SECTIONS section .g4kbss1 bss align=1 %else section .bss %endif ; // the one and only synth object %if MAX_VOICES > 1 go4k_voiceindex resd 16 %endif go4k_transformed_values resd 16 go4k_synth_wrk resb go4k_synth.size global _go4k_delay_buffer_ofs _go4k_delay_buffer_ofs resd 1 global _go4k_delay_buffer _go4k_delay_buffer resd 16*16*go4kDLL_wrk.size %ifdef AUTHORING global __4klang_current_tick __4klang_current_tick resd 0 %endif %ifdef GO4K_USE_ENVELOPE_RECORDINGS global __4klang_envelope_buffer __4klang_envelope_buffer resd ((MAX_SAMPLES)/8) ; // samples every 256 samples and stores 16*2 = 32 values %endif %ifdef GO4K_USE_NOTE_RECORDINGS global __4klang_note_buffer __4klang_note_buffer resd ((MAX_SAMPLES)/8) ; // samples every 256 samples and stores 16*2 = 32 values %endif ; //======================================================================================== ; // .g4kdat section (initialized data for go4k) ; //======================================================================================== %ifdef USE_SECTIONS section .g4kdat1 data align=1 %else section .data %endif ; // some synth constants go4k_synth_commands dd 0 dd _go4kENV_func@0 dd _go4kVCO_func@0 dd _go4kVCF_func@0 dd _go4kDST_func@0 dd _go4kDLL_func@0 dd _go4kFOP_func@0 dd _go4kFST_func@0 dd _go4kPAN_func@0 dd _go4kOUT_func@0 dd _go4kACC_func@0 dd _go4kFLD_func@0 %ifdef GO4K_USE_GLITCH dd _go4kGLITCH_func@0 %else dd _go4kFLD_func@0 %endif %ifdef GO4K_USE_FSTG dd _go4kFSTG_func@0 %endif %ifdef USE_SECTIONS section .g4kdat2 data align=1 %else section .data %endif %ifdef GO4K_USE_16BIT_OUTPUT c_32767 dd 32767.0 %endif c_i128 dd 0.0078125 c_RandDiv dd 65536*32768 c_0_5 dd 0.5 %ifdef GO4K_USE_VCO_GATE c_16 dd 16.0 %endif %ifdef GO4K_USE_DLL_CHORUS DLL_DEPTH dd 1024.0 %endif %ifdef GO4K_USE_DLL_DC_FILTER c_dc_const dd 0.99609375 ; R = 1 - (pi*2 * frequency /samplerate) %else %ifdef GO4K_USE_VCO_GATE c_dc_const dd 0.99609375 ; R = 1 - (pi*2 * frequency /samplerate) %endif %endif global _RandSeed _RandSeed dd 1 c_24 dd 24 c_i12 dd 0x3DAAAAAA FREQ_NORMALIZE dd 0.000092696138 ; // 220.0/(2^(69/12)) / 44100.0 global _LFO_NORMALIZE _LFO_NORMALIZE dd DEF_LFO_NORMALIZE %ifdef GO4K_USE_GROOVE_PATTERN go4k_groove_pattern dw 0011100111001110b %endif ; //======================================================================================== ; // .crtemui section (emulates crt functions) ; //======================================================================================== %ifdef USE_SECTIONS section .crtemui code align=1 %else section .text %endif export_func FloatRandomNumber@0 push eax imul eax,dword [_RandSeed],16007 mov dword [_RandSeed], eax fild dword [_RandSeed] fidiv dword [c_RandDiv] pop eax ret ; //======================================================================================== ; // .g4kcod* sections (code for go4k) ; //======================================================================================== %ifdef INCLUDE_WAVESHAPER %ifdef USE_SECTIONS section .g4kcod2 code align=1 %else section .text %endif ; //---------------------------------------------------------------------------------------- ; // Waveshaper function ; //---------------------------------------------------------------------------------------- ; // Input : st0 : shaping coeff ; // st1 : input ; // Output: st0 : result ; //---------------------------------------------------------------------------------------- go4kWaveshaper: %ifdef GO4K_USE_WAVESHAPER_CLIP fxch fld1 ; // 1 val fucomi st1 ; // 1 val jbe short go4kWaveshaper_clip fchs ; // -1 val fucomi st1 ; // -1 val fcmovb st0, st1 ; // val -1 (if val > -1) go4kWaveshaper_clip: fstp st1 ; // newval fxch %endif fsub dword [c_0_5] fadd st0 fst dword [esp-4] ; // amnt in fadd st0 ; // 2*amnt in fld1 ; // 1 2*amnt in fsub dword [esp-4] ; // 1-amnt 2*amnt in fdivp st1, st0 ; // k in fld st1 ; // sin k in fabs ; // a(in) k in fmul st1 ; // k*a(in) k in fld1 faddp st1, st0 ; // 1+k*a(in) k in fxch st1 ; // k 1+k*a(in) in fld1 faddp st1, st0 ; // 1+k 1+k*a(in) in fmulp st2 ; // 1+k*a(in) (1+k)*in fdivp st1, st0 ; // out ret %endif %ifdef USE_SECTIONS section .g4kcod3 code align=1 %else section .text %endif ; //---------------------------------------------------------------------------------------- ; // unit values preparation/transform ; //---------------------------------------------------------------------------------------- go4kTransformValues: push ecx xor ecx, ecx xor eax, eax mov edx, go4k_transformed_values go4kTransformValues_loop: lodsb push eax fild dword [esp] fmul dword [c_i128] fstp dword [edx+ecx*4] pop eax inc ecx cmp ecx, dword [esp+8] jl go4kTransformValues_loop pop ecx ret 4 %ifdef USE_SECTIONS section .g4kcod4 code align=1 %else section .text %endif ; //---------------------------------------------------------------------------------------- ; // Envelope param mapping ; //---------------------------------------------------------------------------------------- go4kENVMap: fld dword [edx+eax*4] %ifdef GO4K_USE_ENV_MOD_ADR fadd dword [WRK+go4kENV_wrk.am+eax*4] %endif fimul dword [c_24] fchs ; //---------------------------------------------------------------------------------------- ; // Power function (2^x) ; //---------------------------------------------------------------------------------------- ; // Input : st0 : base ; // st1 : exponent ; // Output: st0 : result ; //---------------------------------------------------------------------------------------- export_func Power@0 ; // base exp fld1 fadd st0 fyl2x ; // log2_base fld1 ; // 1 log2_base fld st1 ; // log2_base 1 log2_base fprem ; // (frac)log2_base 1 log2_base f2xm1 ; // 2 ^ '' - 1 1 log2_base faddp st1, st0 ; // 2 ^ '' (int)log2_base fscale fstp st1 ret %ifdef USE_SECTIONS section .g4kcoda code align=1 %else section .text %endif ; //---------------------------------------------------------------------------------------- ; // ENV Tick ; //---------------------------------------------------------------------------------------- ; // IN : WRK = unit workspace ; // IN : VAL = unit values ; // IN : ecx = global workspace ; // OUT : ; // DIRTY : eax ; //---------------------------------------------------------------------------------------- export_func go4kENV_func@0 push 5 call go4kTransformValues %ifdef GO4K_USE_ENV_CHECK ; check if current note still active mov eax, dword [ecx-4] test eax, eax jne go4kENV_func_do fldz ret %endif go4kENV_func_do: mov eax, dword [ecx-8] ; // is the instrument in release mode (note off)? test eax, eax je go4kENV_func_process mov dword [WRK+go4kENV_wrk.state], ENV_STATE_RELEASE go4kENV_func_process: mov eax, dword [WRK+go4kENV_wrk.state] fld dword [WRK+go4kENV_wrk.level] ; // val - ; // check for sustain state cmp al, ENV_STATE_SUSTAIN je short go4kENV_func_leave2 go4kENV_func_attac: cmp al, ENV_STATE_ATTAC jne short go4kENV_func_decay call go4kENVMap ; // newval faddp st1, st0 ; // check for end of attac fld1 ; // 1 newval fucomi st1 ; // 1 newval fcmovnb st0, st1 ; // newval 1 (if newval < 1) jbe short go4kENV_func_statechange go4kENV_func_decay: cmp al, ENV_STATE_DECAY jne short go4kENV_func_release call go4kENVMap ; // newval fsubp st1, st0 ; // check for end of decay fld dword [edx+go4kENV_val.sustain] ; // sustain newval fucomi st1 ; // sustain newval fcmovb st0, st1 ; // newval sustain (if newval > sustain) jnc short go4kENV_func_statechange go4kENV_func_release: ; // release state? cmp al, ENV_STATE_RELEASE jne short go4kENV_func_leave call go4kENVMap ; // newval fsubp st1, st0 ; // check for end of release fldz ; // 0 newval fucomi st1 ; // 0 newval fcmovb st0, st1 ; // newval 0 (if newval > 0) jc short go4kENV_func_leave go4kENV_func_statechange: ; // newval inc dword [WRK+go4kENV_wrk.state] go4kENV_func_leave: ; // newval bla fstp st1 ; // store new env value fst dword [WRK+go4kENV_wrk.level] go4kENV_func_leave2: ; // mul by gain %ifdef GO4K_USE_ENV_MOD_GM fld dword [edx+go4kENV_val.gain] fadd dword [WRK+go4kENV_wrk.gm] fmulp st1, st0 %else fmul dword [edx+go4kENV_val.gain] %endif ret ; //---------------------------------------------------------------------------------------- ; // VCO Tick ; //---------------------------------------------------------------------------------------- ; // IN : WRK = unit workspace ; // IN : VAL = unit values ; // IN : ecx = global workspace ; // OUT : ; // DIRTY : eax ; //---------------------------------------------------------------------------------------- %ifdef USE_SECTIONS section .g4kcodp code align=1 %else section .text %endif go4kVCO_pulse: fucomi st1 ; // c p fld1 jnc short go4kVCO_func_pulse_up ; // +1 c p fchs ; // -1 c p go4kVCO_func_pulse_up: fstp st1 ; // +-1 p fstp st1 ; // +-1 ret %ifdef USE_SECTIONS section .g4kcodt code align=1 %else section .text %endif go4kVCO_trisaw: fucomi st1 ; // c p jnc short go4kVCO_func_trisaw_up fld1 ; // 1 c p fsubr st2, st0 ; // 1 c 1-p fsubrp st1, st0 ; // 1-c 1-p go4kVCO_func_trisaw_up: fdivp st1, st0 ; // tp'/tc fadd st0 ; // 2*'' fld1 ; // 1 2*'' fsubp st1, st0 ; // 2*''-1 ret %ifdef USE_SECTIONS section .g4kcods code align=1 %else section .text %endif go4kVCO_sine: fucomi st1 ; // c p jnc short go4kVCO_func_sine_do fstp st1 fsub st0, st0 ; // 0 ret go4kVCO_func_sine_do fdivp st1, st0 ; // p/c fldpi ; // pi p fadd st0 ; // 2*pi p fmulp st1, st0 ; // 2*pi*p fsin ; // sin(2*pi*p) ret %ifdef GO4K_USE_VCO_GATE %ifdef USE_SECTIONS section .g4kcodq code align=1 %else section .text %endif go4kVCO_gate: fxch ; // p c fstp st1 ; // p fmul dword [c_16] ; // p' push eax push eax fistp dword [esp] ; // - fld1 ; // 1 pop eax and al, 0xf bt word [VAL-5],ax jc go4kVCO_gate_bit fsub st0, st0 ; // 0 go4kVCO_gate_bit: fld dword [WRK+go4kVCO_wrk.cm] ; // f x fsub st1 ; // f-x x fmul dword [c_dc_const] ; // c(f-x) x faddp st1, st0 ; // x' fst dword [WRK+go4kVCO_wrk.cm] pop eax ret %endif %ifdef USE_SECTIONS section .g4kcodb code align=1 %else section .text %endif export_func go4kVCO_func@0 %ifdef GO4K_USE_VCO_PHASE_OFFSET %ifdef GO4K_USE_VCO_SHAPE %ifdef GO4K_USE_VCO_GATE push 8 %else push 7 %endif %else %ifdef GO4K_USE_VCO_GATE push 7 %else push 6 %endif %endif %else %ifdef GO4K_USE_VCO_SHAPE %ifdef GO4K_USE_VCO_GATE push 7 %else push 6 %endif %else %ifdef GO4K_USE_VCO_GATE push 6 %else push 5 %endif %endif %endif call go4kTransformValues %ifdef GO4K_USE_VCO_CHECK ; check if current note still active mov eax, dword [ecx-4] test eax, eax jne go4kVCO_func_do %ifdef GO4K_USE_VCO_STEREO movzx eax, byte [VAL-1] ; // get flags and check for stereo test al, byte VCO_STEREO jz short go4kVCO_func_nostereoout fldz go4kVCO_func_nostereoout: %endif fldz ret go4kVCO_func_do: %endif movzx eax, byte [VAL-1] ; // get flags %ifdef GO4K_USE_VCO_STEREO test al, byte VCO_STEREO jz short go4kVCO_func_nopswap fld dword [WRK+go4kVCO_wrk.phase] ;// swap left/right phase values for first stereo run fld dword [WRK+go4kVCO_wrk.phase2] fstp dword [WRK+go4kVCO_wrk.phase] fstp dword [WRK+go4kVCO_wrk.phase2] go4kVCO_func_nopswap: %endif go4kVCO_func_process: fld dword [edx+go4kVCO_val.transpose] fsub dword [c_0_5] %ifdef GO4K_USE_VCO_MOD_TM fadd dword [WRK+go4kVCO_wrk.tm] %endif fdiv dword [c_i128] fld dword [edx+go4kVCO_val.detune] fsub dword [c_0_5] fadd st0 %ifdef GO4K_USE_VCO_STEREO test al, byte VCO_STEREO jz short go4kVCO_func_nodswap fchs ;// negate detune for stereo go4kVCO_func_nodswap: %endif faddp st1 %ifdef GO4K_USE_VCO_MOD_DM fadd dword [WRK+go4kVCO_wrk.dm] %endif ; // st0 now contains the transpose+detune offset test al, byte LFO jnz go4kVCO_func_skipnote fiadd dword [ecx-4] ; // st0 is note, st1 is t+d offset go4kVCO_func_skipnote: fmul dword [c_i12] call _Power@0 test al, byte LFO jz short go4kVCO_func_normalize_note fmul dword [_LFO_NORMALIZE] ; // st0 is now frequency for lfo jmp short go4kVCO_func_normalized go4kVCO_func_normalize_note: fmul dword [FREQ_NORMALIZE] ; // st0 is now frequency go4kVCO_func_normalized: fadd dword [WRK+go4kVCO_wrk.phase] %ifdef GO4K_USE_VCO_MOD_FM fadd dword [WRK+go4kVCO_wrk.fm] %endif fld1 fadd st1, st0 fxch fprem fstp st1 fst dword [WRK+go4kVCO_wrk.phase] %ifdef GO4K_USE_VCO_MOD_PM fadd dword [WRK+go4kVCO_wrk.pm] %endif %ifdef GO4K_USE_VCO_PHASE_OFFSET fadd dword [edx+go4kVCO_val.phaseofs] %endif %ifdef PHASE_RENORMALIZE fld1 fadd st1, st0 fxch fprem fstp st1 ; // p %endif fld dword [edx+go4kVCO_val.color] ; // c p %ifdef GO4K_USE_VCO_MOD_CM fadd dword [WRK+go4kVCO_wrk.cm] ; // c p %endif go4kVCO_func_sine: test al, byte SINE jz short go4kVCO_func_trisaw call go4kVCO_sine go4kVCO_func_trisaw: test al, byte TRISAW jz short go4kVCO_func_pulse call go4kVCO_trisaw go4kVCO_func_pulse: test al, byte PULSE %ifdef GO4K_USE_VCO_GATE jz short go4kVCO_func_gate %else jz short go4kVCO_func_noise %endif call go4kVCO_pulse %ifdef GO4K_USE_VCO_GATE go4kVCO_func_gate: test al, byte GATE jz short go4kVCO_func_noise call go4kVCO_gate %endif go4kVCO_func_noise: test al, byte NOISE jz short go4kVCO_func_end call _FloatRandomNumber@0 fstp st1 fstp st1 go4kVCO_func_end: %ifdef GO4K_USE_VCO_SHAPE fld dword [edx+go4kVCO_val.shape] %ifdef GO4K_USE_VCO_MOD_SM fadd dword [WRK+go4kVCO_wrk.sm] %endif call go4kWaveshaper %endif fld dword [edx+go4kVCO_val.gain] %ifdef GO4K_USE_VCO_MOD_GM fadd dword [WRK+go4kVCO_wrk.gm] %endif fmulp st1, st0 %ifdef GO4K_USE_VCO_STEREO test al, byte VCO_STEREO jz short go4kVCO_func_stereodone sub al, byte VCO_STEREO fld dword [WRK+go4kVCO_wrk.phase] ;// swap left/right phase values again for second stereo run fld dword [WRK+go4kVCO_wrk.phase2] fstp dword [WRK+go4kVCO_wrk.phase] fstp dword [WRK+go4kVCO_wrk.phase2] jmp go4kVCO_func_process go4kVCO_func_stereodone: %endif ret %ifdef USE_SECTIONS section .g4kcodc code align=1 %else section .text %endif ; //---------------------------------------------------------------------------------------- ; // VCF Tick ; //---------------------------------------------------------------------------------------- ; // IN : WRK = unit workspace ; // IN : VAL = unit values ; // IN : ecx = global workspace ; // OUT : ; // DIRTY : eax ; //---------------------------------------------------------------------------------------- export_func go4kVCF_func@0 push 3 call go4kTransformValues %ifdef GO4K_USE_VCF_CHECK ; check if current note still active mov eax, dword [ecx-4] test eax, eax jne go4kVCF_func_do ret go4kVCF_func_do: %endif movzx eax, byte [VAL-1] ; // get type flag fld dword [edx+go4kVCF_val.res] ; // r in %ifdef GO4K_USE_VCF_MOD_RM fadd dword [WRK+go4kVCF_wrk.rm] %endif fstp dword [esp-8] fld dword [edx+go4kVCF_val.freq] ; // f in %ifdef GO4K_USE_VCF_MOD_FM fadd dword [WRK+go4kVCF_wrk.fm] %endif fmul st0, st0 ; // square the input so we never get negative and also have a smoother behaviour in the lower frequencies fstp dword [esp-4] ; // in %ifdef GO4K_USE_VCF_STEREO test al, byte STEREO jz short go4kVCF_func_process add WRK, go4kVCF_wrk.low2 go4kVCF_func_stereoloop: ; // switch channels fxch st1 ; // inr inl %endif go4kVCF_func_process: fld dword [esp-8] fld dword [esp-4] fmul dword [WRK+go4kVCF_wrk.band] ; // f*b r in fadd dword [WRK+go4kVCF_wrk.low] ; // l' r in fst dword [WRK+go4kVCF_wrk.low] ; // l' r in fsubp st2, st0 ; // r in-l' fmul dword [WRK+go4kVCF_wrk.band] ; // r*b in-l' fsubp st1, st0 ; // h' fst dword [WRK+go4kVCF_wrk.high] ; // h' fmul dword [esp-4] ; // h'*f fadd dword [WRK+go4kVCF_wrk.band] ; // b' fstp dword [WRK+go4kVCF_wrk.band] fldz go4kVCF_func_low: test al, byte LOWPASS jz short go4kVCF_func_high fadd dword [WRK+go4kVCF_wrk.low] go4kVCF_func_high: %ifdef GO4K_USE_VCF_HIGH test al, byte HIGHPASS jz short go4kVCF_func_band fadd dword [WRK+go4kVCF_wrk.high] %endif go4kVCF_func_band: %ifdef GO4K_USE_VCF_BAND test al, byte BANDPASS jz short go4kVCF_func_peak fadd dword [WRK+go4kVCF_wrk.band] %endif go4kVCF_func_peak: %ifdef GO4K_USE_VCF_PEAK test al, byte PEAK jz short go4kVCF_func_processdone fadd dword [WRK+go4kVCF_wrk.low] fsub dword [WRK+go4kVCF_wrk.high] %endif go4kVCF_func_processdone: %ifdef GO4K_USE_VCF_STEREO test al, byte STEREO ; // outr inl jz short go4kVCF_func_end sub al, byte STEREO sub WRK, go4kVCF_wrk.low2 jmp go4kVCF_func_stereoloop %endif go4kVCF_func_end: ; // value - - - - ret %ifdef USE_SECTIONS section .g4kcodd code align=1 %else section .text %endif ; //---------------------------------------------------------------------------------------- ; // DST Tick ; //---------------------------------------------------------------------------------------- ; // IN : WRK = unit workspace ; // IN : VAL = unit values ; // IN : ecx = global workspace ; // OUT : ; // DIRTY : eax ; //---------------------------------------------------------------------------------------- export_func go4kDST_func@0 %ifdef GO4K_USE_DST %ifdef GO4K_USE_DST_SH %ifdef GO4K_USE_DST_STEREO push 3 %else push 2 %endif %else %ifdef GO4K_USE_DST_STEREO push 2 %else push 1 %endif %endif call go4kTransformValues %ifdef GO4K_USE_DST_CHECK ; check if current note still active mov eax, dword [ecx-4] test eax, eax jne go4kDST_func_do ret go4kDST_func_do: %endif movzx eax, byte [VAL-1] ; // get type flag %ifdef GO4K_USE_DST_SH fld dword [edx+go4kDST_val.snhfreq] ; // snh in (inr) %ifdef GO4K_USE_DST_MOD_SH fadd dword [WRK+go4kDST_wrk.sm] ; // snh' in (inr) %endif fmul st0, st0 ; // square the input so we never get negative and also have a smoother behaviour in the lower frequencies fchs fadd dword [WRK+go4kDST_wrk.snhphase]; // snh' in (inr) fst dword [WRK+go4kDST_wrk.snhphase] fldz ; // 0 snh' in (inr) fucomip st1 ; // snh' in (inr) fstp dword [esp-4] ; // in (inr) jc short go4kDST_func_hold fld1 ; // 1 in (inr) fadd dword [esp-4] ; // 1+snh' in (inr) fstp dword [WRK+go4kDST_wrk.snhphase]; // in (inr) %endif ; // calc pregain and postgain %ifdef GO4K_USE_DST_STEREO test al, byte STEREO jz short go4kDST_func_mono fxch st1 ; // inr inl fld dword [edx+go4kDST_val.drive] ; // drive inr inl %ifdef GO4K_USE_DST_MOD_DM fadd dword [WRK+go4kDST_wrk.dm] %endif call go4kWaveshaper ; // outr inl %ifdef GO4K_USE_DST_SH fst dword [WRK+go4kDST_wrk.out2] ; // outr inl %endif fxch st1 ; // inl outr go4kDST_func_mono: %endif fld dword [edx+go4kDST_val.drive] ; // drive in (outr) %ifdef GO4K_USE_DST_MOD_DM fadd dword [WRK+go4kDST_wrk.dm] %endif call go4kWaveshaper ; // out (outr) %ifdef GO4K_USE_DST_SH fst dword [WRK+go4kDST_wrk.out] ; // out' (outr) %endif ret ; // out' (outr) %ifdef GO4K_USE_DST_SH go4kDST_func_hold: ; // in (inr) fstp st0 ; // (inr) %ifdef GO4K_USE_DST_STEREO test al, byte STEREO jz short go4kDST_func_monohold ; // (inr) fstp st0 ; // fld dword [WRK+go4kDST_wrk.out2] ; // outr go4kDST_func_monohold: %endif fld dword [WRK+go4kDST_wrk.out] ; // out (outr) ret %endif %endif %ifdef USE_SECTIONS section .g4kcodf code align=1 %else section .text %endif ; //---------------------------------------------------------------------------------------- ; // DLL Tick ; //---------------------------------------------------------------------------------------- ; // IN : WRK = unit workspace ; // IN : VAL = unit values ; // IN : ecx = global workspace ; // OUT : ; // DIRTY : eax ; //---------------------------------------------------------------------------------------- export_func go4kDLL_func@0 %ifdef GO4K_USE_DLL %ifdef GO4K_USE_DLL_CHORUS %ifdef GO4K_USE_DLL_DAMP push 8 %else push 7 %endif %else %ifdef GO4K_USE_DLL_DAMP push 6 %else push 5 %endif %endif call go4kTransformValues pushad movzx ebx, byte [VAL-(go4kDLL_val.size-go4kDLL_val.delay)/4] ;// delay length index %ifdef GO4K_USE_DLL_NOTE_SYNC test ebx, ebx jne go4kDLL_func_process fld1 fild dword [ecx-4] ; // load note freq fmul dword [c_i12] call _Power@0 fmul dword [FREQ_NORMALIZE] ; // normalize fdivp st1, st0 ; // invert to get numer of samples fistp word [_go4k_delay_times+ebx*2] ; store current comb size %endif go4kDLL_func_process: mov ecx, eax ;// ecx is delay counter %ifdef GO4K_USE_DLL_MOD mov edi, WRK ;// edi is modulation workspace %endif mov WRK, dword [_go4k_delay_buffer_ofs] ;// ebp is current delay fld st0 ;// in in %ifdef GO4K_USE_DLL_MOD_IM fld dword [edx+go4kDLL_val.dry] ;// dry in in fadd dword [edi+go4kDLL_wrk2.im] ;// dry' in in fmulp st1, st0 ;// out in %else fmul dword [edx+go4kDLL_val.dry] ;// out in %endif fxch ;// in out %ifdef GO4K_USE_DLL_MOD_PM fld dword [edx+go4kDLL_val.pregain] ;// pg in out fadd dword [edi+go4kDLL_wrk2.pm] ;// pg' in out fmul st0, st0 ;// pg'' in out fmulp st1, st0 ;// in' out %else fmul dword [edx+go4kDLL_val.pregain] ;// in' out fmul dword [edx+go4kDLL_val.pregain] ;// in' out %endif %ifdef GO4K_USE_DLL_CHORUS ;// update saw lfo for chorus/flanger fld dword [edx+go4kDLL_val.freq] ;// f in' out %ifdef GO4K_USE_DLL_MOD_SM fadd dword [edi+go4kDLL_wrk2.sm] ;// f' in' out %endif fmul st0, st0 fmul st0, st0 fdiv dword [DLL_DEPTH] fadd dword [WRK+go4kDLL_wrk.phase] ;// p' in' out ;// clamp phase to 0,1 (only in editor, since delay can be active quite long) %ifdef GO4K_USE_DLL_CHORUS_CLAMP fld1 ;// 1 p' in' out fadd st1, st0 ;// 1 1+p' in' out fxch ;// 1+p' 1 in' out fprem ;// p'' 1 in' out fstp st1 ;// p'' in' out %endif fst dword [WRK+go4kDLL_wrk.phase] ;// get current sine value fldpi ; // pi p in' out fadd st0 ; // 2*pi p in' out fmulp st1, st0 ; // 2*pi*p in' out fsin ; // sin in' out fld1 ; // 1 sin in' out faddp st1, st0 ; // 1+sin in' out ;// mul with depth and convert to samples fld dword [edx+go4kDLL_val.depth] ; // d 1+sin in' out %ifdef GO4K_USE_DLL_MOD_AM fadd dword [edi+go4kDLL_wrk2.am] ; // d' 1+sin in' out %endif fmul st0, st0 fmul st0, st0 fmul dword [DLL_DEPTH] fmulp st1, st0 fistp dword [esp-4] ; // in' out %endif go4kDLL_func_loop: movzx esi, word [_go4k_delay_times+ebx*2] ; fetch comb size mov eax, dword [WRK+go4kDLL_wrk.index] ;// eax is current comb index %ifdef GO4K_USE_DLL_CHORUS ;// add lfo offset and wrap buffer add eax, dword [esp-4] cmp eax, esi jl short go4kDLL_func_buffer_nowrap1 sub eax, esi go4kDLL_func_buffer_nowrap1: %endif fld dword [WRK+eax*4+go4kDLL_wrk.buffer];// cout in' out %ifdef GO4K_USE_DLL_CHORUS mov eax, dword [WRK+go4kDLL_wrk.index] %endif ;// add comb output to current output fadd st2, st0 ;// cout in' out' %ifdef GO4K_USE_DLL_DAMP fld1 ;// 1 cout in' out' fsub dword [edx+go4kDLL_val.damp] ;// 1-damp cout in' out' %ifdef GO4K_USE_DLL_MOD_DM fsub dword [edi+go4kDLL_wrk2.dm] ;// 1-damp' cout in' out' %endif fmulp st1, st0 ;// cout*d2 in' out' fld dword [edx+go4kDLL_val.damp] ;// d1 cout*d2 in' out' %ifdef GO4K_USE_DLL_MOD_DM fadd dword [edi+go4kDLL_wrk2.dm] ;// d1' cout*d2 in' out' %endif fmul dword [WRK+go4kDLL_wrk.store] ;// store*d1 cout*d2 in' out' faddp st1, st0 ;// store' in' out' fst dword [WRK+go4kDLL_wrk.store] ;// store' in' out' %endif %ifdef GO4K_USE_DLL_MOD_FM fld dword [edx+go4kDLL_val.feedback] ;// fb cout in' out' fadd dword [edi+go4kDLL_wrk2.fm] ;// fb' cout in' out' fmulp st1, st0 ;// cout*fb' in' out' %else fmul dword [edx+go4kDLL_val.feedback] ;// cout*fb in' out' %endif %ifdef GO4K_USE_DLL_DC_FILTER fadd st0, st1 ;// store in' out' fstp dword [WRK+eax*4+go4kDLL_wrk.buffer];// in' out' %else fsub st0, st1 ;// store in' out' fstp dword [WRK+eax*4+go4kDLL_wrk.buffer];// in' out' fneg %endif ;// wrap comb buffer pos inc eax cmp eax, esi jl short go4kDLL_func_buffer_nowrap2 %ifdef GO4K_USE_DLL_CHORUS sub eax, esi %else xor eax, eax %endif go4kDLL_func_buffer_nowrap2: mov dword [WRK+go4kDLL_wrk.index], eax ;// increment buffer pointer to next buffer inc ebx ;// go to next delay length index add WRK, go4kDLL_wrk.size ;// go to next delay mov dword [_go4k_delay_buffer_ofs], WRK ;// store next delay offset loopne go4kDLL_func_loop fstp st0 ;// out' ;// process a dc filter to prevent heavy offsets in reverb %ifdef GO4K_USE_DLL_DC_FILTER ; y(n) = x(n) - x(n-1) + R * y(n-1) sub WRK, go4kDLL_wrk.size fld dword [WRK+go4kDLL_wrk.dcout] ;// dco out' fmul dword [c_dc_const] ;// dcc*dco out' fsub dword [WRK+go4kDLL_wrk.dcin] ;// dcc*dco-dci out' fxch ;// out' dcc*dco-dci fst dword [WRK+go4kDLL_wrk.dcin] ;// out' dcc*dco-dci faddp st1 ;// out' %ifdef GO4K_USE_UNDENORMALIZE fadd dword [c_0_5] ;// add and sub small offset to prevent denormalization fsub dword [c_0_5] %endif fst dword [WRK+go4kDLL_wrk.dcout] %endif popad ret %endif %ifdef USE_SECTIONS section .g4kcodu code align=1 %else section .text %endif ; //---------------------------------------------------------------------------------------- ; // GLITCH Tick ; //---------------------------------------------------------------------------------------- ; // IN : WRK = unit workspace ; // IN : VAL = unit values ; // IN : ecx = global workspace ; // OUT : ; // DIRTY : eax ; //---------------------------------------------------------------------------------------- export_func go4kGLITCH_func@0 %ifdef GO4K_USE_GLITCH push 5 call go4kTransformValues pushad mov edi, WRK mov WRK, dword [_go4k_delay_buffer_ofs] ;// ebp is current delay ; mov eax, dword [edx+go4kGLITCH_val.active] ; or eax, dword [edi+go4kGLITCH_wrk2.am] ; test eax, eax ; je go4kGLITCH_func_notactive ;// out fld dword [edx+go4kGLITCH_val.active] ;// a in fadd dword [edi+go4kGLITCH_wrk2.am] ;// a' in ; // check for activity fldz ;// 0 a' in fucomip st1 ;// a' in fstp st0 ;// in jnc go4kGLITCH_func_notactive ;// out ;// check for first call and init if so init (using slizesize slot) mov eax, dword [WRK+go4kGLITCH_wrk.slizesize] and eax, eax jnz go4kGLITCH_func_process mov dword [WRK+go4kGLITCH_wrk.index], eax mov dword [WRK+go4kGLITCH_wrk.store], eax movzx ebx, byte [VAL-(go4kGLITCH_val.size-go4kGLITCH_val.slicesize)/4] ;// slicesize index movzx eax, word [_go4k_delay_times+ebx*2] ;// fetch slicesize push eax fld1 fild dword [esp] fstp dword [WRK+go4kGLITCH_wrk.slizesize] fstp dword [WRK+go4kGLITCH_wrk.slicepitch] pop eax go4kGLITCH_func_process: ;// fill buffer until full mov eax, dword [WRK+go4kGLITCH_wrk.store] cmp eax, MAX_DELAY jae go4kGLITCH_func_filldone fst dword [WRK+eax*4+go4kDLL_wrk.buffer] ;// in inc dword [WRK+go4kGLITCH_wrk.store] go4kGLITCH_func_filldone: ;// save input push eax fstp dword [esp] ;// - ;// read from buffer push eax fld dword [WRK+go4kGLITCH_wrk.index] ;// idx fist dword [esp] pop eax fld dword [WRK+eax*4+go4kDLL_wrk.buffer] ;// out idx fxch ;// idx out ;// progress readindex with current play speed fadd dword [WRK+go4kGLITCH_wrk.slicepitch] ;// idx' out fst dword [WRK+go4kGLITCH_wrk.index] ;// check for slice done fld dword [WRK+go4kGLITCH_wrk.slizesize] ;// size idx' out fxch ;// idx' size out fucomip st1 ;// idx' out fstp st0 ;// out jc go4kGLITCH_func_process_done ;// reinit for next loop xor eax, eax mov dword [WRK+go4kGLITCH_wrk.index], eax fld dword [edx+go4kGLITCH_val.dsize] fadd dword [edi+go4kGLITCH_wrk2.sm] fsub dword [c_0_5] fmul dword [c_0_5] call _Power@0 fmul dword [WRK+go4kGLITCH_wrk.slizesize] fstp dword [WRK+go4kGLITCH_wrk.slizesize] fld dword [edx+go4kGLITCH_val.dpitch] fadd dword [edi+go4kGLITCH_wrk2.pm] fsub dword [c_0_5] fmul dword [c_0_5] call _Power@0 fmul dword [WRK+go4kGLITCH_wrk.slicepitch] fstp dword [WRK+go4kGLITCH_wrk.slicepitch] go4kGLITCH_func_process_done: ;// dry wet mix fld dword [edx+go4kGLITCH_val.dry] ;// dry out fadd dword [edi+go4kGLITCH_wrk2.dm] ;// dry' out fld1 ;// 1 dry' out fsub st1 ;// 1-dry' dry' out fmulp st2 ;// dry' out' fmul dword [esp] ;// in' out' faddp st1, st0 ;// out' pop eax jmp go4kGLITCH_func_leave go4kGLITCH_func_notactive: ;// mark as uninitialized again (using slizesize slot) xor eax,eax mov dword [WRK+go4kGLITCH_wrk.slizesize], eax go4kGLITCH_func_leave: add WRK, go4kDLL_wrk.size ;// go to next delay mov dword [_go4k_delay_buffer_ofs], WRK ;// store next delay offset popad ret %endif %ifdef USE_SECTIONS section .g4kcodg code align=1 %else section .text %endif ; //---------------------------------------------------------------------------------------- ; // FOP Tick (various fp stack based operations) ; //---------------------------------------------------------------------------------------- ; // IN : WRK = unit workspace ; // IN : VAL = unit values ; // IN : ecx = global workspace ; // OUT : ; // DIRTY : ; //---------------------------------------------------------------------------------------- export_func go4kFOP_func@0 push 1 call go4kTransformValues go4kFOP_func_pop: dec eax jnz go4kFOP_func_addp fstp st0 ret go4kFOP_func_addp: dec eax jnz go4kFOP_func_mulp faddp st1, st0 ret go4kFOP_func_mulp: dec eax jnz go4kFOP_func_push fmulp st1, st0 ret go4kFOP_func_push: dec eax jnz go4kFOP_func_xch fld st0 ret go4kFOP_func_xch: dec eax jnz go4kFOP_func_add fxch ret go4kFOP_func_add: dec eax jnz go4kFOP_func_mul fadd st1 ret go4kFOP_func_mul: dec eax jnz go4kFOP_func_addp2 fmul st1 ret go4kFOP_func_addp2: dec eax jnz go4kFOP_func_loadnote faddp st2, st0 faddp st2, st0 ret go4kFOP_func_loadnote: dec eax jnz go4kFOP_func_mulp2 fild dword [ecx-4] fmul dword [c_i128] ret go4kFOP_func_mulp2: fmulp st2, st0 fmulp st2, st0 ret %ifdef USE_SECTIONS section .g4kcodh code align=1 %else section .text %endif ; //---------------------------------------------------------------------------------------- ; // FST Tick (stores a value somewhere in the local workspace) ; //---------------------------------------------------------------------------------------- ; // IN : WRK = unit workspace ; // IN : VAL = unit values ; // IN : ecx = global workspace ; // OUT : ; // DIRTY : ; //---------------------------------------------------------------------------------------- export_func go4kFST_func@0 push 1 call go4kTransformValues fld dword [edx+go4kFST_val.amount] fsub dword [c_0_5] fadd st0 fmul st1 lodsw and eax, 0x00003fff ; // eax is destination slot test word [VAL-2], FST_ADD jz go4kFST_func_set fadd dword [ecx+eax*4] go4kFST_func_set: fstp dword [ecx+eax*4] test word [VAL-2], FST_POP jz go4kFST_func_done fstp st0 go4kFST_func_done: ret %ifdef USE_SECTIONS section .g4kcodm code align=1 %else section .text %endif ; //---------------------------------------------------------------------------------------- ; // FLD Tick (load a value on stack, optionally add a modulation signal beforehand) ; //---------------------------------------------------------------------------------------- ; // IN : WRK = unit workspace ; // IN : VAL = unit values ; // IN : ecx = global workspace ; // OUT : signal-signal*pan , signal*pan ; // DIRTY : ; //---------------------------------------------------------------------------------------- export_func go4kFLD_func@0 ;// in main env %ifdef GO4K_USE_FLD push 1 call go4kTransformValues fld dword [edx+go4kFLD_val.value] ;// value in fsub dword [c_0_5] fadd st0 %ifdef GO4K_USE_FLD_MOD_VM fadd dword [WRK+go4kFLD_wrk.vm] ;// value' in %endif %endif ret %ifdef GO4K_USE_FSTG %ifdef USE_SECTIONS section .g4kcodi code align=1 %else section .text %endif ; //---------------------------------------------------------------------------------------- ; // FSTG Tick (stores a value anywhere in the synth (and in each voice)) ; //---------------------------------------------------------------------------------------- ; // IN : WRK = unit workspace ; // IN : VAL = unit values ; // IN : ecx = global workspace ; // OUT : ; // DIRTY : ; //---------------------------------------------------------------------------------------- export_func go4kFSTG_func@0 push 1 call go4kTransformValues %ifdef GO4K_USE_FSTG_CHECK ; check if current note still active mov eax, dword [ecx-4] test eax, eax jne go4kFSTG_func_do lodsw jmp go4kFSTG_func_testpop go4kFSTG_func_do: %endif fld dword [edx+go4kFST_val.amount] fsub dword [c_0_5] fadd st0 fmul st1 lodsw and eax, 0x00003fff ; // eax is destination slot test word [VAL-2], FST_ADD jz go4kFSTG_func_set fadd dword [go4k_synth_wrk+eax*4] go4kFSTG_func_set: %if MAX_VOICES > 1 fst dword [go4k_synth_wrk+eax*4] fstp dword [go4k_synth_wrk+eax*4+go4k_instrument.size] %else fstp dword [go4k_synth_wrk+eax*4] %endif go4kFSTG_func_testpop: test word [VAL-2], FST_POP jz go4kFSTG_func_done fstp st0 go4kFSTG_func_done: ret %endif %ifdef USE_SECTIONS section .g4kcodj code align=1 %else section .text %endif ; //---------------------------------------------------------------------------------------- ; // PAN Tick (multiplies signal with main envelope and converts to stereo) ; //---------------------------------------------------------------------------------------- ; // IN : WRK = unit workspace ; // IN : VAL = unit values ; // IN : ecx = global workspace ; // OUT : signal-signal*pan , signal*pan ; // DIRTY : ; //---------------------------------------------------------------------------------------- export_func go4kPAN_func@0 ;// in main env %ifdef GO4K_USE_PAN push 1 call go4kTransformValues fld dword [edx+go4kPAN_val.panning] ;// pan in %ifdef GO4K_USE_PAN_MOD fadd dword [WRK+go4kPAN_wrk.pm] ;// pan in %endif fmul st1 ;// r in fsub st1, st0 ;// r l fxch ;// l r %else fmul dword [c_0_5] fld st0 %endif ret %ifdef USE_SECTIONS section .g4kcodk code align=1 %else section .text %endif ; //---------------------------------------------------------------------------------------- ; // OUT Tick ( stores stereo signal pair in temp buffers of the instrument) ; //---------------------------------------------------------------------------------------- ; // IN : WRK = unit workspace ; // IN : VAL = unit values ; // IN : ecx = global workspace ; // OUT : ; // DIRTY : ; //---------------------------------------------------------------------------------------- export_func go4kOUT_func@0 ;// l r %ifdef GO4K_USE_GLOBAL_DLL push 2 call go4kTransformValues pushad lea edi, [ecx+MAX_UNITS*MAX_UNIT_SLOTS*4] fld st1 ;// r l r fld st1 ;// l r l r fld dword [edx+go4kOUT_val.auxsend] ;// as l r l r %ifdef GO4K_USE_OUT_MOD_AM fadd dword [WRK+go4kOUT_wrk.am] ;// am l r l r %endif fmulp st1, st0 ;// l' r l r fstp dword [edi] ;// r l r scasd fld dword [edx+go4kOUT_val.auxsend] ;// as r l r %ifdef GO4K_USE_OUT_MOD_AM fadd dword [WRK+go4kOUT_wrk.am] ;// am r l r %endif fmulp st1, st0 ;// r' l r fstp dword [edi] ;// l r scasd fld dword [edx+go4kOUT_val.gain] ;// g l r %ifdef GO4K_USE_OUT_MOD_GM fadd dword [WRK+go4kOUT_wrk.gm] ;// gm l r %endif fmulp st1, st0 ;// l' r fstp dword [edi] ;// r scasd fld dword [edx+go4kOUT_val.gain] ;// g r %ifdef GO4K_USE_OUT_MOD_GM fadd dword [WRK+go4kOUT_wrk.gm] ;// gm r %endif fmulp st1, st0 ;// r' fstp dword [edi] ;// - scasd popad %else push 1 call go4kTransformValues fld dword [edx+go4kOUT_val.gain] ;// g l r %ifdef GO4K_USE_OUT_MOD_GM fadd dword [WRK+go4kOUT_wrk.gm] ;// gm l r %endif fmulp st1, st0 ;// l' r fstp dword [ecx+MAX_UNITS*MAX_UNIT_SLOTS*4+0] ;// r fld dword [edx+go4kOUT_val.gain] ;// g r %ifdef GO4K_USE_OUT_MOD_GM fadd dword [WRK+go4kOUT_wrk.gm] ;// gm r %endif fmulp st1, st0 ;// r' fstp dword [ecx+MAX_UNITS*MAX_UNIT_SLOTS*4+4] ;// - %endif ret %ifdef USE_SECTIONS section .g4kcodl code align=1 %else section .text %endif ; //---------------------------------------------------------------------------------------- ; // ACC Tick (stereo signal accumulation for synth commands only -> dont use in instrument commands) ; //---------------------------------------------------------------------------------------- ; // IN : WRK = unit workspace ; // IN : VAL = unit values ; // IN : ecx = global workspace ; // OUT : ; // DIRTY : eax ; //---------------------------------------------------------------------------------------- export_func go4kACC_func@0 push 1 call go4kTransformValues pushad mov edi, go4k_synth_wrk add edi, go4k_instrument.size sub edi, eax ; // eax already contains the accumulation offset from the go4kTransformValues call mov cl, MAX_INSTRUMENTS*MAX_VOICES fldz ;// 0 fldz ;// 0 0 go4kACC_func_loop: fadd dword [edi-8] ;// l 0 fxch ;// 0 l fadd dword [edi-4] ;// r l fxch ;// l r add edi, go4k_instrument.size dec cl jnz go4kACC_func_loop popad ret %ifdef USE_SECTIONS section .g4kcodw code align=1 %else section .text %endif ; //---------------------------------------------------------------------------------------- ; // Update Instrument (allocate voices, set voice to release) ; //---------------------------------------------------------------------------------------- ; // IN : ; // IN : ; // OUT : ; // DIRTY : ; //---------------------------------------------------------------------------------------- go4kUpdateInstrument: ; // get new note mov eax, dword [esp+4+4] ; // eax = current tick shr eax, PATTERN_SIZE_SHIFT ; // eax = current pattern imul edx, ecx, dword MAX_PATTERNS ; // edx = instrument pattern list index movzx edx, byte [edx+eax+go4k_pattern_lists] ; // edx = pattern index mov eax, dword [esp+4+4] ; // eax = current tick shl edx, PATTERN_SIZE_SHIFT and eax, PATTERN_SIZE-1 movzx edx, byte [edx+eax+go4k_patterns] ; // edx = requested note in new patterntick ; // apply note changes cmp dl, HLD ; // anything but hold causes action je short go4kUpdateInstrument_done inc dword [edi] ; // set release flag if needed %if MAX_VOICES > 1 inc dword [edi+go4k_instrument.size] ; // set release flag if needed %endif cmp dl, HLD ; // check for new note jl short go4kUpdateInstrument_done %if MAX_VOICES > 1 pushad xchg eax, dword [go4k_voiceindex + ecx*4] test eax, eax je go4kUpdateInstrument_newNote add edi, go4k_instrument.size go4kUpdateInstrument_newNote: xor al,1 xchg dword [go4k_voiceindex + ecx*4], eax %endif pushad xor eax, eax mov ecx, (8+MAX_UNITS*MAX_UNIT_SLOTS*4)/4 ; // clear only relase, note and workspace rep stosd popad mov dword [edi+4], edx ; // set requested note as current note %if MAX_VOICES > 1 popad %endif jmp short go4kUpdateInstrument_done go4kUpdateInstrument_done: ret %ifdef USE_SECTIONS section .g4kcodx code align=1 %else section .text %endif ; //---------------------------------------------------------------------------------------- ; // Render Voices ; //---------------------------------------------------------------------------------------- ; // IN : ; // IN : ; // OUT : ; // DIRTY : ; //---------------------------------------------------------------------------------------- go4kRenderVoices: push ecx ; // save current instrument counter %if MAX_VOICES > 1 push COM ; // save current instrument command index push VAL ; // save current instrument values index %endif call go4k_VM_process ; // call synth vm for instrument voices mov eax, dword [ecx+go4kENV_wrk.state] cmp al, byte ENV_STATE_OFF jne go4kRenderVoices_next xor eax, eax mov dword [ecx-4], eax ; // kill note if voice is done go4kRenderVoices_next: %if MAX_VOICES > 1 pop VAL ; // restore instrument value index pop COM ; // restore instrument command index %endif %ifdef GO4K_USE_BUFFER_RECORDINGS mov eax, dword [esp+16] ; // get current tick shr eax, 8 ; // every 256th sample = ~ 172 hz shl eax, 5 ; // for 16 instruments a 2 voices add eax, dword [esp] add eax, dword [esp] ; // + 2*currentinstrument+0 %ifdef GO4K_USE_ENVELOPE_RECORDINGS mov edx, dword [ecx+go4kENV_wrk.level] mov dword [__4klang_envelope_buffer+eax*4], edx %endif %ifdef GO4K_USE_NOTE_RECORDINGS mov edx, dword [ecx-4] mov dword [__4klang_note_buffer+eax*4], edx %endif %endif %if MAX_VOICES > 1 call go4k_VM_process ; // call synth vm for instrument voices mov eax, dword [ecx+go4kENV_wrk.state] cmp al, byte ENV_STATE_OFF jne go4k_render_instrument_next2 xor eax, eax mov dword [ecx-4], eax ; // kill note if voice is done go4k_render_instrument_next2: %ifdef GO4K_USE_BUFFER_RECORDINGS mov eax, dword [esp+16] ; // get current tick shr eax, 8 ; // every 256th sample = ~ 172 hz shl eax, 5 ; // for 16 instruments a 2 voices add eax, dword [esp] add eax, dword [esp] ; // + 2*currentinstrument+0 %ifdef GO4K_USE_ENVELOPE_RECORDINGS mov edx, dword [ecx+go4kENV_wrk.level] mov dword [__4klang_envelope_buffer+eax*4+4], edx %endif %ifdef GO4K_USE_NOTE_RECORDINGS mov edx, dword [ecx-4] mov dword [__4klang_note_buffer+eax*4+4], edx %endif %endif %endif pop ecx ; // restore instrument counter ret %ifdef USE_SECTIONS section .g4kcody code align=1 %else section .text %endif ; //---------------------------------------------------------------------------------------- ; // the entry point for the synth ; //---------------------------------------------------------------------------------------- %ifdef USE_SECTIONS export_func _4klang_render@4 %else export_func _4klang_render %endif pushad xor ecx, ecx %ifdef GO4K_USE_BUFFER_RECORDINGS push ecx %endif ; loop all ticks go4k_render_tickloop: push ecx xor ecx, ecx ; loop all samples per tick go4k_render_sampleloop: push ecx xor ecx, ecx mov ebx, go4k_synth_instructions ; // ebx = instrument command index mov VAL, go4k_synth_parameter_values; // VAL = instrument values index mov edi, _go4k_delay_buffer ; // get offset of first delay buffer mov dword [_go4k_delay_buffer_ofs], edi ; // store offset in delaybuffer offset variable mov edi, go4k_synth_wrk ; // edi = first instrument ; loop all instruments go4k_render_instrumentloop: mov eax, dword [esp] ; // eax = current tick sample and eax, eax jnz go4k_render_instrument_process ; // tick change? (first sample in current tick) call go4kUpdateInstrument ; // update instrument state ; process instrument go4k_render_instrument_process: call go4kRenderVoices inc ecx cmp cl, byte MAX_INSTRUMENTS jl go4k_render_instrumentloop mov dword [edi+4], ecx ; // move a value != 0 into note slot, so processing will be done call go4k_VM_process ; // call synth vm for synth go4k_render_output_sample: %ifdef GO4K_USE_BUFFER_RECORDINGS inc dword [esp+8] xchg esi, dword [esp+48] ; // edx = destbuffer %else xchg esi, dword [esp+44] ; // edx = destbuffer %endif %ifdef GO4K_CLIP_OUTPUT fld dword [edi-8] fld1 ; // 1 val fucomi st1 ; // 1 val jbe short go4k_render_clip1 fchs ; // -1 val fucomi st1 ; // -1 val fcmovb st0, st1 ; // val -1 (if val > -1) go4k_render_clip1: fstp st1 ; // newval %ifdef GO4K_USE_16BIT_OUTPUT push eax fmul dword [c_32767] fistp dword [esp] pop eax mov word [esi],ax ; // store integer converted left sample lodsw %else fstp dword [esi] ; // store left sample lodsd %endif fld dword [edi-4] fld1 ; // 1 val fucomi st1 ; // 1 val jbe short go4k_render_clip2 fchs ; // -1 val fucomi st1 ; // -1 val fcmovb st0, st1 ; // val -1 (if val > -1) go4k_render_clip2: fstp st1 ; // newval %ifdef GO4K_USE_16BIT_OUTPUT push eax fmul dword [c_32767] fistp dword [esp] pop eax mov word [esi],ax ; // store integer converted right sample lodsw %else fstp dword [esi] ; // store right sample lodsd %endif %else fld dword [edi-8] %ifdef GO4K_USE_16BIT_OUTPUT push eax fmul dword [c_32767] fistp dword [esp] pop eax mov word [esi],ax ; // store integer converted left sample lodsw %else fstp dword [esi] ; // store left sample lodsd %endif fld dword [edi-4] %ifdef GO4K_USE_16BIT_OUTPUT push eax fmul dword [c_32767] fistp dword [esp] pop eax mov word [esi],ax ; // store integer converted right sample lodsw %else fstp dword [esi] ; // store right sample lodsd %endif %endif %ifdef GO4K_USE_BUFFER_RECORDINGS xchg esi, dword [esp+48] %else xchg esi, dword [esp+44] %endif pop ecx inc ecx %ifdef GO4K_USE_GROOVE_PATTERN mov ebx, dword SAMPLES_PER_TICK mov eax, dword [esp] and eax, 0x0f bt dword [go4k_groove_pattern],eax jnc go4k_render_nogroove sub ebx, dword 3000 go4k_render_nogroove: cmp ecx, ebx %else cmp ecx, dword SAMPLES_PER_TICK %endif jl go4k_render_sampleloop pop ecx inc ecx %ifdef AUTHORING mov dword[__4klang_current_tick], ecx %endif cmp ecx, dword MAX_TICKS jl go4k_render_tickloop %ifdef GO4K_USE_BUFFER_RECORDINGS pop ecx %endif popad ret 4 %ifdef USE_SECTIONS section .g4kcodz code align=1 %else section .text %endif ; //---------------------------------------------------------------------------------------- ; // the magic behind it :) ; //---------------------------------------------------------------------------------------- ; // IN : edi = instrument pointer ; // IN : esi = instrumet values ; // IN : ebx = instrument instructions ; // OUT : ; // DIRTY : ; //---------------------------------------------------------------------------------------- go4k_VM_process: lea WRK, [edi+8] ; // get current workspace pointer mov ecx, WRK ; // ecx = workspace start go4k_VM_process_loop: movzx eax, byte [ebx] ; // get command byte inc ebx test eax, eax je go4k_VM_process_done ; // command byte = 0? so done call dword [eax*4+go4k_synth_commands] add WRK, MAX_UNIT_SLOTS*4 ; // go to next workspace slot jmp short go4k_VM_process_loop go4k_VM_process_done: add edi, go4k_instrument.size ; // go to next instrument voice ret