sointu/4klang_source/Go4kVSTi/win/4klang.asm

1758 lines
50 KiB
NASM

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