mirror of
https://github.com/vsariola/sointu.git
synced 2026-02-04 23:00:17 -05:00
Rewrote most of the synth to better support stereo signals and polyphony. VSTi removed as there is no plan to update the VSTi to support the new features.
The stereo opcode variants have bit 1 of the command stream set. The polyphony is split into two parts: 1) polyphony, meaning that voices reuse the same opcodes; 2) multitrack voices, meaning that a track triggers more than voice. They both can be flexible defined in any combinations: for example voice 1 and 2 can be triggered by track 1 and use instrument 1, and voice 3 by track 2/instrument 2 and voice 4 by track 3/instrument 2. This is achieved through the use of bitmasks: in the aforementioned example, bit 1 of su_voicetrack_bitmask would be set, meaning "the voice after voice #1 will be triggered by the same track". On the other hand, bits 1 and 3 of su_polyphony_bitmask would be set to indicate that "the voices after #1 and #3 will reuse the same instruments".
This commit is contained in:
155
src/opcodes/arithmetic.asm
Normal file
155
src/opcodes/arithmetic.asm
Normal file
@ -0,0 +1,155 @@
|
||||
SECT_TEXT(suarithm)
|
||||
|
||||
;-------------------------------------------------------------------------------
|
||||
; op_pop function: a -> (empty)
|
||||
; stereo: a b -> (empty)
|
||||
;-------------------------------------------------------------------------------
|
||||
%if POP_ID > -1
|
||||
|
||||
EXPORT MANGLE_FUNC(su_op_pop,0)
|
||||
%ifdef INCLUDE_STEREO_POP
|
||||
jnc su_op_pop_mono
|
||||
fstp st0
|
||||
su_op_pop_mono:
|
||||
%endif
|
||||
fstp st0
|
||||
ret
|
||||
|
||||
%endif
|
||||
|
||||
;-------------------------------------------------------------------------------
|
||||
; op_add function: a b -> a+b b
|
||||
; stereo: a b c d -> a+c b+d c d
|
||||
;-------------------------------------------------------------------------------
|
||||
%if ADD_ID > -1
|
||||
|
||||
EXPORT MANGLE_FUNC(su_op_add,0)
|
||||
%ifdef INCLUDE_STEREO_ADD
|
||||
jnc su_op_add_mono
|
||||
fadd st0, st2
|
||||
fxch
|
||||
fadd st0, st3
|
||||
fxch
|
||||
ret
|
||||
su_op_pop_mono:
|
||||
%endif
|
||||
fadd st1
|
||||
ret
|
||||
|
||||
%endif
|
||||
|
||||
;-------------------------------------------------------------------------------
|
||||
; op_addp function: a b -> a+b
|
||||
; stereo: a b c d -> a+c b+d
|
||||
;-------------------------------------------------------------------------------
|
||||
%if ADDP_ID > -1
|
||||
|
||||
EXPORT MANGLE_FUNC(su_op_addp,0)
|
||||
%ifdef INCLUDE_STEREO_ADDP
|
||||
jnc su_op_addp_mono
|
||||
faddp st2, st0
|
||||
faddp st2, st0
|
||||
ret
|
||||
su_op_addp_mono:
|
||||
%endif
|
||||
faddp st1, st0
|
||||
ret
|
||||
|
||||
%endif
|
||||
|
||||
;-------------------------------------------------------------------------------
|
||||
; op_loadnote function: (empty) -> n
|
||||
; stereo: (empty) -> n n
|
||||
; ecx should point to the workspace (slightly offset)
|
||||
;-------------------------------------------------------------------------------
|
||||
%if LOADNOTE_ID > -1
|
||||
|
||||
EXPORT MANGLE_FUNC(su_op_loadnote,0)
|
||||
%ifdef INCLUDE_STEREO_LOADNOTE
|
||||
jnc su_op_loadnote_mono
|
||||
call su_op_loadnote_mono
|
||||
su_op_loadnote_mono:
|
||||
%endif
|
||||
fild dword [ecx+su_unit.size-su_voice.workspace+su_voice.note]
|
||||
fmul dword [c_i128]
|
||||
ret
|
||||
|
||||
%endif
|
||||
|
||||
;-------------------------------------------------------------------------------
|
||||
; op_mul function: a b -> a*b a
|
||||
; stereo: a b c d -> a*c b*d c d
|
||||
;-------------------------------------------------------------------------------
|
||||
%if MUL_ID > -1
|
||||
|
||||
EXPORT MANGLE_FUNC(su_op_mul,0)
|
||||
%ifdef INCLUDE_STEREO_MUL
|
||||
jnc su_op_mul_mono
|
||||
fmul st0, st2
|
||||
fxch
|
||||
fadd st0, st3
|
||||
fxch
|
||||
ret
|
||||
su_op_mul_mono:
|
||||
%endif
|
||||
fmul st1
|
||||
ret
|
||||
|
||||
%endif
|
||||
|
||||
;-------------------------------------------------------------------------------
|
||||
; op_mulp function: a b -> a*b
|
||||
; stereo: a b c d -> a*c b*d
|
||||
;-------------------------------------------------------------------------------
|
||||
%if MULP_ID > -1
|
||||
|
||||
EXPORT MANGLE_FUNC(su_op_mulp,0)
|
||||
%ifdef INCLUDE_STEREO_MULP
|
||||
jnc su_op_mulp_mono
|
||||
fmulp st2, st0
|
||||
fmulp st2, st0
|
||||
ret
|
||||
su_op_mulp_mono:
|
||||
%endif
|
||||
fmulp st1
|
||||
ret
|
||||
|
||||
%endif
|
||||
|
||||
;-------------------------------------------------------------------------------
|
||||
; op_push function: a -> a a
|
||||
; stereo: a b -> a b a b
|
||||
;-------------------------------------------------------------------------------
|
||||
%if PUSH_ID > -1
|
||||
|
||||
EXPORT MANGLE_FUNC(su_op_push,0)
|
||||
%ifdef INCLUDE_STEREO_PUSH
|
||||
jnc su_op_push_mono
|
||||
fld st1
|
||||
fld st1
|
||||
ret
|
||||
su_op_push_mono:
|
||||
%endif
|
||||
fld st0
|
||||
ret
|
||||
|
||||
%endif
|
||||
|
||||
;-------------------------------------------------------------------------------
|
||||
; op_xch function: a b -> b a
|
||||
; stereo: a b c d -> c d a b
|
||||
;-------------------------------------------------------------------------------
|
||||
%if XCH_ID > -1
|
||||
|
||||
EXPORT MANGLE_FUNC(su_op_xch,0)
|
||||
%ifdef INCLUDE_STEREO_XCH
|
||||
jnc su_op_xch_mono
|
||||
fxch st0, st2 ; c b a d
|
||||
fxch st0, st1 ; b c a d
|
||||
fxch st0, st2 ; d c a b
|
||||
su_op_xch_mono:
|
||||
%endif
|
||||
fxch st0, st1
|
||||
ret
|
||||
|
||||
%endif
|
||||
168
src/opcodes/arithmetic.inc
Normal file
168
src/opcodes/arithmetic.inc
Normal file
@ -0,0 +1,168 @@
|
||||
;-------------------------------------------------------------------------------
|
||||
; ADDP related defines
|
||||
;-------------------------------------------------------------------------------
|
||||
%assign ADDP_ID -1
|
||||
%macro USE_ADDP 0
|
||||
%if ADDP_ID == -1
|
||||
%assign ADDP_ID CUR_ID
|
||||
%assign CUR_ID CUR_ID + 2
|
||||
%xdefine OPCODES OPCODES MANGLE_FUNC(su_op_addp,0),
|
||||
%xdefine NUMPARAMS NUMPARAMS 0,
|
||||
%endif
|
||||
%endmacro
|
||||
|
||||
%macro SU_ADDP 1
|
||||
USE_ADDP
|
||||
%xdefine CMDS CMDS ADDP_ID + %1,
|
||||
%if %1 == STEREO
|
||||
%define INCLUDE_STEREO_ADDP
|
||||
%endif
|
||||
%endmacro
|
||||
|
||||
;-------------------------------------------------------------------------------
|
||||
; ADD related defines
|
||||
;-------------------------------------------------------------------------------
|
||||
%assign ADD_ID -1
|
||||
%macro USE_ADD 0
|
||||
%if ADD_ID == -1
|
||||
%assign ADD_ID CUR_ID
|
||||
%assign CUR_ID CUR_ID + 2
|
||||
%xdefine OPCODES OPCODES MANGLE_FUNC(su_op_add,0),
|
||||
%xdefine NUMPARAMS NUMPARAMS 0,
|
||||
%endif
|
||||
%endmacro
|
||||
|
||||
%assign ADD_ID -1
|
||||
%macro SU_ADD 1
|
||||
USE_ADD
|
||||
%xdefine CMDS CMDS ADD_ID + %1,
|
||||
%if %1 == STEREO
|
||||
%define INCLUDE_STEREO_ADD
|
||||
%endif
|
||||
%endmacro
|
||||
|
||||
;-------------------------------------------------------------------------------
|
||||
; POP related defines
|
||||
;-------------------------------------------------------------------------------
|
||||
%assign POP_ID -1
|
||||
%macro USE_POP 0
|
||||
%if POP_ID == -1
|
||||
%assign POP_ID CUR_ID
|
||||
%assign CUR_ID CUR_ID + 2
|
||||
%xdefine OPCODES OPCODES MANGLE_FUNC(su_op_pop,0),
|
||||
%xdefine NUMPARAMS NUMPARAMS 0,
|
||||
%endif
|
||||
%endmacro
|
||||
|
||||
%macro SU_POP 1
|
||||
USE_POP
|
||||
%xdefine CMDS CMDS POP_ID + %1,
|
||||
%if %1 == STEREO
|
||||
%define INCLUDE_STEREO_POP
|
||||
%endif
|
||||
%endmacro
|
||||
|
||||
;-------------------------------------------------------------------------------
|
||||
; LOADNOTE related defines
|
||||
;-------------------------------------------------------------------------------
|
||||
%assign LOADNOTE_ID -1
|
||||
%macro USE_LOADNOTE 0
|
||||
%if LOADNOTE_ID == -1
|
||||
%assign LOADNOTE_ID CUR_ID
|
||||
%assign CUR_ID CUR_ID + 2
|
||||
%xdefine OPCODES OPCODES MANGLE_FUNC(su_op_loadnote,0),
|
||||
%xdefine NUMPARAMS NUMPARAMS 0,
|
||||
%endif
|
||||
%endmacro
|
||||
|
||||
%macro SU_LOADNOTE 1
|
||||
USE_LOADNOTE
|
||||
%xdefine CMDS CMDS LOADNOTE_ID + %1,
|
||||
%if %1 == STEREO
|
||||
%define INCLUDE_STEREO_LOADNOTE
|
||||
%endif
|
||||
%endmacro
|
||||
|
||||
;-------------------------------------------------------------------------------
|
||||
; MUL related defines
|
||||
;-------------------------------------------------------------------------------
|
||||
%assign MUL_ID -1
|
||||
%macro USE_MUL 0
|
||||
%if MUL_ID == -1
|
||||
%assign MUL_ID CUR_ID
|
||||
%assign CUR_ID CUR_ID + 2
|
||||
%xdefine OPCODES OPCODES MANGLE_FUNC(su_op_mul,0),
|
||||
%xdefine NUMPARAMS NUMPARAMS 0,
|
||||
%endif
|
||||
%endmacro
|
||||
|
||||
%macro SU_MUL 1
|
||||
USE_MUL
|
||||
%xdefine CMDS CMDS MUL_ID + %1,
|
||||
%if %1 == STEREO
|
||||
%define INCLUDE_STEREO_MUL
|
||||
%endif
|
||||
%endmacro
|
||||
|
||||
;-------------------------------------------------------------------------------
|
||||
; MULP related defines
|
||||
;-------------------------------------------------------------------------------
|
||||
%assign MULP_ID -1
|
||||
%macro USE_MULP 0
|
||||
%if MULP_ID == -1
|
||||
%assign MULP_ID CUR_ID
|
||||
%assign CUR_ID CUR_ID + 2
|
||||
%xdefine OPCODES OPCODES MANGLE_FUNC(su_op_mulp,0),
|
||||
%xdefine NUMPARAMS NUMPARAMS 0,
|
||||
%endif
|
||||
%endmacro
|
||||
|
||||
%macro SU_MULP 1
|
||||
USE_MULP
|
||||
%xdefine CMDS CMDS MULP_ID + %1,
|
||||
%if %1 == STEREO
|
||||
%define INCLUDE_STEREO_MULP
|
||||
%endif
|
||||
%endmacro
|
||||
|
||||
;-------------------------------------------------------------------------------
|
||||
; PUSH related defines
|
||||
;-------------------------------------------------------------------------------
|
||||
%assign PUSH_ID -1
|
||||
%macro USE_PUSH 0
|
||||
%if PUSH_ID == -1
|
||||
%assign PUSH_ID CUR_ID
|
||||
%assign CUR_ID CUR_ID + 2
|
||||
%xdefine OPCODES OPCODES MANGLE_FUNC(su_op_push,0),
|
||||
%xdefine NUMPARAMS NUMPARAMS 0,
|
||||
%endif
|
||||
%endmacro
|
||||
|
||||
%macro SU_PUSH 1
|
||||
USE_PUSH
|
||||
%xdefine CMDS CMDS PUSH_ID + %1,
|
||||
%if %1 == STEREO
|
||||
%define INCLUDE_STEREO_PUSH
|
||||
%endif
|
||||
%endmacro
|
||||
|
||||
;-------------------------------------------------------------------------------
|
||||
; XCH related defines
|
||||
;-------------------------------------------------------------------------------
|
||||
%assign XCH_ID -1
|
||||
%macro USE_XCH 0
|
||||
%if XCH_ID == -1
|
||||
%assign XCH_ID CUR_ID
|
||||
%assign CUR_ID CUR_ID + 2
|
||||
%xdefine OPCODES OPCODES MANGLE_FUNC(su_op_xch,0),
|
||||
%xdefine NUMPARAMS NUMPARAMS 0,
|
||||
%endif
|
||||
%endmacro
|
||||
|
||||
%macro SU_XCH 1
|
||||
USE_XCH
|
||||
%xdefine CMDS CMDS XCH_ID + %1,
|
||||
%if %1 == STEREO
|
||||
%define INCLUDE_STEREO_XCH
|
||||
%endif
|
||||
%endmacro
|
||||
351
src/opcodes/effects.asm
Normal file
351
src/opcodes/effects.asm
Normal file
@ -0,0 +1,351 @@
|
||||
;-------------------------------------------------------------------------------
|
||||
; DISTORT Tick
|
||||
;-------------------------------------------------------------------------------
|
||||
; Input: st0 : x - input value
|
||||
; Output: st0 : x*a/(1-a+(2*a-1)*abs(x))
|
||||
; where x is clamped first
|
||||
;-------------------------------------------------------------------------------
|
||||
%if DISTORT_ID > -1
|
||||
|
||||
SECT_TEXT(sudistrt)
|
||||
|
||||
EXPORT MANGLE_FUNC(su_op_distort,0)
|
||||
%ifdef INCLUDE_STEREO_DISTORT
|
||||
jnc su_op_distort_mono
|
||||
call su_stereo_filterhelper
|
||||
%define INCLUDE_STEREO_FILTERHELPER
|
||||
su_op_distort_mono:
|
||||
%endif
|
||||
fld dword [edx+su_distort_ports.drive]
|
||||
%define SU_INCLUDE_WAVESHAPER
|
||||
; flow into waveshaper
|
||||
%endif
|
||||
|
||||
%ifdef SU_INCLUDE_WAVESHAPER
|
||||
su_waveshaper:
|
||||
fxch ; x a
|
||||
call MANGLE_FUNC(su_clip_op,0)
|
||||
fxch ; a x' (from now on just called x)
|
||||
fld st0 ; a a x
|
||||
fsub dword [c_0_5] ; a-.5 a x
|
||||
fadd st0 ; 2*a-1 a x
|
||||
fld st2 ; x 2*a-1 a x
|
||||
fabs ; abs(x) 2*a-1 a x
|
||||
fmulp st1 ; (2*a-1)*abs(x) a x
|
||||
fld1 ; 1 (2*a-1)*abs(x) a x
|
||||
faddp st1 ; 1+(2*a-1)*abs(x) a x
|
||||
fsub st1 ; 1-a+(2*a-1)*abs(x) a x
|
||||
fdivp st1, st0 ; a/(1-a+(2*a-1)*abs(x)) x
|
||||
fmulp st1 ; x*a/(1-a+(2*a-1)*abs(x))
|
||||
ret
|
||||
|
||||
%define SU_INCLUDE_CLIP
|
||||
|
||||
%endif ; SU_USE_DST
|
||||
|
||||
;-------------------------------------------------------------------------------
|
||||
; HOLD Tick
|
||||
;-------------------------------------------------------------------------------
|
||||
%if HOLD_ID > -1
|
||||
|
||||
SECT_TEXT(suhold)
|
||||
|
||||
EXPORT MANGLE_FUNC(su_op_hold,0)
|
||||
%ifdef INCLUDE_STEREO_HOLD
|
||||
jnc su_op_hold_mono
|
||||
call su_stereo_filterhelper
|
||||
%define INCLUDE_STEREO_FILTERHELPER
|
||||
su_op_hold_mono:
|
||||
%endif
|
||||
fld dword [edx+su_hold_ports.freq] ; f x
|
||||
fmul st0, st0 ; f^2 x
|
||||
fchs ; -f^2 x
|
||||
fadd dword [WRK+su_hold_wrk.phase] ; p-f^2 x
|
||||
fst dword [WRK+su_hold_wrk.phase] ; p <- p-f^2
|
||||
fldz ; 0 p x
|
||||
fucomip st1 ; p x
|
||||
fstp dword [esp-4] ; t=p, x
|
||||
jc short su_op_hold_holding ; if (0 < p) goto holding
|
||||
fld1 ; 1 x
|
||||
fadd dword [esp-4] ; 1+t x
|
||||
fstp dword [WRK+su_hold_wrk.phase] ; x
|
||||
fst dword [WRK+su_hold_wrk.holdval] ; save holded value
|
||||
ret ; x
|
||||
su_op_hold_holding:
|
||||
fstp st0 ;
|
||||
fld dword [WRK+su_hold_wrk.holdval] ; x
|
||||
ret
|
||||
|
||||
%endif ; HOLD_ID > -1
|
||||
|
||||
;-------------------------------------------------------------------------------
|
||||
; su_op_filter: perform low/high/band-pass filtering on the signal
|
||||
;-------------------------------------------------------------------------------
|
||||
; Input: WRK : pointer to unit workspace
|
||||
; VAL : pointer to unit values as bytes
|
||||
; ecx : pointer to global workspace
|
||||
; st0 : signal
|
||||
; Output: st0 : filtered signal
|
||||
; Dirty: eax, edx
|
||||
;-------------------------------------------------------------------------------
|
||||
%if FILTER_ID > -1
|
||||
SECT_TEXT(sufilter)
|
||||
|
||||
EXPORT MANGLE_FUNC(su_op_filter,0)
|
||||
lodsb ; load the flags to al
|
||||
%ifdef INCLUDE_STEREO_FILTER
|
||||
jnc su_op_filter_mono
|
||||
call su_stereo_filterhelper
|
||||
%define INCLUDE_STEREO_FILTERHELPER
|
||||
su_op_filter_mono:
|
||||
%endif
|
||||
fld dword [edx+su_filter_ports.res] ; r x
|
||||
fld dword [edx+su_filter_ports.freq]; f r x
|
||||
fmul st0, st0 ; f2 x (square the input so we never get negative and also have a smoother behaviour in the lower frequencies)
|
||||
fst dword [esp-4] ; f2 r x
|
||||
fmul dword [WRK+su_filter_wrk.band] ; f2*b r x
|
||||
fadd dword [WRK+su_filter_wrk.low] ; f2*b+l r x
|
||||
fst dword [WRK+su_filter_wrk.low] ; l'=f2*b+l r x
|
||||
fsubp st2, st0 ; r x-l'
|
||||
fmul dword [WRK+su_filter_wrk.band] ; r*b x-l'
|
||||
fsubp st1, st0 ; x-l'-r*b
|
||||
fst dword [WRK+su_filter_wrk.high] ; h'=x-l'-r*b
|
||||
fmul dword [esp-4] ; f2*h'
|
||||
fadd dword [WRK+su_filter_wrk.band] ; f2*h'+b
|
||||
fstp dword [WRK+su_filter_wrk.band] ; b'=f2*h'+b
|
||||
fldz ; 0
|
||||
%ifdef INCLUDE_LOWPASS
|
||||
test al, byte LOWPASS
|
||||
jz short su_op_filter_skiplowpass
|
||||
fadd dword [WRK+su_filter_wrk.low]
|
||||
su_op_filter_skiplowpass:
|
||||
%endif
|
||||
%ifdef INCLUDE_BANDPASS
|
||||
test al, byte BANDPASS
|
||||
jz short su_op_filter_skipbandpass
|
||||
fadd dword [WRK+su_filter_wrk.band]
|
||||
su_op_filter_skipbandpass:
|
||||
%endif
|
||||
%ifdef INCLUDE_HIGHPASS
|
||||
test al, byte HIGHPASS
|
||||
jz short su_op_filter_skiphighpass
|
||||
fadd dword [WRK+su_filter_wrk.high]
|
||||
su_op_filter_skiphighpass:
|
||||
%endif
|
||||
%ifdef INCLUDE_NEGBANDPASS
|
||||
test al, byte NEGBANDPASS
|
||||
jz short su_op_filter_skipnegbandpass
|
||||
fsub dword [WRK+su_filter_wrk.band]
|
||||
su_op_filter_skipnegbandpass:
|
||||
%endif
|
||||
%ifdef INCLUDE_NEGHIGHPASS
|
||||
test al, byte NEGHIGHPASS
|
||||
jz short su_op_filter_skipneghighpass
|
||||
fsub dword [WRK+su_filter_wrk.high]
|
||||
su_op_filter_skipneghighpass:
|
||||
%endif
|
||||
ret
|
||||
%endif ; SU_INCLUDE_FILTER
|
||||
|
||||
;-------------------------------------------------------------------------------
|
||||
; su_clip function
|
||||
;-------------------------------------------------------------------------------
|
||||
; Input: st0 : x
|
||||
; Output: st0 : min(max(x,-1),1)
|
||||
;-------------------------------------------------------------------------------
|
||||
%if CLIP_ID > -1
|
||||
%define SU_INCLUDE_CLIP
|
||||
%endif
|
||||
|
||||
%ifdef SU_INCLUDE_CLIP
|
||||
|
||||
SECT_TEXT(suclip)
|
||||
|
||||
EXPORT MANGLE_FUNC(su_clip_op,0)
|
||||
fld1 ; 1 x a
|
||||
fucomi st1 ; if (1 <= x)
|
||||
jbe short su_clip_do ; goto Clip_Do
|
||||
fchs ; -1 x a
|
||||
fucomi st1 ; if (-1 < x)
|
||||
fcmovb st0, st1 ; x x a
|
||||
su_clip_do:
|
||||
fstp st1 ; x' a, where x' = clamp(x)
|
||||
ret
|
||||
|
||||
%endif ; SU_INCLUDE_CLIP
|
||||
|
||||
;-------------------------------------------------------------------------------
|
||||
; PAN Tick
|
||||
;-------------------------------------------------------------------------------
|
||||
; Input: WRK : pointer to unit workspace
|
||||
; VAL : pointer to unit values as bytes
|
||||
; ecx : pointer to global workspace
|
||||
; st0 : s, the signal
|
||||
; Output: st0 : s*(1-p), where p is the panning in [0,1] range
|
||||
; st1 : s*p
|
||||
; Dirty: eax, edx
|
||||
;-------------------------------------------------------------------------------
|
||||
%if PAN_ID > -1
|
||||
|
||||
SECT_TEXT(supan)
|
||||
|
||||
%ifdef INCLUDE_STEREO_PAN
|
||||
|
||||
EXPORT MANGLE_FUNC(su_op_pan,0)
|
||||
jc su_op_pan_do ; this time, if this is mono op...
|
||||
fld st0 ; ...we duplicate the mono into stereo first
|
||||
su_op_pan_do:
|
||||
fld dword [edx+su_pan_ports.panning] ; p l r
|
||||
fld1 ; 1 p l r
|
||||
fsub st1 ; 1-p p l r
|
||||
fmulp st2 ; p (1-p)*l r
|
||||
fmulp st2 ; (1-p)*l p*r
|
||||
ret
|
||||
|
||||
%else ; ifndef INCLUDE_STEREO_PAN
|
||||
|
||||
EXPORT MANGLE_FUNC(su_op_pan,0)
|
||||
fld dword [edx+su_pan_ports.panning] ; p s
|
||||
fmul st1 ; p*s s
|
||||
fsub st1, st0 ; p*s s-p*s
|
||||
; Equal to
|
||||
; s*p s*(1-p)
|
||||
fxch ; s*(1-p) s*p SHOULD PROBABLY DELETE, WHY BOTHER
|
||||
ret
|
||||
|
||||
%endif ; INCLUDE_STEREO_PAN
|
||||
|
||||
%endif ; SU_USE_PAN
|
||||
|
||||
;-------------------------------------------------------------------------------
|
||||
; su_stereo_filterhelper: moves the workspace to next, does the filtering for
|
||||
; right channel (pulling the calling address from stack), rewinds the
|
||||
; workspace and returns
|
||||
;-------------------------------------------------------------------------------
|
||||
%ifdef INCLUDE_STEREO_FILTERHELPER
|
||||
|
||||
su_stereo_filterhelper:
|
||||
add WRK, 16
|
||||
fxch ; r l
|
||||
call dword [esp] ; call whoever called me...
|
||||
fxch ; l r
|
||||
sub WRK, 16 ; move WRK back to where it was
|
||||
ret
|
||||
|
||||
%endif
|
||||
|
||||
;-------------------------------------------------------------------------------
|
||||
; Delay Tick
|
||||
;-------------------------------------------------------------------------------
|
||||
; Pseudocode:
|
||||
; q = dr*x
|
||||
; for (i = 0;i < count;i++)
|
||||
; s = b[(t-delaytime[i+offset])&65535]
|
||||
; q += s
|
||||
; o[i] = o[i]*da+s*(1-da)
|
||||
; b[t] = f*o[i] +p^2*x
|
||||
; Perform dc-filtering q and output
|
||||
;-------------------------------------------------------------------------------
|
||||
%if DELAY_ID > -1
|
||||
|
||||
SECT_TEXT(sudelay)
|
||||
|
||||
EXPORT MANGLE_FUNC(su_op_delay,0)
|
||||
lodsb ; eax = delay index
|
||||
mov edi, eax
|
||||
lodsb ; eax = delay count
|
||||
%ifdef INCLUDE_STEREO_DELAY
|
||||
jnc su_op_delay_mono
|
||||
fxch
|
||||
call su_op_delay_mono ; do right delay
|
||||
fxch
|
||||
add edi, eax ; the second delay is done with the delay time index added by count
|
||||
su_op_delay_mono:
|
||||
%endif
|
||||
pushad
|
||||
mov ebx, edi; ugly register juggling, refactor
|
||||
%ifdef DELAY_NOTE_SYNC
|
||||
test ebx, ebx ; note s
|
||||
jne su_op_delay_skipnotesync
|
||||
fld1
|
||||
fild dword [ecx+su_unit.size-su_voice.workspace+su_voice.note]
|
||||
fmul dword [c_i12]
|
||||
call MANGLE_FUNC(su_power,0)
|
||||
fmul dword [c_freq_normalize] ; // normalize
|
||||
fdivp st1, st0 ; // invert to get numer of samples
|
||||
fistp word [MANGLE_DATA(su_delay_times)] ; store current comb size
|
||||
su_op_delay_skipnotesync:
|
||||
%endif
|
||||
kmDLL_func_process:
|
||||
mov ecx, eax ;// ecx is the number of parallel delays
|
||||
mov WRK, dword [MANGLE_DATA(su_delay_buffer_ofs)] ;// ebp is current delay
|
||||
fld st0 ; x x
|
||||
fmul dword [edx+su_delay_ports.dry] ; dr*x x
|
||||
fxch ; x dr*x
|
||||
fmul dword [edx+su_delay_ports.pregain] ; p*x dr*x
|
||||
fmul dword [edx+su_delay_ports.pregain] ; p^2*x dr*x
|
||||
|
||||
kmDLL_func_loop:
|
||||
mov edi, dword [WRK + su_delayline_wrk.time]
|
||||
inc edi
|
||||
and edi, MAX_DELAY-1
|
||||
mov dword [WRK + su_delayline_wrk.time],edi
|
||||
movzx esi, word [MANGLE_DATA(su_delay_times)+ebx*2] ; esi = comb size from the delay times table
|
||||
mov eax, edi
|
||||
sub eax, esi
|
||||
and eax, MAX_DELAY-1
|
||||
fld dword [WRK+eax*4+su_delayline_wrk.buffer] ; s p^2*x dr*x, where s is the sample from delay buffer
|
||||
;// add comb output to current output
|
||||
fadd st2, st0 ; s p^2*x dr*x+s
|
||||
fld1 ; 1 s p^2*x dr*x+s
|
||||
fsub dword [edx+su_delay_ports.damp] ; 1-da s p^2*x dr*x+s
|
||||
fmulp st1, st0 ; s*(1-da) p^2*x dr*x+s
|
||||
fld dword [edx+su_delay_ports.damp] ; da s*(1-da) p^2*x dr*x+s
|
||||
fmul dword [WRK+su_delayline_wrk.filtstate] ; o*da s*(1-da) p^2*x dr*x+s, where o is stored
|
||||
faddp st1, st0 ; o*da+s*(1-da) p^2*x dr*x+s
|
||||
fst dword [WRK+su_delayline_wrk.filtstate] ; o'=o*da+s*(1-da), o' p^2*x dr*x+s
|
||||
fmul dword [edx+su_delay_ports.feedback] ; f*o' p^2*x dr*x+s
|
||||
fadd st0, st1 ; f*o'+p^2*x p^2*x dr*x+s
|
||||
fstp dword [WRK+edi*4+su_delayline_wrk.buffer]; save f*o'+p^2*x to delay buffer
|
||||
inc ebx ;// go to next delay lenkmh index
|
||||
add WRK, su_delayline_wrk.size ;// go to next delay
|
||||
mov dword [MANGLE_DATA(su_delay_buffer_ofs)], WRK ;// store next delay offset
|
||||
loopne kmDLL_func_loop
|
||||
fstp st0 ; dr*x+s1+s2+s3+...
|
||||
; DC-filtering
|
||||
sub WRK, su_delayline_wrk.size ; the reason to use the last su_delayline_wrk instead of su_delay_wrk is that su_delay_wrk is wiped by retriggering
|
||||
fld dword [WRK+su_delayline_wrk.dcout] ; o s
|
||||
fmul dword [c_dc_const] ; c*o s
|
||||
fsub dword [WRK+su_delayline_wrk.dcin] ; c*o-i s
|
||||
fxch ; s c*o-i
|
||||
fst dword [WRK+su_delayline_wrk.dcin] ; i'=s, s c*o-i
|
||||
faddp st1 ; s+c*o-i
|
||||
fadd dword [c_0_5] ;// add and sub small offset to prevent denormalization
|
||||
fsub dword [c_0_5]
|
||||
fst dword [WRK+su_delayline_wrk.dcout] ; o'=s+c*o-i
|
||||
popad
|
||||
ret
|
||||
|
||||
;-------------------------------------------------------------------------------
|
||||
; Delay data
|
||||
;-------------------------------------------------------------------------------
|
||||
SECT_BSS(sudelbuf)
|
||||
|
||||
EXPORT MANGLE_DATA(su_delay_buffer_ofs)
|
||||
resd 1
|
||||
EXPORT MANGLE_DATA(su_delay_buffer)
|
||||
resb NUM_DELAY_LINES*su_delayline_wrk.size
|
||||
|
||||
SECT_DATA(suconst)
|
||||
|
||||
%ifndef C_DC_CONST
|
||||
c_dc_const dd 0.99609375 ; R = 1 - (pi*2 * frequency /samplerate)
|
||||
%define C_DC_CONST
|
||||
%endif
|
||||
|
||||
%ifndef C_FREQ_NORMALIZE
|
||||
c_freq_normalize dd 0.000092696138 ; // 220.0/(2^(69/12)) / 44100.0
|
||||
%define C_FREQ_NORMALIZE
|
||||
%endif
|
||||
|
||||
%endif ; DELAY_ID > -1
|
||||
256
src/opcodes/effects.inc
Normal file
256
src/opcodes/effects.inc
Normal file
@ -0,0 +1,256 @@
|
||||
;-------------------------------------------------------------------------------
|
||||
; Filter (LOWPASS, BANDPASS...) effect related defines
|
||||
;-------------------------------------------------------------------------------
|
||||
%assign FILTER_ID -1
|
||||
|
||||
%macro USE_FILTER 0
|
||||
%if FILTER_ID == -1
|
||||
%assign FILTER_ID CUR_ID
|
||||
%assign CUR_ID CUR_ID + 2
|
||||
%xdefine OPCODES OPCODES MANGLE_FUNC(su_op_filter,0),
|
||||
%xdefine NUMPARAMS NUMPARAMS 2,
|
||||
%endif
|
||||
%endmacro
|
||||
|
||||
%define LOWPASS 0x40
|
||||
%define BANDPASS 0x20
|
||||
%define HIGHPASS 0x10
|
||||
%define NEGBANDPASS 0x08
|
||||
%define NEGHIGHPASS 0x04
|
||||
|
||||
%macro SU_FILTER 4
|
||||
db %2
|
||||
db %3
|
||||
db %4
|
||||
USE_FILTER
|
||||
%xdefine CMDS CMDS FILTER_ID + %1,
|
||||
%if %1 == STEREO
|
||||
%define INCLUDE_STEREO_FILTER
|
||||
%endif
|
||||
%if (%4) & LOWPASS == LOWPASS
|
||||
%define INCLUDE_LOWPASS
|
||||
%endif
|
||||
%if (%4) & BANDPASS == BANDPASS
|
||||
%define INCLUDE_BANDPASS
|
||||
%endif
|
||||
%if (%4) & HIGHPASS == HIGHPASS
|
||||
%define INCLUDE_HIGHPASS
|
||||
%endif
|
||||
%if (%4) & NEGBANDPASS == NEGBANDPASS
|
||||
%define INCLUDE_NEGBANDPASS
|
||||
%endif
|
||||
%if (%4) & NEGHIGHPASS == NEGHIGHPASS
|
||||
%define INCLUDE_NEGHIGHPASS
|
||||
%endif
|
||||
%endmacro
|
||||
|
||||
%define FREQUENCY(val) val
|
||||
%define RESONANCE(val) val
|
||||
%define FLAGS(val) val
|
||||
|
||||
struc su_filter_ports
|
||||
.freq resd 1
|
||||
.res resd 1
|
||||
.params
|
||||
endstruc
|
||||
|
||||
struc su_filter_wrk
|
||||
.low resd 1
|
||||
.high resd 1
|
||||
.band resd 1
|
||||
.size
|
||||
endstruc
|
||||
|
||||
;-------------------------------------------------------------------------------
|
||||
; PAN effect related defines
|
||||
;-------------------------------------------------------------------------------
|
||||
%assign PAN_ID -1
|
||||
|
||||
%macro USE_PAN 0
|
||||
%if PAN_ID == -1
|
||||
%assign PAN_ID CUR_ID
|
||||
%assign CUR_ID CUR_ID + 2
|
||||
%xdefine OPCODES OPCODES MANGLE_FUNC(su_op_pan,0),
|
||||
%xdefine NUMPARAMS NUMPARAMS 1,
|
||||
%endif
|
||||
%endmacro
|
||||
|
||||
%macro SU_PAN 2
|
||||
db %2
|
||||
USE_PAN
|
||||
%xdefine CMDS CMDS PAN_ID + %1,
|
||||
%if %1 == STEREO
|
||||
%define INCLUDE_STEREO_PAN
|
||||
%endif
|
||||
%endmacro
|
||||
|
||||
%define PANNING(val) val
|
||||
|
||||
struc su_pan_ports
|
||||
.panning resd 1
|
||||
.params
|
||||
endstruc
|
||||
|
||||
;-------------------------------------------------------------------------------
|
||||
; DISTORT effect related defines
|
||||
;-------------------------------------------------------------------------------
|
||||
%assign DISTORT_ID -1
|
||||
|
||||
%macro USE_DISTORT 0
|
||||
%if DISTORT_ID == -1
|
||||
%assign DISTORT_ID CUR_ID
|
||||
%assign CUR_ID CUR_ID + 2
|
||||
%xdefine OPCODES OPCODES MANGLE_FUNC(su_op_distort,0),
|
||||
%xdefine NUMPARAMS NUMPARAMS 1,
|
||||
%endif
|
||||
%endmacro
|
||||
|
||||
%macro SU_DISTORT 2
|
||||
db %2
|
||||
USE_DISTORT
|
||||
%xdefine CMDS CMDS DISTORT_ID + %1,
|
||||
%if %1 == STEREO
|
||||
%define INCLUDE_STEREO_DISTORT
|
||||
%endif
|
||||
%endmacro
|
||||
|
||||
%define DRIVE(val) val
|
||||
|
||||
struc su_distort_ports
|
||||
.drive resd 1
|
||||
.params
|
||||
endstruc
|
||||
|
||||
;-------------------------------------------------------------------------------
|
||||
; HOLD effect related defines
|
||||
;-------------------------------------------------------------------------------
|
||||
%assign HOLD_ID -1
|
||||
|
||||
%macro USE_HOLD 0
|
||||
%if HOLD_ID == -1
|
||||
%assign HOLD_ID CUR_ID
|
||||
%assign CUR_ID CUR_ID + 2
|
||||
%xdefine OPCODES OPCODES MANGLE_FUNC(su_op_hold,0),
|
||||
%xdefine NUMPARAMS NUMPARAMS 1,
|
||||
%endif
|
||||
%endmacro
|
||||
|
||||
%macro SU_HOLD 2
|
||||
db %2
|
||||
USE_HOLD
|
||||
%xdefine CMDS CMDS HOLD_ID + %1,
|
||||
%if %1 == STEREO
|
||||
%define INCLUDE_STEREO_DISTORT
|
||||
%endif
|
||||
%endmacro
|
||||
|
||||
%define HOLDFREQ(val) val
|
||||
|
||||
struc su_hold_ports
|
||||
.freq resd 1
|
||||
.params
|
||||
endstruc
|
||||
|
||||
struc su_hold_wrk
|
||||
.phase resd 1
|
||||
.holdval resd 1
|
||||
endstruc
|
||||
|
||||
;-------------------------------------------------------------------------------
|
||||
; CLIP effect related defines
|
||||
;-------------------------------------------------------------------------------
|
||||
%assign CLIP_ID -1
|
||||
|
||||
%macro USE_CLIP 0
|
||||
%if CLIP_ID == -1
|
||||
%assign CLIP_ID CUR_ID
|
||||
%assign CUR_ID CUR_ID + 2
|
||||
%xdefine OPCODES OPCODES MANGLE_FUNC(su_op_clip,0),
|
||||
%xdefine NUMPARAMS NUMPARAMS 0,
|
||||
%endif
|
||||
%endmacro
|
||||
|
||||
%macro SU_CLIP 1
|
||||
USE_CLIP
|
||||
%xdefine CMDS CMDS CLIP_ID + %1,
|
||||
%if %1 == STEREO
|
||||
%define INCLUDE_STEREO_CLIP
|
||||
%endif
|
||||
%endmacro
|
||||
|
||||
;-------------------------------------------------------------------------------
|
||||
; Delay effect related defines
|
||||
;-------------------------------------------------------------------------------
|
||||
%assign DELAY_ID -1
|
||||
%macro USE_DELAY 0
|
||||
%if DELAY_ID == -1
|
||||
%assign DELAY_ID CUR_ID
|
||||
%assign CUR_ID CUR_ID + 2
|
||||
%xdefine OPCODES OPCODES MANGLE_FUNC(su_op_delay,0),
|
||||
%xdefine NUMPARAMS NUMPARAMS 4,
|
||||
%endif
|
||||
%endmacro
|
||||
|
||||
%define MAX_DELAY 65536
|
||||
%assign NUM_DELAY_LINES 0
|
||||
|
||||
%macro SU_DELAY 7
|
||||
db %2
|
||||
db %3
|
||||
db %4
|
||||
db %5
|
||||
db %6
|
||||
db %7
|
||||
USE_DELAY
|
||||
%xdefine CMDS CMDS DELAY_ID + %1,
|
||||
%assign NUM_DELAY_LINES NUM_DELAY_LINES + %7 * (1+%1)
|
||||
%if %1 == STEREO
|
||||
%define INCLUDE_STEREO_DELAY
|
||||
%endif
|
||||
%if %6 == 0
|
||||
%define DELAY_NOTE_SYNC
|
||||
%endif
|
||||
%endmacro
|
||||
|
||||
%macro SU_BEGIN_DELTIMES 0
|
||||
SECT_DATA(sudeltim)
|
||||
|
||||
EXPORT MANGLE_DATA(su_delay_times)
|
||||
dw 0
|
||||
%endmacro
|
||||
|
||||
%define SU_END_DELTIMES
|
||||
|
||||
%macro DELTIME 1-*
|
||||
%rep %0
|
||||
dw %1
|
||||
%rotate 1
|
||||
%endrep
|
||||
%endmacro
|
||||
|
||||
|
||||
%define PREGAIN(val) val
|
||||
%define DRY(val) val
|
||||
%define FEEDBACK(val) val
|
||||
%define DEPTH(val) val
|
||||
%define DAMP(val) val
|
||||
%define DELAY(val) val
|
||||
%define COUNT(val) val
|
||||
|
||||
struc su_delay_ports
|
||||
.pregain resd 1
|
||||
.dry resd 1
|
||||
.feedback resd 1
|
||||
.damp resd 1
|
||||
.freq resd 1
|
||||
.ports
|
||||
endstruc
|
||||
|
||||
struc su_delayline_wrk
|
||||
.time resd 1
|
||||
.filtstate resd 1
|
||||
.dcin resd 1
|
||||
.dcout resd 1
|
||||
.buffer resd MAX_DELAY
|
||||
.size
|
||||
endstruc
|
||||
63
src/opcodes/flowcontrol.asm
Normal file
63
src/opcodes/flowcontrol.asm
Normal file
@ -0,0 +1,63 @@
|
||||
;-------------------------------------------------------------------------------
|
||||
; su_op_advance function: opcode to advance from one instrument to next
|
||||
;-------------------------------------------------------------------------------
|
||||
; Stack: voice wrkptr valptr comptr
|
||||
; voice : the number of voice we are currently processing
|
||||
; wrkptr : pointer to the first unit of current voice - su_unit.size
|
||||
; valptr : pointer to the first unit's value of current voice
|
||||
; comptr : pointer to the first command of current voice
|
||||
; COM : pointer to the command after current command
|
||||
; Output: WRK : pointer to the next unit to be processed
|
||||
; VAL : pointer to the values of the next to be processed
|
||||
; COM : pointer to the next command to be executed
|
||||
;
|
||||
; Checks if this was the last voice of current instrument. If so, moves to
|
||||
; next opcodes and updates the stack to reflect the instrument change.
|
||||
; If this instrument has more voices to process, rolls back the COM and VAL
|
||||
; pointers to where they were when this instrument started.
|
||||
;-------------------------------------------------------------------------------
|
||||
SECT_TEXT(suopadvn)
|
||||
|
||||
%ifdef INCLUDE_POLYPHONY
|
||||
|
||||
EXPORT MANGLE_FUNC(su_op_advance,0) ; Stack: addr voice wrkptr valptr comptr
|
||||
mov WRK, dword [esp+8] ; WRK = wrkptr
|
||||
add WRK, su_voice.size ; move to next voice
|
||||
mov dword [esp+8], WRK ; update stack
|
||||
mov ecx, dword [esp+4] ; ecx = voice
|
||||
bt dword [su_polyphony_bitmask],ecx ; if voice bit of su_polyphonism not set
|
||||
jnc su_op_advance_next_instrument ; goto next_instrument
|
||||
mov VAL, dword [esp+12] ; rollback to where we were earlier
|
||||
mov COM, dword [esp+16]
|
||||
jmp short su_op_advance_finish
|
||||
su_op_advance_next_instrument:
|
||||
mov dword [esp+12], VAL ; save current VAL as a checkpoint
|
||||
mov dword [esp+16], COM ; save current COM as a checkpoint
|
||||
su_op_advance_finish:
|
||||
inc ecx ; voice++
|
||||
mov dword [esp+4], ecx
|
||||
ret
|
||||
|
||||
%else
|
||||
|
||||
EXPORT MANGLE_FUNC(su_op_advance,0) ; Stack: addr voice wrkptr valptr comptr
|
||||
mov WRK, dword [esp+8] ; WRK = wrkptr
|
||||
add WRK, su_voice.size ; move to next voice
|
||||
mov dword [esp+8], WRK ; update stack
|
||||
inc dword [esp+4] ; voice++
|
||||
ret
|
||||
|
||||
%endif
|
||||
|
||||
|
||||
;-------------------------------------------------------------------------------
|
||||
; Constants
|
||||
;-------------------------------------------------------------------------------
|
||||
%ifdef SU_USE_DLL_DC_FILTER
|
||||
%ifndef C_DC_CONST
|
||||
SECT_DATA(suconst)
|
||||
c_dc_const dd 0.99609375 ; R = 1 - (pi*2 * frequency /samplerate)
|
||||
%define C_DC_CONST
|
||||
%endif
|
||||
|
||||
%endif
|
||||
0
src/opcodes/flowcontrol.inc
Normal file
0
src/opcodes/flowcontrol.inc
Normal file
70
src/opcodes/sinks.asm
Normal file
70
src/opcodes/sinks.asm
Normal file
@ -0,0 +1,70 @@
|
||||
;-------------------------------------------------------------------------------
|
||||
; OUT Tick
|
||||
;-------------------------------------------------------------------------------
|
||||
%if OUT_ID > -1
|
||||
|
||||
SECT_TEXT(suopout)
|
||||
|
||||
EXPORT MANGLE_FUNC(su_op_out,0) ; l r
|
||||
mov eax, su_synth_obj + su_synth.left
|
||||
%ifdef INCLUDE_STEREO_OUT
|
||||
jnc su_op_out_mono
|
||||
call su_op_out_mono
|
||||
add eax, 4
|
||||
su_op_out_mono:
|
||||
%endif
|
||||
fmul dword [edx+su_out_ports.gain] ; g*l
|
||||
fadd dword [eax] ; g*l+o
|
||||
fstp dword [eax] ; o'=g*l+o
|
||||
ret
|
||||
|
||||
%endif ; SU_OUT_ID > -1
|
||||
|
||||
;-------------------------------------------------------------------------------
|
||||
; Send Tick
|
||||
;-------------------------------------------------------------------------------
|
||||
; Input: WRK : pointer to unit workspace
|
||||
; VAL : pointer to unit values as bytes
|
||||
; ecx : pointer to global workspace
|
||||
; st0 : signal
|
||||
; Output: (st0) : signal, unless popped
|
||||
; Dirty: eax, edx
|
||||
;-------------------------------------------------------------------------------
|
||||
%if SEND_ID > -1
|
||||
|
||||
SECT_TEXT(susend)
|
||||
|
||||
EXPORT MANGLE_FUNC(su_op_send,0)
|
||||
lodsw
|
||||
%ifdef INCLUDE_STEREO_SEND
|
||||
jnc su_op_send_mono
|
||||
mov edi, eax
|
||||
inc eax ; send the right channel first
|
||||
fxch ; r l
|
||||
call su_op_send_mono ; (r) l
|
||||
mov eax, edi ; move back to original address
|
||||
test edx, SEND_POP ; if r was not popped and is still in the stack
|
||||
jnz su_op_send_mono
|
||||
fxch ; swap them back: l r
|
||||
su_op_send_mono:
|
||||
%endif
|
||||
%ifdef INCLUDE_GLOBAL_SEND
|
||||
test eax, SEND_GLOBAL
|
||||
jz su_op_send_skipglobal
|
||||
mov ecx, su_synth_obj - su_unit.size
|
||||
su_op_send_skipglobal:
|
||||
%endif
|
||||
test eax, SEND_POP ; if the SEND_POP bit is not set
|
||||
jnz su_op_send_skippush
|
||||
fld st0 ; duplicate the signal on stack: s s
|
||||
su_op_send_skippush: ; there is signal s, but maybe also another: s (s)
|
||||
fld dword [edx+su_send_ports.amount] ; a l (l)
|
||||
fsub dword [c_0_5] ; a-.5 l (l)
|
||||
fadd st0 ; g=2*a-1 l (l)
|
||||
and eax, 0x0000ffff - SEND_POP - SEND_GLOBAL ; eax = send address
|
||||
fmulp st1, st0 ; g*l (l)
|
||||
fadd dword [ecx+su_unit.size+eax*4] ; g*l+L (l),where L is the current value
|
||||
fstp dword [ecx+su_unit.size+eax*4] ; (l)
|
||||
ret
|
||||
|
||||
%endif ; SU_USE_SEND > -1
|
||||
66
src/opcodes/sinks.inc
Normal file
66
src/opcodes/sinks.inc
Normal file
@ -0,0 +1,66 @@
|
||||
;-------------------------------------------------------------------------------
|
||||
; OUT structs
|
||||
;-------------------------------------------------------------------------------
|
||||
%assign OUT_ID -1
|
||||
%macro USE_OUT 0
|
||||
%if OUT_ID == -1
|
||||
%assign OUT_ID CUR_ID
|
||||
%assign CUR_ID CUR_ID + 2
|
||||
%xdefine OPCODES OPCODES MANGLE_FUNC(su_op_out,0),
|
||||
%xdefine NUMPARAMS NUMPARAMS 1,
|
||||
%endif
|
||||
%endmacro
|
||||
|
||||
%macro SU_OUT 2
|
||||
db %2
|
||||
USE_OUT
|
||||
%xdefine CMDS CMDS OUT_ID+%1,
|
||||
%if %1 == STEREO
|
||||
%define INCLUDE_STEREO_OUT
|
||||
%endif
|
||||
%endmacro
|
||||
|
||||
%define GAIN(val) val
|
||||
|
||||
struc su_out_ports
|
||||
.gain resd 1
|
||||
.params
|
||||
endstruc
|
||||
|
||||
;-------------------------------------------------------------------------------
|
||||
; SEND structs
|
||||
;-------------------------------------------------------------------------------
|
||||
%assign SEND_ID -1
|
||||
%macro USE_SEND 0
|
||||
%if SEND_ID == -1
|
||||
%assign SEND_ID CUR_ID
|
||||
%assign CUR_ID CUR_ID + 2
|
||||
%xdefine OPCODES OPCODES MANGLE_FUNC(su_op_send,0),
|
||||
%xdefine NUMPARAMS NUMPARAMS 1,
|
||||
%endif
|
||||
%endmacro
|
||||
|
||||
%macro SU_SEND 3
|
||||
db %2
|
||||
dw %3
|
||||
USE_SEND
|
||||
%xdefine CMDS CMDS SEND_ID + %1,
|
||||
%if %1 == STEREO
|
||||
%define INCLUDE_STEREO_SEND
|
||||
%endif
|
||||
%if (%3) & SEND_GLOBAL == SEND_GLOBAL
|
||||
%define INCLUDE_GLOBAL_SEND
|
||||
%endif
|
||||
%endmacro
|
||||
|
||||
%define AMOUNT(val) val
|
||||
%define PORT(unit,unittype,port) (unit*su_unit.size + su_ %+ unittype %+ _ports. %+ port + su_unit.ports)/4
|
||||
%define GLOBALPORT(voice,unit,unittype,port) SEND_GLOBAL + (su_synth.voices+voice*su_voice.size+su_voice.workspace+unit*su_unit.size + su_ %+ unittype %+ _ports. %+ port + su_unit.ports)/4
|
||||
%define OUTPORT 0
|
||||
%define SEND_POP 0x8000
|
||||
%define SEND_GLOBAL 0x4000
|
||||
|
||||
struc su_send_ports
|
||||
.amount resd 1
|
||||
.params
|
||||
endstruc
|
||||
312
src/opcodes/sources.asm
Normal file
312
src/opcodes/sources.asm
Normal file
@ -0,0 +1,312 @@
|
||||
;-------------------------------------------------------------------------------
|
||||
; ENV Tick
|
||||
;-------------------------------------------------------------------------------
|
||||
; Input: WRK : pointer to unit workspace
|
||||
; VAL : pointer to unit values as bytes
|
||||
; ecx : pointer to global workspace
|
||||
; Output: st0 : envelope value, [gain]*level. The slopes of
|
||||
; level is 2^(-24*p) per sample, where p is either
|
||||
; attack, decay or release in [0,1] range
|
||||
; Dirty: eax, edx
|
||||
;-------------------------------------------------------------------------------
|
||||
%if ENVELOPE_ID > -1
|
||||
|
||||
SECT_TEXT(suenvelo)
|
||||
|
||||
EXPORT MANGLE_FUNC(su_op_envelope,0)
|
||||
%ifdef INCLUDE_STEREO_ENVELOPE
|
||||
jnc su_op_envelope_mono
|
||||
call su_op_envelope_mono
|
||||
fld st0
|
||||
ret
|
||||
su_op_envelope_mono:
|
||||
%endif
|
||||
kmENV_func_do:
|
||||
mov eax, dword [ecx+su_unit.size-su_voice.workspace+su_voice.release] ; eax = su_instrument.release
|
||||
test eax, eax ; if (eax == 0)
|
||||
je kmENV_func_process ; goto process
|
||||
mov dword [WRK+su_env_work.state], ENV_STATE_RELEASE ; [state]=RELEASE
|
||||
kmENV_func_process:
|
||||
mov eax, dword [WRK+su_env_work.state] ; al=[state]
|
||||
fld dword [WRK+su_env_work.level] ; x=[level]
|
||||
cmp al, ENV_STATE_SUSTAIN ; if (al==SUSTAIN)
|
||||
je short kmENV_func_leave2 ; goto leave2
|
||||
kmENV_func_attac:
|
||||
cmp al, ENV_STATE_ATTAC ; if (al!=ATTAC)
|
||||
jne short kmENV_func_decay ; goto decay
|
||||
call su_env_map ; a x, where a=attack
|
||||
faddp st1, st0 ; a+x
|
||||
fld1 ; 1 a+x
|
||||
fucomi st1 ; if (a+x<=1) // is attack complete?
|
||||
fcmovnb st0, st1 ; a+x a+x
|
||||
jbe short kmENV_func_statechange ; else goto statechange
|
||||
kmENV_func_decay:
|
||||
cmp al, ENV_STATE_DECAY ; if (al!=DECAY)
|
||||
jne short kmENV_func_release ; goto release
|
||||
call su_env_map ; d x, where d=decay
|
||||
fsubp st1, st0 ; x-d
|
||||
fld dword [edx+su_env_ports.sustain] ; s x-d, where s=sustain
|
||||
fucomi st1 ; if (x-d>s) // is decay complete?
|
||||
fcmovb st0, st1 ; x-d x-d
|
||||
jnc short kmENV_func_statechange ; else goto statechange
|
||||
kmENV_func_release:
|
||||
cmp al, ENV_STATE_RELEASE ; if (al!=RELEASE)
|
||||
jne short kmENV_func_leave ; goto leave
|
||||
call su_env_map ; r x, where r=release
|
||||
fsubp st1, st0 ; x-r
|
||||
fldz ; 0 x-r
|
||||
fucomi st1 ; if (x-r>0) // is release complete?
|
||||
fcmovb st0, st1 ; x-r x-r, then goto leave
|
||||
jc short kmENV_func_leave
|
||||
kmENV_func_statechange:
|
||||
inc dword [WRK+su_env_work.state] ; [state]++
|
||||
kmENV_func_leave:
|
||||
fstp st1 ; x', where x' is the new value
|
||||
fst dword [WRK+su_env_work.level] ; [level]=x'
|
||||
kmENV_func_leave2:
|
||||
fmul dword [edx+su_env_ports.gain] ; [gain]*x'
|
||||
ret
|
||||
|
||||
%endif ; SU_USE_ENVELOPE
|
||||
|
||||
;-------------------------------------------------------------------------------
|
||||
; su_noise function: noise oscillators
|
||||
;-------------------------------------------------------------------------------
|
||||
%if NOISE_ID > -1
|
||||
|
||||
SECT_TEXT(sunoise)
|
||||
|
||||
EXPORT MANGLE_FUNC(su_op_noise,0)
|
||||
%ifdef INCLUDE_STEREO_NOISE
|
||||
jnc su_op_noise_mono
|
||||
clc
|
||||
call dword [esp]
|
||||
su_op_noise_mono:
|
||||
%endif
|
||||
call MANGLE_FUNC(FloatRandomNumber,0)
|
||||
fld dword [edx+su_noise_ports.shape]
|
||||
call su_waveshaper
|
||||
fld dword [edx+su_noise_ports.gain]
|
||||
fmulp st1, st0
|
||||
ret
|
||||
|
||||
%define SU_INCLUDE_WAVESHAPER
|
||||
|
||||
%endif
|
||||
|
||||
;-------------------------------------------------------------------------------
|
||||
; su_op_oscillat function: oscillator, the heart of the synth
|
||||
;-------------------------------------------------------------------------------
|
||||
%if OSCILLAT_ID > -1
|
||||
|
||||
SECT_TEXT(suoscill)
|
||||
|
||||
EXPORT MANGLE_FUNC(su_op_oscillat,0)
|
||||
lodsb ; load the flags
|
||||
%ifdef INCLUDE_STEREO_OSCILLAT
|
||||
jnc su_op_oscillat_mono
|
||||
add WRK, 4
|
||||
call su_op_oscillat_mono
|
||||
fld1 ; invert the detune for second run for some stereo separation
|
||||
fld dword [edx+su_osc_ports.detune]
|
||||
fsubp st1
|
||||
fstp dword [edx+su_osc_ports.detune]
|
||||
sub WRK, 4
|
||||
su_op_oscillat_mono:
|
||||
%endif
|
||||
fld dword [edx+su_osc_ports.transpose]
|
||||
fsub dword [c_0_5]
|
||||
fdiv dword [c_i128]
|
||||
fld dword [edx+su_osc_ports.detune]
|
||||
fsub dword [c_0_5]
|
||||
fadd st0
|
||||
faddp st1
|
||||
test al, byte LFO
|
||||
jnz su_op_oscillat_skipnote
|
||||
fiadd dword [ecx+su_unit.size-su_voice.workspace+su_voice.note] ; // st0 is note, st1 is t+d offset
|
||||
su_op_oscillat_skipnote:
|
||||
fmul dword [c_i12]
|
||||
call MANGLE_FUNC(su_power,0)
|
||||
test al, byte LFO
|
||||
jz short su_op_oscillat_normalize_note
|
||||
fmul dword [c_lfo_normalize] ; // st0 is now frequency for lfo
|
||||
jmp short su_op_oscillat_normalized
|
||||
su_op_oscillat_normalize_note:
|
||||
fmul dword [c_freq_normalize] ; // st0 is now frequency
|
||||
su_op_oscillat_normalized:
|
||||
fadd dword [WRK+su_osc_wrk.phase]
|
||||
fst dword [WRK+su_osc_wrk.phase]
|
||||
fadd dword [edx+su_osc_ports.phaseofs]
|
||||
fld1
|
||||
fadd st1, st0
|
||||
fxch
|
||||
fprem
|
||||
fstp st1
|
||||
fld dword [edx+su_osc_ports.color] ; // c p
|
||||
; every oscillator test included if needed
|
||||
%ifdef INCLUDE_SINE
|
||||
test al, byte SINE
|
||||
jz short su_op_oscillat_notsine
|
||||
call su_oscillat_sine
|
||||
su_op_oscillat_notsine:
|
||||
%endif
|
||||
%ifdef INCLUDE_TRISAW
|
||||
test al, byte TRISAW
|
||||
jz short su_op_oscillat_not_trisaw
|
||||
call su_oscillat_trisaw
|
||||
su_op_oscillat_not_trisaw:
|
||||
%endif
|
||||
%ifdef INCLUDE_PULSE
|
||||
test al, byte PULSE
|
||||
jz short su_op_oscillat_not_pulse
|
||||
call su_oscillat_pulse
|
||||
su_op_oscillat_not_pulse:
|
||||
%endif
|
||||
%ifdef INCLUDE_GATE
|
||||
test al, byte GATE
|
||||
jz short su_op_oscillat_not_gate
|
||||
call su_oscillat_gate
|
||||
jmp su_op_oscillat_gain ; skip waveshaping as the shape parameter is reused for gateshigh
|
||||
su_op_oscillat_not_gate:
|
||||
%endif
|
||||
; finally, shape the oscillator and apply gain
|
||||
fld dword [edx+su_osc_ports.shape]
|
||||
call su_waveshaper
|
||||
su_op_oscillat_gain:
|
||||
fld dword [edx+su_osc_ports.gain]
|
||||
fmulp st1, st0
|
||||
ret
|
||||
%define SU_INCLUDE_WAVESHAPER
|
||||
|
||||
SECT_DATA(suconst)
|
||||
|
||||
%ifndef C_FREQ_NORMALIZE
|
||||
c_freq_normalize dd 0.000092696138 ; // 220.0/(2^(69/12)) / 44100.0
|
||||
%define C_FREQ_NORMALIZE
|
||||
%endif
|
||||
c_lfo_normalize dd 0.000038
|
||||
|
||||
%endif
|
||||
|
||||
; PULSE
|
||||
%ifdef INCLUDE_PULSE
|
||||
|
||||
SECT_TEXT(supulse)
|
||||
|
||||
su_oscillat_pulse:
|
||||
fucomi st1 ; // c p
|
||||
fld1
|
||||
jnc short su_oscillat_pulse_up ; // +1 c p
|
||||
fchs ; // -1 c p
|
||||
su_oscillat_pulse_up:
|
||||
fstp st1 ; // +-1 p
|
||||
fstp st1 ; // +-1
|
||||
ret
|
||||
|
||||
%endif
|
||||
|
||||
; TRISAW
|
||||
%ifdef INCLUDE_TRISAW
|
||||
|
||||
SECT_TEXT(sutrisaw)
|
||||
|
||||
su_oscillat_trisaw:
|
||||
fucomi st1 ; // c p
|
||||
jnc short su_oscillat_trisaw_up
|
||||
fld1 ; // 1 c p
|
||||
fsubr st2, st0 ; // 1 c 1-p
|
||||
fsubrp st1, st0 ; // 1-c 1-p
|
||||
su_oscillat_trisaw_up:
|
||||
fdivp st1, st0 ; // tp'/tc
|
||||
fadd st0 ; // 2*''
|
||||
fld1 ; // 1 2*''
|
||||
fsubp st1, st0 ; // 2*''-1
|
||||
ret
|
||||
%endif
|
||||
|
||||
; SINE
|
||||
%ifdef INCLUDE_SINE
|
||||
|
||||
SECT_TEXT(susine)
|
||||
|
||||
su_oscillat_sine:
|
||||
fucomi st1 ; // c p
|
||||
jnc short su_oscillat_sine_do
|
||||
fstp st1
|
||||
fsub st0, st0 ; // 0
|
||||
ret
|
||||
su_oscillat_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
|
||||
|
||||
%endif
|
||||
|
||||
%ifdef INCLUDE_GATE
|
||||
|
||||
SECT_TEXT(sugate)
|
||||
|
||||
su_oscillat_gate:
|
||||
fxch ; p c
|
||||
fstp st1 ; p
|
||||
fmul dword [c_16] ; 16*p
|
||||
push eax
|
||||
push eax
|
||||
fistp dword [esp] ; s=int(16*p), stack empty
|
||||
fld1 ; 1
|
||||
pop eax
|
||||
and al, 0xf ; ax=int(16*p) & 15, stack: 1
|
||||
bt word [VAL-4],ax ; if bit ax of the gate word is set
|
||||
jc go4kVCO_gate_bit ; goto gate_bit
|
||||
fsub st0, st0 ; stack: 0
|
||||
go4kVCO_gate_bit: ; stack: 0/1, let's call it x
|
||||
fld dword [WRK+su_osc_wrk.gatestate] ; g x, g is gatestate, x is the input to this filter 0/1
|
||||
fsub st1 ; g-x x
|
||||
fmul dword [c_dc_const] ; c(g-x) x
|
||||
faddp st1, st0 ; x+c(g-x)
|
||||
fst dword [WRK+su_osc_wrk.gatestate] ; g'=x+c(g-x)
|
||||
pop eax ; Another way to see this (c~0.996)
|
||||
ret ; g'=cg+(1-c)x
|
||||
; This is a low-pass to smooth the gate transitions
|
||||
|
||||
SECT_DATA(suconst)
|
||||
|
||||
%ifndef C_16
|
||||
c_16 dd 16.0
|
||||
%define C_16
|
||||
%endif
|
||||
|
||||
%ifndef C_DC_CONST
|
||||
c_dc_const dd 0.99609375 ; R = 1 - (pi*2 * frequency /samplerate)
|
||||
%define C_DC_CONST
|
||||
%endif
|
||||
|
||||
%endif
|
||||
|
||||
;-------------------------------------------------------------------------------
|
||||
; LOAD_VAL opcode
|
||||
;-------------------------------------------------------------------------------
|
||||
; Input: WRK : pointer to unit workspace
|
||||
; VAL : pointer to unit values as bytes
|
||||
; Output: st0 : 2*v-1, where v is the loaded value
|
||||
; Dirty: eax, edx
|
||||
;-------------------------------------------------------------------------------
|
||||
%if LOAD_VAL_ID > -1
|
||||
|
||||
SECT_TEXT(suloadvl)
|
||||
|
||||
EXPORT MANGLE_FUNC(su_op_load_val,0)
|
||||
%ifdef INCLUDE_STEREO_LOAD_VAL
|
||||
jnc su_op_load_val_mono
|
||||
call su_load_val_mono
|
||||
su_load_val_mono:
|
||||
%endif
|
||||
fld dword [edx+su_load_val_ports.value] ; v
|
||||
fsub dword [c_0_5] ; v-.5
|
||||
fadd st0 ; 2*v-1
|
||||
ret
|
||||
|
||||
%endif ; SU_USE_LOAD_VAL
|
||||
185
src/opcodes/sources.inc
Normal file
185
src/opcodes/sources.inc
Normal file
@ -0,0 +1,185 @@
|
||||
;-------------------------------------------------------------------------------
|
||||
; ENVELOPE structs
|
||||
;-------------------------------------------------------------------------------
|
||||
%assign ENVELOPE_ID -1
|
||||
%macro USE_ENVELOPE 0
|
||||
%if ENVELOPE_ID == -1
|
||||
%assign ENVELOPE_ID CUR_ID
|
||||
%assign CUR_ID CUR_ID + 2
|
||||
%xdefine OPCODES OPCODES MANGLE_FUNC(su_op_envelope,0),
|
||||
%xdefine NUMPARAMS NUMPARAMS 5,
|
||||
%endif
|
||||
%endmacro
|
||||
|
||||
%macro SU_ENVELOPE 6
|
||||
db %2
|
||||
db %3
|
||||
db %4
|
||||
db %5
|
||||
db %6
|
||||
USE_ENVELOPE
|
||||
%xdefine CMDS CMDS ENVELOPE_ID+%1,
|
||||
%if %1 == STEREO
|
||||
%define INCLUDE_STEREO_ENVELOPE
|
||||
%endif
|
||||
%endmacro
|
||||
|
||||
%define ATTAC(val) val
|
||||
%define DECAY(val) val
|
||||
%define SUSTAIN(val) val
|
||||
%define RELEASE(val) val
|
||||
%define GAIN(val) val
|
||||
|
||||
struc su_env_ports
|
||||
.attac resd 1
|
||||
.decay resd 1
|
||||
.sustain resd 1
|
||||
.release resd 1
|
||||
.gain resd 1
|
||||
.params
|
||||
endstruc
|
||||
|
||||
struc su_env_work
|
||||
.state resd 1
|
||||
.level resd 1
|
||||
.size
|
||||
endstruc
|
||||
|
||||
%define ENV_STATE_ATTAC 0
|
||||
%define ENV_STATE_DECAY 1
|
||||
%define ENV_STATE_SUSTAIN 2
|
||||
%define ENV_STATE_RELEASE 3
|
||||
%define ENV_STATE_OFF 4
|
||||
|
||||
;-------------------------------------------------------------------------------
|
||||
; OSCILLAT structs
|
||||
;-------------------------------------------------------------------------------
|
||||
%assign OSCILLAT_ID -1
|
||||
%macro USE_OSCILLAT 0
|
||||
%if OSCILLAT_ID == -1
|
||||
%assign OSCILLAT_ID CUR_ID
|
||||
%assign CUR_ID CUR_ID + 2
|
||||
%xdefine OPCODES OPCODES MANGLE_FUNC(su_op_oscillat,0),
|
||||
%xdefine NUMPARAMS NUMPARAMS 6,
|
||||
%endif
|
||||
%endmacro
|
||||
|
||||
%define SINE 0x40
|
||||
%define TRISAW 0x20
|
||||
%define PULSE 0x10
|
||||
%define LFO 0x08
|
||||
%define GATE 0x04
|
||||
|
||||
%macro SU_OSCILLAT 8
|
||||
db %2
|
||||
db %3
|
||||
db %4
|
||||
db %5
|
||||
db %6
|
||||
db %7
|
||||
db %8
|
||||
USE_OSCILLAT
|
||||
%xdefine CMDS CMDS OSCILLAT_ID + %1,
|
||||
%if %1 == STEREO
|
||||
%define INCLUDE_STEREO_OSCILLAT
|
||||
%endif
|
||||
%if (%8) & SINE == SINE
|
||||
%define INCLUDE_SINE
|
||||
%endif
|
||||
%if (%8) & TRISAW == TRISAW
|
||||
%define INCLUDE_TRISAW
|
||||
%endif
|
||||
%if (%8) & PULSE == PULSE
|
||||
%define INCLUDE_PULSE
|
||||
%endif
|
||||
%if (%8) & GATE == GATE
|
||||
%define INCLUDE_GATE
|
||||
%endif
|
||||
%endmacro
|
||||
|
||||
struc su_osc_ports
|
||||
.transpose resd 1
|
||||
.detune resd 1
|
||||
.phaseofs resd 1
|
||||
.color resd 1
|
||||
.shape resd 1
|
||||
.gain resd 1
|
||||
.params
|
||||
endstruc
|
||||
|
||||
struc su_osc_wrk
|
||||
.phase resd 1
|
||||
.gatestate resd 1
|
||||
.size
|
||||
endstruc
|
||||
|
||||
%define TRANSPOSE(val) val
|
||||
%define DETUNE(val) val
|
||||
%define PHASE(val) val
|
||||
%define GATESLOW(val) val
|
||||
%define GATESHIGH(val) val
|
||||
%define COLOR(val) val
|
||||
%define SHAPE(val) val
|
||||
%define FLAGS(val) val
|
||||
|
||||
;-------------------------------------------------------------------------------
|
||||
; NOISE structs
|
||||
;-------------------------------------------------------------------------------
|
||||
%assign NOISE_ID -1
|
||||
%macro USE_NOISE 0
|
||||
%if NOISE_ID == -1
|
||||
%assign NOISE_ID CUR_ID
|
||||
%assign CUR_ID CUR_ID + 2
|
||||
%xdefine OPCODES OPCODES MANGLE_FUNC(su_op_noise,0),
|
||||
%xdefine NUMPARAMS NUMPARAMS 2,
|
||||
%endif
|
||||
%endmacro
|
||||
|
||||
%macro SU_NOISE 3
|
||||
db %2
|
||||
db %3
|
||||
USE_NOISE
|
||||
%xdefine CMDS CMDS NOISE_ID + %1,
|
||||
%if %1 == STEREO
|
||||
%define INCLUDE_STEREO_NOISE
|
||||
%endif
|
||||
%endmacro
|
||||
|
||||
struc su_noise_ports
|
||||
.shape resd 1
|
||||
.gain resd 1
|
||||
.params
|
||||
endstruc
|
||||
|
||||
;-------------------------------------------------------------------------------
|
||||
; LOAD_VAL structs
|
||||
;-------------------------------------------------------------------------------
|
||||
%assign LOAD_VAL_ID -1
|
||||
%macro USE_LOAD_VAL 0
|
||||
%if LOAD_VAL_ID == -1
|
||||
%assign LOAD_VAL_ID CUR_ID
|
||||
%assign CUR_ID CUR_ID + 2
|
||||
%xdefine OPCODES OPCODES MANGLE_FUNC(su_op_load_val,0),
|
||||
%xdefine NUMPARAMS NUMPARAMS 1,
|
||||
%endif
|
||||
%endmacro
|
||||
|
||||
%macro SU_LOAD_VAL 2
|
||||
db %2
|
||||
USE_LOAD_VAL
|
||||
%xdefine CMDS CMDS LOAD_VAL_ID+%1,
|
||||
%if %1 == STEREO
|
||||
%define INCLUDE_STEREO_LOAD_VAL
|
||||
%endif
|
||||
%endmacro
|
||||
|
||||
%define VALUE(val) val
|
||||
|
||||
struc su_load_val_ports
|
||||
.value resd 1
|
||||
.params
|
||||
endstruc
|
||||
|
||||
struc su_load_val_wrk
|
||||
.size
|
||||
endstruc
|
||||
Reference in New Issue
Block a user