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:
Veikko Sariola
2020-05-16 08:25:52 +03:00
parent 5c1b87f254
commit 78d4cd50e8
238 changed files with 3460 additions and 21774 deletions

155
src/opcodes/arithmetic.asm Normal file
View 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
View 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
View 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
View 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

View 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

View File

70
src/opcodes/sinks.asm Normal file
View 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
View 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
View 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
View 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