mirror of
https://github.com/vsariola/sointu.git
synced 2025-07-18 21:14:31 -04:00
feat(asm&go4k): Rewrote both library & player to use text/template compiler
There is no more plain .asms, both library & player are created from the templates using go text/template package.
This commit is contained in:
@ -1,4 +1,4 @@
|
||||
{{- if .Opcode "pop"}}
|
||||
{{- if .HasOp "pop"}}
|
||||
;-------------------------------------------------------------------------------
|
||||
; POP opcode: remove (discard) the topmost signal from the stack
|
||||
;-------------------------------------------------------------------------------
|
||||
@ -24,7 +24,7 @@ su_op_pop_mono:
|
||||
{{end}}
|
||||
|
||||
|
||||
{{- if .Opcode "add"}}
|
||||
{{- if .HasOp "add"}}
|
||||
;-------------------------------------------------------------------------------
|
||||
; ADD opcode: add the two top most signals on the stack
|
||||
;-------------------------------------------------------------------------------
|
||||
@ -58,7 +58,7 @@ su_op_add_mono:
|
||||
{{end}}
|
||||
|
||||
|
||||
{{- if .Opcode "addp"}}
|
||||
{{- if .HasOp "addp"}}
|
||||
;-------------------------------------------------------------------------------
|
||||
; ADDP opcode: add the two top most signals on the stack and pop
|
||||
;-------------------------------------------------------------------------------
|
||||
@ -84,7 +84,7 @@ su_op_addp_mono:
|
||||
{{end}}
|
||||
|
||||
|
||||
{{- if .Opcode "loadnote"}}
|
||||
{{- if .HasOp "loadnote"}}
|
||||
;-------------------------------------------------------------------------------
|
||||
; LOADNOTE opcode: load the current note, scaled to [-1,1]
|
||||
;-------------------------------------------------------------------------------
|
||||
@ -109,7 +109,7 @@ su_op_addp_mono:
|
||||
{{end}}
|
||||
|
||||
|
||||
{{- if .Opcode "mul"}}
|
||||
{{- if .HasOp "mul"}}
|
||||
;-------------------------------------------------------------------------------
|
||||
; MUL opcode: multiply the two top most signals on the stack
|
||||
;-------------------------------------------------------------------------------
|
||||
@ -129,7 +129,7 @@ su_op_mul_mono:
|
||||
{{end}}
|
||||
|
||||
|
||||
{{- if .Opcode "mulp"}}
|
||||
{{- if .HasOp "mulp"}}
|
||||
;-------------------------------------------------------------------------------
|
||||
; MULP opcode: multiply the two top most signals on the stack and pop
|
||||
;-------------------------------------------------------------------------------
|
||||
@ -155,7 +155,7 @@ su_op_mulp_mono:
|
||||
{{end}}
|
||||
|
||||
|
||||
{{- if .Opcode "push"}}
|
||||
{{- if .HasOp "push"}}
|
||||
;-------------------------------------------------------------------------------
|
||||
; PUSH opcode: push the topmost signal on the stack
|
||||
;-------------------------------------------------------------------------------
|
||||
@ -181,7 +181,7 @@ su_op_push_mono:
|
||||
{{end}}
|
||||
|
||||
|
||||
{{- if .Opcode "xch"}}
|
||||
{{- if .HasOp "xch"}}
|
||||
;-------------------------------------------------------------------------------
|
||||
; XCH opcode: exchange the signals on the stack
|
||||
;-------------------------------------------------------------------------------
|
||||
|
@ -1,4 +1,4 @@
|
||||
{{- if .Opcode "distort"}}
|
||||
{{- if .HasOp "distort"}}
|
||||
;-------------------------------------------------------------------------------
|
||||
; DISTORT opcode: apply distortion on the signal
|
||||
;-------------------------------------------------------------------------------
|
||||
@ -14,7 +14,7 @@
|
||||
{{end}}
|
||||
|
||||
|
||||
{{- if .Opcode "hold"}}
|
||||
{{- if .HasOp "hold"}}
|
||||
;-------------------------------------------------------------------------------
|
||||
; HOLD opcode: sample and hold the signal, reducing sample rate
|
||||
;-------------------------------------------------------------------------------
|
||||
@ -46,7 +46,7 @@ su_op_hold_holding:
|
||||
{{end}}
|
||||
|
||||
|
||||
{{- if .Opcode "crush"}}
|
||||
{{- if .HasOp "crush"}}
|
||||
;-------------------------------------------------------------------------------
|
||||
; CRUSH opcode: quantize the signal to finite number of levels
|
||||
;-------------------------------------------------------------------------------
|
||||
@ -64,7 +64,7 @@ su_op_hold_holding:
|
||||
{{end}}
|
||||
|
||||
|
||||
{{- if .Opcode "gain"}}
|
||||
{{- if .HasOp "gain"}}
|
||||
;-------------------------------------------------------------------------------
|
||||
; GAIN opcode: apply gain on the signal
|
||||
;-------------------------------------------------------------------------------
|
||||
@ -88,7 +88,7 @@ su_op_gain_mono:
|
||||
{{end}}
|
||||
|
||||
|
||||
{{- if .Opcode "invgain"}}
|
||||
{{- if .HasOp "invgain"}}
|
||||
;-------------------------------------------------------------------------------
|
||||
; INVGAIN opcode: apply inverse gain on the signal
|
||||
;-------------------------------------------------------------------------------
|
||||
@ -112,7 +112,7 @@ su_op_invgain_mono:
|
||||
{{end}}
|
||||
|
||||
|
||||
{{- if .Opcode "filter"}}
|
||||
{{- if .HasOp "filter"}}
|
||||
;-------------------------------------------------------------------------------
|
||||
; FILTER opcode: perform low/high/band-pass/notch etc. filtering on the signal
|
||||
;-------------------------------------------------------------------------------
|
||||
@ -139,31 +139,31 @@ su_op_invgain_mono:
|
||||
fadd dword [{{.WRK}}+8] ; f2*h'+b
|
||||
fstp dword [{{.WRK}}+8] ; b'=f2*h'+b
|
||||
fldz ; 0
|
||||
{{- if .HasParamValue "filter" "lowpass" 1}}
|
||||
{{- if .SupportsParamValue "filter" "lowpass" 1}}
|
||||
test al, byte 0x40
|
||||
jz short su_op_filter_skiplowpass
|
||||
fadd dword [{{.WRK}}]
|
||||
su_op_filter_skiplowpass:
|
||||
{{- end}}
|
||||
{{- if .HasParamValue "filter" "bandpass" 1}}
|
||||
{{- if .SupportsParamValue "filter" "bandpass" 1}}
|
||||
test al, byte 0x20
|
||||
jz short su_op_filter_skipbandpass
|
||||
fadd dword [{{.WRK}}+8]
|
||||
su_op_filter_skipbandpass:
|
||||
{{- end}}
|
||||
{{- if .HasParamValue "filter" "highpass" 1}}
|
||||
{{- if .SupportsParamValue "filter" "highpass" 1}}
|
||||
test al, byte 0x10
|
||||
jz short su_op_filter_skiphighpass
|
||||
fadd dword [{{.WRK}}+4]
|
||||
su_op_filter_skiphighpass:
|
||||
{{- end}}
|
||||
{{- if .HasParamValue "filter" "negbandpass" 1}}
|
||||
{{- if .SupportsParamValue "filter" "negbandpass" 1}}
|
||||
test al, byte 0x08
|
||||
jz short su_op_filter_skipnegbandpass
|
||||
fsub dword [{{.WRK}}+8]
|
||||
su_op_filter_skipnegbandpass:
|
||||
{{- end}}
|
||||
{{- if .HasParamValue "filter" "neghighpass" 1}}
|
||||
{{- if .SupportsParamValue "filter" "neghighpass" 1}}
|
||||
test al, byte 0x04
|
||||
jz short su_op_filter_skipneghighpass
|
||||
fsub dword [{{.WRK}}+4]
|
||||
@ -173,7 +173,7 @@ su_op_filter_skipneghighpass:
|
||||
{{end}}
|
||||
|
||||
|
||||
{{- if .Opcode "clip"}}
|
||||
{{- if .HasOp "clip"}}
|
||||
;-------------------------------------------------------------------------------
|
||||
; CLIP opcode: clips the signal into [-1,1] range
|
||||
;-------------------------------------------------------------------------------
|
||||
@ -188,7 +188,7 @@ su_op_filter_skipneghighpass:
|
||||
{{end}}
|
||||
|
||||
|
||||
{{- if .Opcode "pan" -}}
|
||||
{{- if .HasOp "pan" -}}
|
||||
;-------------------------------------------------------------------------------
|
||||
; PAN opcode: pan the signal
|
||||
;-------------------------------------------------------------------------------
|
||||
@ -220,7 +220,7 @@ su_op_pan_do:
|
||||
{{end}}
|
||||
|
||||
|
||||
{{- if .Opcode "delay"}}
|
||||
{{- if .HasOp "delay"}}
|
||||
;-------------------------------------------------------------------------------
|
||||
; DELAY opcode: adds delay effect to the signal
|
||||
;-------------------------------------------------------------------------------
|
||||
@ -234,12 +234,13 @@ su_op_pan_do:
|
||||
lodsw ; al = delay index, ah = delay count
|
||||
{{- .PushRegs .VAL "DelayVal" .COM "DelayCom" | indent 4}}
|
||||
movzx ebx, al
|
||||
; %ifdef RUNTIME_TABLES ; when using runtime tables, delaytimes is pulled from the stack so can be a pointer to heap
|
||||
; mov _SI, [{{.SP}} + su_stack.delaytimes + PUSH_REG_SIZE(2)]
|
||||
; lea _BX, [_SI + _BX*2]
|
||||
; %else
|
||||
{{.Prepare "su_delay_times" | indent 4}}
|
||||
{{- if .Library}}
|
||||
mov {{.SI}}, [{{.Stack "DelayTable"}}] ; when using runtime tables, delaytimes is pulled from the stack so can be a pointer to heap
|
||||
lea {{.BX}}, [{{.SI}} + {{.BX}}*2]
|
||||
{{- else}}
|
||||
{{- .Prepare "su_delay_times" | indent 4}}
|
||||
lea {{.BX}},[{{.Use "su_delay_times"}} + {{.BX}}*2] ; BX now points to the right position within delay time table
|
||||
{{- end}}
|
||||
movzx esi, word [{{.Stack "GlobalTick"}}] ; notice that we load word, so we wrap at 65536
|
||||
mov {{.CX}}, {{.PTRWORD}} [{{.Stack "DelayWorkSpace"}}] ; {{.WRK}} is now the separate delay workspace, as they require a lot more space
|
||||
{{- if .StereoAndMono "delay"}}
|
||||
@ -256,7 +257,7 @@ su_op_delay_mono: ; flow into mono delay
|
||||
call su_op_delay_do ; when stereo delay is not enabled, we could inline this to save 5 bytes, but I expect stereo delay to be farely popular so maybe not worth the hassle
|
||||
mov {{.PTRWORD}} [{{.Stack "DelayWorkSpace"}}],{{.CX}} ; move delay workspace pointer back to stack.
|
||||
{{- .PopRegs .VAL .COM | indent 4}}
|
||||
{{- if .UsesDelayModulation}}
|
||||
{{- if .SupportsModulation "delay" "delaytime"}}
|
||||
xor eax, eax
|
||||
mov dword [{{.Modulation "delay" "delaytime"}}], eax
|
||||
{{- end}}
|
||||
@ -281,9 +282,9 @@ su_op_delay_mono: ; flow into mono delay
|
||||
fxch ; y p*p*x
|
||||
fmul dword [{{.Input "delay" "dry"}}] ; dr*y p*p*x
|
||||
su_op_delay_loop:
|
||||
{{- if or .UsesDelayModulation (.HasParamValue "delay" "notetracking" 1)}} ; delaytime modulation or note syncing require computing the delay time in floats
|
||||
{{- if or (.SupportsModulation "delay" "delaytime") (.SupportsParamValue "delay" "notetracking" 1)}} ; delaytime modulation or note syncing require computing the delay time in floats
|
||||
fild word [{{.BX}}] ; k dr*y p*p*x, where k = delay time
|
||||
{{- if .HasParamValue "delay" "notetracking" 1}}
|
||||
{{- if .SupportsParamValue "delay" "notetracking" 1}}
|
||||
test ah, 1 ; note syncing is the least significant bit of ah, 0 = ON, 1 = OFF
|
||||
jne su_op_delay_skipnotesync
|
||||
fild dword [{{.INP}}-su_voice.inputs+su_voice.note]
|
||||
@ -293,7 +294,7 @@ su_op_delay_loop:
|
||||
fdivp st1, st0 ; use 10787 for delaytime to have neutral transpose
|
||||
su_op_delay_skipnotesync:
|
||||
{{- end}}
|
||||
{{- if .UsesDelayModulation}}
|
||||
{{- if .SupportsModulation "delay" "delaytime"}}
|
||||
fld dword [{{.Modulation "delay" "delaytime"}}]
|
||||
{{- .Float 32767.0 | .Prepare | indent 8}}
|
||||
fmul dword [{{.Float 32767.0 | .Use}}] ; scale it up, as the modulations would be too small otherwise
|
||||
@ -339,7 +340,7 @@ su_op_delay_loop:
|
||||
{{end}}
|
||||
|
||||
|
||||
{{- if .Opcode "compressor"}}
|
||||
{{- if .HasOp "compressor"}}
|
||||
;-------------------------------------------------------------------------------
|
||||
; COMPRES opcode: push compressor gain to stack
|
||||
;-------------------------------------------------------------------------------
|
||||
|
@ -1,4 +1,4 @@
|
||||
{{- if .Opcode "speed" -}}
|
||||
{{- if .HasOp "speed" -}}
|
||||
;-------------------------------------------------------------------------------
|
||||
; SPEED opcode: modulate the speed (bpm) of the song based on ST0
|
||||
;-------------------------------------------------------------------------------
|
||||
|
@ -1,3 +1,5 @@
|
||||
{{- if .SupportsParamValue "oscillator" "type" .Sample}}
|
||||
|
||||
{{- if eq .OS "windows"}}
|
||||
{{.ExportFunc "su_load_gmdls"}}
|
||||
{{- if .Amd64}}
|
||||
@ -24,7 +26,7 @@
|
||||
call ReadFile ; Readfile(handle,&su_sample_table,SAMPLE_TABLE_SIZE,&bytes_read,NULL)
|
||||
add rsp, 40 ; shadow space, as required by Win64 ABI
|
||||
ret
|
||||
{{- else}}
|
||||
{{else}}
|
||||
mov edx, su_sample_table
|
||||
mov ecx, su_gmdls_path1
|
||||
su_gmdls_pathloop:
|
||||
@ -50,9 +52,9 @@ extern _ReadFile@20 ; requires windows
|
||||
db 'drivers/gm.dls',0
|
||||
su_gmdls_path2:
|
||||
db 'drivers/etc/gm.dls',0
|
||||
{{end}}
|
||||
|
||||
{{.SectBss "susamtable"}}
|
||||
su_sample_table:
|
||||
resb 3440660 ; size of gmdls.
|
||||
|
||||
{{end}}
|
||||
|
@ -1,113 +1,10 @@
|
||||
; source file for compiling sointu as a library
|
||||
%define SU_DISABLE_PLAYER
|
||||
|
||||
%include "sointu/header.inc"
|
||||
|
||||
; use every opcode
|
||||
USE_ADD
|
||||
USE_ADDP
|
||||
USE_POP
|
||||
USE_LOADNOTE
|
||||
USE_MUL
|
||||
USE_MULP
|
||||
USE_PUSH
|
||||
USE_XCH
|
||||
USE_DISTORT
|
||||
USE_HOLD
|
||||
USE_CRUSH
|
||||
USE_GAIN
|
||||
USE_INVGAIN
|
||||
USE_FILTER
|
||||
USE_CLIP
|
||||
USE_PAN
|
||||
USE_DELAY
|
||||
USE_COMPRES
|
||||
USE_SPEED
|
||||
USE_OUT
|
||||
USE_OUTAUX
|
||||
USE_AUX
|
||||
USE_SEND
|
||||
USE_ENVELOPE
|
||||
USE_NOISE
|
||||
USE_OSCILLAT
|
||||
USE_LOAD_VAL
|
||||
USE_RECEIVE
|
||||
USE_IN
|
||||
|
||||
; include stereo variant of each opcode
|
||||
%define INCLUDE_STEREO_ADD
|
||||
%define INCLUDE_STEREO_ADDP
|
||||
%define INCLUDE_STEREO_POP
|
||||
%define INCLUDE_STEREO_LOADNOTE
|
||||
%define INCLUDE_STEREO_MUL
|
||||
%define INCLUDE_STEREO_MULP
|
||||
%define INCLUDE_STEREO_PUSH
|
||||
%define INCLUDE_STEREO_XCH
|
||||
%define INCLUDE_STEREO_DISTORT
|
||||
%define INCLUDE_STEREO_HOLD
|
||||
%define INCLUDE_STEREO_CRUSH
|
||||
%define INCLUDE_STEREO_GAIN
|
||||
%define INCLUDE_STEREO_INVGAIN
|
||||
%define INCLUDE_STEREO_FILTER
|
||||
%define INCLUDE_STEREO_CLIP
|
||||
%define INCLUDE_STEREO_PAN
|
||||
%define INCLUDE_STEREO_DELAY
|
||||
%define INCLUDE_STEREO_COMPRES
|
||||
%define INCLUDE_STEREO_SPEED
|
||||
%define INCLUDE_STEREO_OUT
|
||||
%define INCLUDE_STEREO_OUTAUX
|
||||
%define INCLUDE_STEREO_AUX
|
||||
%define INCLUDE_STEREO_SEND
|
||||
%define INCLUDE_STEREO_ENVELOPE
|
||||
%define INCLUDE_STEREO_NOISE
|
||||
%define INCLUDE_STEREO_OSCILLAT
|
||||
%define INCLUDE_STEREO_LOADVAL
|
||||
%define INCLUDE_STEREO_RECEIVE
|
||||
%define INCLUDE_STEREO_IN
|
||||
|
||||
; include all features inside all opcodes
|
||||
%define INCLUDE_TRISAW
|
||||
%define INCLUDE_SINE
|
||||
%define INCLUDE_PULSE
|
||||
%define INCLUDE_GATE
|
||||
%define INCLUDE_UNISONS
|
||||
%define INCLUDE_POLYPHONY
|
||||
%define INCLUDE_MULTIVOICE_TRACKS
|
||||
%define INCLUDE_DELAY_MODULATION
|
||||
%define INCLUDE_LOWPASS
|
||||
%define INCLUDE_BANDPASS
|
||||
%define INCLUDE_HIGHPASS
|
||||
%define INCLUDE_NEGBANDPASS
|
||||
%define INCLUDE_NEGHIGHPASS
|
||||
%define INCLUDE_GLOBAL_SEND
|
||||
%define INCLUDE_DELAY_NOTETRACKING
|
||||
%define INCLUDE_DELAY_FLOAT_TIME
|
||||
|
||||
%ifidn __OUTPUT_FORMAT__,win32
|
||||
%define INCLUDE_SAMPLES
|
||||
%define INCLUDE_GMDLS
|
||||
%endif
|
||||
%ifidn __OUTPUT_FORMAT__,win64
|
||||
%define INCLUDE_SAMPLES
|
||||
%define INCLUDE_GMDLS
|
||||
%endif
|
||||
|
||||
%include "sointu/footer.inc"
|
||||
|
||||
section .text
|
||||
|
||||
struc su_sampleoff
|
||||
.start resd 1
|
||||
.loopstart resw 1
|
||||
.looplength resw 1
|
||||
.size:
|
||||
endstruc
|
||||
{{template "structs.asm" .}}
|
||||
|
||||
struc su_synth
|
||||
.synthwrk resb su_synthworkspace.size
|
||||
.delaywrks resb su_delayline_wrk.size * 64
|
||||
.synth_wrk resb su_synthworkspace.size
|
||||
.delay_wrks resb su_delayline_wrk.size * 64
|
||||
.delaytimes resw 768
|
||||
.sampleoffs resb su_sampleoff.size * 256
|
||||
.sampleoffs resb su_sample_offset.size * 256
|
||||
.randseed resd 1
|
||||
.globaltime resd 1
|
||||
.commands resb 32 * 64
|
||||
@ -116,189 +13,122 @@ struc su_synth
|
||||
.numvoices resd 1
|
||||
endstruc
|
||||
|
||||
SECT_TEXT(sursampl)
|
||||
|
||||
EXPORT MANGLE_FUNC(su_render,16)
|
||||
%if BITS == 32 ; stdcall
|
||||
pushad ; push registers
|
||||
mov ecx, [esp + 4 + 32] ; ecx = &synthState
|
||||
mov edx, [esp + 8 + 32] ; edx = &buffer
|
||||
mov esi, [esp + 12 + 32] ; esi = &samples
|
||||
mov ebx, [esp + 16 + 32] ; ebx = &time
|
||||
%else
|
||||
%ifidn __OUTPUT_FORMAT__,win64 ; win64 ABI: rcx = &synth, rdx = &buffer, r8 = &bufsize, r9 = &time
|
||||
push_registers rdi, rsi, rbx, rbp ; win64 ABI: these registers are non-volatile
|
||||
mov rsi, r8 ; rsi = &samples
|
||||
mov rbx, r9 ; rbx = &time
|
||||
%else ; System V ABI: rdi = &synth, rsi = &buffer, rdx = &samples, rcx = &time
|
||||
push_registers rbx, rbp ; System V ABI: these registers are non-volatile
|
||||
mov rbx, rcx ; rbx points to time
|
||||
xchg rsi, rdx ; rdx points to buffer, rsi points to samples
|
||||
mov rcx, rdi ; rcx = &Synthstate
|
||||
%endif
|
||||
%endif
|
||||
sub _SP,108 ; allocate space on stack for the FPU state
|
||||
fsave [_SP] ; save the FPU state to stack & reset the FPU
|
||||
push _SI ; push the pointer to samples
|
||||
push _BX ; push the pointer to time
|
||||
{{.ExportFunc "su_render" "SynthStateParam" "BufferPtrParam" "SamplesParam" "TimeParam"}}
|
||||
{{- if .Amd64}}
|
||||
{{- if eq .OS "windows"}}
|
||||
{{- .PushRegs "rdi" "NonVolatileRDI" "rsi" "NonVolatileRSI" "rbx" "NonVolatileRBX" "rbp" "NonVolatileRBP" | indent 4}}
|
||||
mov rsi, r8 ; rsi = &samples
|
||||
mov rbx, r9 ; rbx = &time
|
||||
{{- else}} ; SystemV amd64 ABI, linux mac or hopefully something similar
|
||||
{{- .PushRegs "rbx" "NonVolatileRBX" "rbp" "NonVolatileRBP" | indent 4}}
|
||||
mov rbx, rcx ; rbx points to time
|
||||
xchg rsi, rdx ; rdx points to buffer, rsi points to samples
|
||||
mov rcx, rdi ; rcx = &Synthstate
|
||||
{{- end}}
|
||||
{{- else}}
|
||||
{{- .PushRegs | indent 4 }} ; push registers
|
||||
mov ecx, [{{.Stack "SynthStateParam"}}] ; ecx = &synthState
|
||||
mov edx, [{{.Stack "BufferPtrParam"}}] ; edx = &buffer
|
||||
mov esi, [{{.Stack "SamplesParam"}}] ; esi = &samples
|
||||
mov ebx, [{{.Stack "TimeParam"}}] ; ebx = &time
|
||||
{{- end}}
|
||||
{{.SaveFPUState | indent 4}} ; save the FPU state to stack & reset the FPU
|
||||
{{.Push .SI "Samples"}}
|
||||
{{.Push .BX "Time"}}
|
||||
xor eax, eax ; samplenumber starts at 0
|
||||
push _AX ; push samplenumber to stack
|
||||
mov esi, [_SI] ; zero extend dereferenced pointer
|
||||
push _SI ; push bufsize
|
||||
push _DX ; push bufptr
|
||||
push _CX ; this takes place of the voicetrack
|
||||
lea _AX, [_CX + su_synth.sampleoffs]
|
||||
push _AX
|
||||
lea _AX, [_CX + su_synth.delaytimes]
|
||||
push _AX
|
||||
mov eax, [_CX + su_synth.randseed]
|
||||
push _AX ; randseed
|
||||
mov eax, [_CX + su_synth.globaltime]
|
||||
push _AX ; global tick time
|
||||
mov ebx, dword [_BX] ; zero extend dereferenced pointer
|
||||
push _BX ; the nominal rowlength should be time_in
|
||||
{{.Push .AX "BufSample"}}
|
||||
mov esi, [{{.SI}}] ; zero extend dereferenced pointer
|
||||
{{.Push .SI "BufSize"}}
|
||||
{{.Push .DX "BufPtr"}}
|
||||
{{.Push .CX "SynthState"}}
|
||||
lea {{.AX}}, [{{.CX}} + su_synth.sampleoffs]
|
||||
{{.Push .AX "SampleTable"}}
|
||||
lea {{.AX}}, [{{.CX}} + su_synth.delaytimes]
|
||||
{{.Push .AX "DelayTable"}}
|
||||
mov eax, [{{.CX}} + su_synth.randseed]
|
||||
{{.Push .AX "RandSeed"}}
|
||||
mov eax, [{{.CX}} + su_synth.globaltime]
|
||||
{{.Push .AX "GlobalTick"}}
|
||||
mov ebx, dword [{{.BX}}] ; zero extend dereferenced pointer
|
||||
{{.Push .BX "RowLength"}} ; the nominal rowlength should be time_in
|
||||
xor eax, eax ; rowtick starts at 0
|
||||
su_render_samples_loop:
|
||||
push _DI
|
||||
fnstsw [_SP] ; store the FPU status flag to stack top
|
||||
pop _DI ; _DI = FPU status flag
|
||||
and _DI, 0011100001000101b ; mask TOP pointer, stack error, zero divide and invalid operation
|
||||
test _DI,_DI ; all the aforementioned bits should be 0!
|
||||
push {{.DI}}
|
||||
fnstsw [{{.SP}}] ; store the FPU status flag to stack top
|
||||
pop {{.DI}} ; {{.DI}} = FPU status flag
|
||||
and {{.DI}}, 0b0011100001000101 ; mask TOP pointer, stack error, zero divide and in{{.VAL}}id operation
|
||||
test {{.DI}},{{.DI}} ; all the aforementioned bits should be 0!
|
||||
jne su_render_samples_time_finish ; otherwise, we exit due to error
|
||||
cmp eax, [_SP] ; if rowtick >= maxtime
|
||||
cmp eax, [{{.Stack "RowLength"}}] ; if rowtick >= maxtime
|
||||
jge su_render_samples_time_finish ; goto finish
|
||||
mov ecx, [_SP + PTRSIZE*7] ; ecx = buffer length in samples
|
||||
cmp [_SP + PTRSIZE*8], ecx ; if samples >= maxsamples
|
||||
mov ecx, [{{.Stack "BufSize"}}] ; ecx = buffer length in samples
|
||||
cmp [{{.Stack "BufSample"}}], ecx ; if samples >= maxsamples
|
||||
jge su_render_samples_time_finish ; goto finish
|
||||
inc eax ; time++
|
||||
inc dword [_SP + PTRSIZE*8] ; samples++
|
||||
mov _CX, [_SP + PTRSIZE*5]
|
||||
push _AX ; push rowtick
|
||||
mov eax, [_CX + su_synth.polyphony]
|
||||
push _AX ;polyphony
|
||||
mov eax, [_CX + su_synth.numvoices]
|
||||
push _AX ;numvoices
|
||||
lea _DX, [_CX+ su_synth.synthwrk]
|
||||
lea COM, [_CX+ su_synth.commands]
|
||||
lea VAL, [_CX+ su_synth.values]
|
||||
lea WRK, [_DX + su_synthworkspace.voices]
|
||||
lea _CX, [_CX+ su_synth.delaywrks - su_delayline_wrk.filtstate]
|
||||
call MANGLE_FUNC(su_run_vm,0)
|
||||
pop _AX
|
||||
pop _AX
|
||||
mov _DI, [_SP + PTRSIZE*7] ; edi containts buffer ptr
|
||||
mov _CX, [_SP + PTRSIZE*6]
|
||||
lea _SI, [_CX + su_synth.synthwrk + su_synthworkspace.left]
|
||||
inc dword [{{.Stack "BufSample"}}] ; samples++
|
||||
mov {{.CX}}, [{{.Stack "SynthState"}}]
|
||||
{{.Push .AX "Sample"}}
|
||||
mov eax, [{{.CX}} + su_synth.polyphony]
|
||||
{{.Push .AX "PolyphonyBitmask"}}
|
||||
mov eax, [{{.CX}} + su_synth.numvoices]
|
||||
{{.Push .AX "VoicesRemain"}}
|
||||
lea {{.DX}}, [{{.CX}}+ su_synth.synth_wrk]
|
||||
lea {{.COM}}, [{{.CX}}+ su_synth.commands]
|
||||
lea {{.VAL}}, [{{.CX}}+ su_synth.values]
|
||||
lea {{.WRK}}, [{{.DX}} + su_synthworkspace.voices]
|
||||
lea {{.CX}}, [{{.CX}}+ su_synth.delay_wrks - su_delayline_wrk.filtstate]
|
||||
{{.Call "su_run_vm"}}
|
||||
{{.Pop .AX}}
|
||||
{{.Pop .AX}}
|
||||
mov {{.DI}}, [{{.Stack "BufPtr"}}] ; edi containts buffer ptr
|
||||
mov {{.CX}}, [{{.Stack "SynthState"}}]
|
||||
lea {{.SI}}, [{{.CX}} + su_synth.synth_wrk + su_synthworkspace.left]
|
||||
movsd ; copy left channel to output buffer
|
||||
movsd ; copy right channel to output buffer
|
||||
mov [_SP + PTRSIZE*7], _DI ; save back the updated ptr
|
||||
lea _DI, [_SI-8]
|
||||
mov [{{.Stack "BufPtr"}}], {{.DI}} ; save back the updated ptr
|
||||
lea {{.DI}}, [{{.SI}}-8]
|
||||
xor eax, eax
|
||||
stosd ; clear left channel so the VM is ready to write them again
|
||||
stosd ; clear right channel so the VM is ready to write them again
|
||||
pop _AX
|
||||
inc dword [_SP + PTRSIZE] ; increment global time, used by delays
|
||||
{{.Pop .AX}}
|
||||
inc dword [{{.Stack "GlobalTick"}}] ; increment global time, used by delays
|
||||
jmp su_render_samples_loop
|
||||
su_render_samples_time_finish:
|
||||
pop _CX
|
||||
pop _BX
|
||||
pop _DX
|
||||
pop _CX ; discard delaytimes ptr
|
||||
pop _CX ; discard samplesoffs ptr
|
||||
pop _CX
|
||||
mov [_CX + su_synth.randseed], edx
|
||||
mov [_CX + su_synth.globaltime], ebx
|
||||
pop _BX
|
||||
pop _BX
|
||||
pop _DX
|
||||
pop _BX ; pop the pointer to time
|
||||
pop _SI ; pop the pointer to samples
|
||||
mov dword [_SI], edx ; *samples = samples rendered
|
||||
mov dword [_BX], eax ; *time = time ticks rendered
|
||||
mov _AX,_DI ; _DI was the masked FPU status flag, _AX is return value
|
||||
frstor [_SP] ; restore fpu state
|
||||
add _SP,108 ; rewind the stack allocate for FPU state
|
||||
%if BITS == 32 ; stdcall
|
||||
mov [_SP + 28],eax ; we want to return eax, but popad pops everything, so put eax to stack for popad to pop
|
||||
popad
|
||||
ret 16
|
||||
%else
|
||||
%ifidn __OUTPUT_FORMAT__,win64
|
||||
pop_registers rdi, rsi, rbx, rbp ; win64 ABI: these registers are non-volatile
|
||||
%else
|
||||
pop_registers rbx, rbp ; System V ABI: these registers are non-volatile
|
||||
%endif
|
||||
{{.Pop .CX}}
|
||||
{{.Pop .BX}}
|
||||
{{.Pop .DX}}
|
||||
{{.Pop .CX}}
|
||||
{{.Pop .CX}}
|
||||
{{.Pop .CX}}
|
||||
mov [{{.CX}} + su_synth.randseed], edx
|
||||
mov [{{.CX}} + su_synth.globaltime], ebx
|
||||
{{.Pop .BX}}
|
||||
{{.Pop .BX}}
|
||||
{{.Pop .DX}}
|
||||
{{.Pop .BX}}
|
||||
{{.Pop .SI}}
|
||||
mov dword [{{.SI}}], edx ; *samples = samples rendered
|
||||
mov dword [{{.BX}}], eax ; *time = time ticks rendered
|
||||
mov {{.AX}},{{.DI}} ; {{.DI}} was the masked FPU status flag, {{.AX}} is return {{.VAL}}ue
|
||||
{{.LoadFPUState | indent 4}} ; load the FPU state from stack
|
||||
{{- if .Amd64}}
|
||||
{{- if eq .OS "windows"}}
|
||||
{{- .PopRegs "rdi" "rsi" "rbx" "rbp" | indent 4}}
|
||||
{{- else}} ; SystemV amd64 ABI, linux mac or hopefully something similar
|
||||
{{- .PopRegs "rbx" "rbp" | indent 4}}
|
||||
{{- end}}
|
||||
ret
|
||||
%endif
|
||||
{{- else}}
|
||||
mov [{{.Stack "eax"}}],eax ; we want to return eax, but popad pops everything, so put eax to stack for popad to pop
|
||||
{{- .PopRegs | indent 4 }} ; popad
|
||||
ret 16
|
||||
{{- end}}
|
||||
|
||||
SECT_DATA(opcodeid)
|
||||
|
||||
; Arithmetic opcode ids
|
||||
EXPORT MANGLE_DATA(su_add_id)
|
||||
dd ADD_ID
|
||||
EXPORT MANGLE_DATA(su_addp_id)
|
||||
dd ADDP_ID
|
||||
EXPORT MANGLE_DATA(su_pop_id)
|
||||
dd POP_ID
|
||||
EXPORT MANGLE_DATA(su_loadnote_id)
|
||||
dd LOADNOTE_ID
|
||||
EXPORT MANGLE_DATA(su_mul_id)
|
||||
dd MUL_ID
|
||||
EXPORT MANGLE_DATA(su_mulp_id)
|
||||
dd MULP_ID
|
||||
EXPORT MANGLE_DATA(su_push_id)
|
||||
dd PUSH_ID
|
||||
EXPORT MANGLE_DATA(su_xch_id)
|
||||
dd XCH_ID
|
||||
{{template "patch.asm" .}}
|
||||
|
||||
; Effect opcode ids
|
||||
EXPORT MANGLE_DATA(su_distort_id)
|
||||
dd DISTORT_ID
|
||||
EXPORT MANGLE_DATA(su_hold_id)
|
||||
dd HOLD_ID
|
||||
EXPORT MANGLE_DATA(su_crush_id)
|
||||
dd CRUSH_ID
|
||||
EXPORT MANGLE_DATA(su_gain_id)
|
||||
dd GAIN_ID
|
||||
EXPORT MANGLE_DATA(su_invgain_id)
|
||||
dd INVGAIN_ID
|
||||
EXPORT MANGLE_DATA(su_filter_id)
|
||||
dd FILTER_ID
|
||||
EXPORT MANGLE_DATA(su_clip_id)
|
||||
dd CLIP_ID
|
||||
EXPORT MANGLE_DATA(su_pan_id)
|
||||
dd PAN_ID
|
||||
EXPORT MANGLE_DATA(su_delay_id)
|
||||
dd DELAY_ID
|
||||
EXPORT MANGLE_DATA(su_compres_id)
|
||||
dd COMPRES_ID
|
||||
|
||||
; Flowcontrol opcode ids
|
||||
EXPORT MANGLE_DATA(su_advance_id)
|
||||
dd SU_ADVANCE_ID
|
||||
EXPORT MANGLE_DATA(su_speed_id)
|
||||
dd SPEED_ID
|
||||
|
||||
; Sink opcode ids
|
||||
EXPORT MANGLE_DATA(su_out_id)
|
||||
dd OUT_ID
|
||||
EXPORT MANGLE_DATA(su_outaux_id)
|
||||
dd OUTAUX_ID
|
||||
EXPORT MANGLE_DATA(su_aux_id)
|
||||
dd AUX_ID
|
||||
EXPORT MANGLE_DATA(su_send_id)
|
||||
dd SEND_ID
|
||||
|
||||
; Source opcode ids
|
||||
EXPORT MANGLE_DATA(su_envelope_id)
|
||||
dd ENVELOPE_ID
|
||||
EXPORT MANGLE_DATA(su_noise_id)
|
||||
dd NOISE_ID
|
||||
EXPORT MANGLE_DATA(su_oscillat_id)
|
||||
dd OSCILLAT_ID
|
||||
EXPORT MANGLE_DATA(su_loadval_id)
|
||||
dd LOADVAL_ID
|
||||
EXPORT MANGLE_DATA(su_receive_id)
|
||||
dd RECEIVE_ID
|
||||
EXPORT MANGLE_DATA(su_in_id)
|
||||
dd IN_ID
|
||||
;-------------------------------------------------------------------------------
|
||||
; Constants
|
||||
;-------------------------------------------------------------------------------
|
||||
{{.SectData "constants"}}
|
||||
{{.Constants}}
|
||||
|
100
templates/library.h
Normal file
100
templates/library.h
Normal file
@ -0,0 +1,100 @@
|
||||
#ifndef _SOINTU_H
|
||||
#define _SOINTU_H
|
||||
|
||||
#pragma pack(push,1) // this should be fine for both Go and assembly
|
||||
typedef struct Unit {
|
||||
float State[8];
|
||||
float Ports[8];
|
||||
} Unit;
|
||||
|
||||
typedef struct Voice {
|
||||
int Note;
|
||||
int Release;
|
||||
float Inputs[8];
|
||||
float Reserved[6];
|
||||
struct Unit Units[63];
|
||||
} Voice;
|
||||
|
||||
typedef struct DelayWorkspace {
|
||||
float Buffer[65536];
|
||||
float Dcin;
|
||||
float Dcout;
|
||||
float Filtstate;
|
||||
} DelayWorkspace;
|
||||
|
||||
typedef struct SynthWorkspace {
|
||||
unsigned char Curvoices[32];
|
||||
float Left;
|
||||
float Right;
|
||||
float Aux[6];
|
||||
struct Voice Voices[32];
|
||||
} SynthWorkspace;
|
||||
|
||||
typedef struct SampleOffset {
|
||||
unsigned int Start;
|
||||
unsigned short LoopStart;
|
||||
unsigned short LoopLength;
|
||||
} SampleOffset;
|
||||
|
||||
typedef struct Synth {
|
||||
struct SynthWorkspace SynthWrk;
|
||||
struct DelayWorkspace DelayWrks[64]; // let's keep this as 64 for now, so the delays take 16 meg. If that's too little or too much, we can change this in future.
|
||||
unsigned short DelayTimes[768];
|
||||
struct SampleOffset SampleOffsets[256];
|
||||
unsigned int RandSeed;
|
||||
unsigned int GlobalTick;
|
||||
unsigned char Commands[32 * 64];
|
||||
unsigned char Values[32 * 64 * 8];
|
||||
unsigned int Polyphony;
|
||||
unsigned int NumVoices;
|
||||
} Synth;
|
||||
#pragma pack(pop)
|
||||
|
||||
#if UINTPTR_MAX == 0xffffffff // are we 32-bit?
|
||||
#if defined(__clang__) || defined(__GNUC__)
|
||||
#define CALLCONV __attribute__ ((stdcall))
|
||||
#elif defined(_WIN32)
|
||||
#define CALLCONV __stdcall // on 32-bit platforms, we just use stdcall, as all know it
|
||||
#endif
|
||||
#else // 64-bit
|
||||
#define CALLCONV // the asm will use honor honor correct x64 ABI on all 64-bit platforms
|
||||
#endif
|
||||
|
||||
void CALLCONV su_load_gmdls(void);
|
||||
|
||||
// int su_render(Synth* synth, float* buffer, int* samples, int* time):
|
||||
// Renders samples until 'samples' number of samples are reached or 'time' number of
|
||||
// modulated time ticks are reached, whichever happens first. 'samples' and 'time' are
|
||||
// are passed by reference as the function modifies to tell how many samples were
|
||||
// actually rendered and how many time ticks were actually advanced.
|
||||
//
|
||||
// Parameters:
|
||||
// synth pointer to the synthesizer used. RandSeed should be > 0 e.g. 1
|
||||
// buffer audio sample buffer, L R L R ...
|
||||
// samples pointer to the maximum number of samples to be rendered.
|
||||
// buffer should have a length of 2 * maxsamples as the audio
|
||||
// is stereo.
|
||||
// time maximum modulated time rendered.
|
||||
//
|
||||
// The value referred by samples is changed to contain the actual number of samples rendered
|
||||
// Similarly, the value referred by time is changed to contain the number of time ticks advanced.
|
||||
// If samples_out == samples_in, then is must be that time_in <= time_out.
|
||||
// If samples_out < samples_in, then time_out >= time_in. Note that it could happen that
|
||||
// time_out > time_in, as it is modulated and the time could advance by 2 or more, so the loop
|
||||
// exit condition would fire when the current time is already past time_in
|
||||
//
|
||||
// Returns an error code, which is actually just masked version of the FPU Status Word
|
||||
// On a succesful run, the return value should be 0
|
||||
// Error code bits:
|
||||
// bit 0 FPU invalid operation (stack over/underflow OR invalid arithmetic e.g. NaNs)
|
||||
// bit 2 Divide by zero occurred
|
||||
// bit 6 Stack overflow or underflow occurred
|
||||
// bits 11-13 The top pointer of the fpu stack. Any other value than 0 indicates that some values were left on the stack.
|
||||
int CALLCONV su_render(Synth* synth, float* buffer, int* samples, int* time);
|
||||
|
||||
#define SU_ADVANCE_ID 0
|
||||
{{- range $index, $element := .Instructions}}
|
||||
#define {{printf "su_%v_id" $element | upper | printf "%-20v"}}{{add1 $index | mul 2}}
|
||||
{{- end}}
|
||||
|
||||
#endif // _SOINTU_H
|
@ -25,8 +25,8 @@ su_run_vm_loop: ; loop until all voices done
|
||||
xor ecx, ecx ; counter = 0
|
||||
xor eax, eax ; clear out high bits of eax, as lodsb only sets al
|
||||
su_transform_values_loop:
|
||||
{{- .Prepare "su_vm_transformcounts" | indent 4}}
|
||||
cmp cl, byte [{{.Use "su_vm_transformcounts"}}+{{.DI}}] ; compare the counter to the value in the param count table
|
||||
{{- .Prepare "su_vm_transformcounts-1" | indent 4}}
|
||||
cmp cl, byte [{{.Use "su_vm_transformcounts-1"}}+{{.DI}}] ; compare the counter to the value in the param count table
|
||||
je su_transform_values_out
|
||||
lodsb ; load the byte value from VAL stream
|
||||
push {{.AX}} ; push it to memory so FPU can read it
|
||||
@ -43,11 +43,12 @@ su_transform_values_loop:
|
||||
su_transform_values_out:
|
||||
bt dword [{{.COM}}-1],0 ; LSB of COM = stereo bit => carry
|
||||
{{- .SaveStack "Opcode"}}
|
||||
{{- .Prepare "su_vm_jumptable" | indent 4}}
|
||||
call [{{.Use "su_vm_jumptable"}}+{{.DI}}*{{.PTRSIZE}}] ; call the function corresponding to the instruction
|
||||
{{- $x := printf "su_vm_jumptable-%v" .PTRSIZE}}
|
||||
{{- .Prepare $x | indent 4}}
|
||||
call [{{.Use $x}}+{{.DI}}*{{.PTRSIZE}}] ; call the function corresponding to the instruction
|
||||
jmp su_run_vm_loop
|
||||
su_run_vm_advance:
|
||||
{{- if .Polyphony}}
|
||||
{{- if .SupportsPolyphony}}
|
||||
mov {{.WRK}}, [{{.Stack "Voice"}}] ; WRK points to start of current voice
|
||||
add {{.WRK}}, su_voice.size ; move to next voice
|
||||
mov [{{.Stack "Voice"}}], {{.WRK}} ; update the pointer in the stack to point to the new voice
|
||||
@ -173,18 +174,15 @@ su_clip_do:
|
||||
; The opcode table jump table. This is constructed to only include the opcodes
|
||||
; that are used so that the jump table is as small as possible.
|
||||
;-------------------------------------------------------------------------------
|
||||
{{.Data "su_vm_jumptable_offset"}}
|
||||
su_vm_jumptable equ $ - {{.PTRSIZE}} ; Advance is not in the opcode table
|
||||
{{- $x := .}}
|
||||
{{- range .Opcodes}}
|
||||
{{$x.DPTR}} su_op_{{.Type}}
|
||||
{{.Data "su_vm_jumptable"}}
|
||||
{{- range .Instructions}}
|
||||
{{$.DPTR}} su_op_{{.}}
|
||||
{{- end}}
|
||||
|
||||
;-------------------------------------------------------------------------------
|
||||
; The number of transformed parameters each opcode takes
|
||||
;-------------------------------------------------------------------------------
|
||||
{{.Data "su_vm_transformcounts_offset"}}
|
||||
su_vm_transformcounts equ $ - 1 ; Advance is not in the opcode table
|
||||
{{- range .Opcodes}}
|
||||
db {{.NumParams}}
|
||||
{{.Data "su_vm_transformcounts"}}
|
||||
{{- range .Instructions}}
|
||||
db {{$.TransformCount .}}
|
||||
{{- end}}
|
||||
|
@ -26,7 +26,7 @@ su_synth_obj:
|
||||
{{- end}}
|
||||
{{- $prologsize := len .Stacklocs}}
|
||||
xor eax, eax
|
||||
{{- if .MultivoiceTracks}}
|
||||
{{- if ne .VoiceTrackBitmask 0}}
|
||||
{{.Push (.VoiceTrackBitmask | printf "%v") "VoiceTrackBitmask"}}
|
||||
{{- end}}
|
||||
{{.Push "1" "RandSeed"}}
|
||||
@ -37,20 +37,20 @@ su_render_rowloop: ; loop through every row in the song
|
||||
xor eax, eax ; ecx is the current sample within row
|
||||
su_render_sampleloop: ; loop through every sample in the row
|
||||
{{.Push .AX "Sample"}}
|
||||
{{- if .Polyphony}}
|
||||
{{- if .SupportsPolyphony}}
|
||||
{{.Push (.PolyphonyBitmask | printf "%v") "PolyphonyBitmask"}} ; does the next voice reuse the current opcodes?
|
||||
{{- end}}
|
||||
{{.Push (.Song.Patch.TotalVoices | printf "%v") "VoicesRemain"}}
|
||||
mov {{.DX}}, {{.PTRWORD}} su_synth_obj ; {{.DX}} points to the synth object
|
||||
mov {{.COM}}, {{.PTRWORD}} su_patch_code ; COM points to vm code
|
||||
mov {{.VAL}}, {{.PTRWORD}} su_patch_parameters ; VAL points to unit params
|
||||
{{- if .Opcode "delay"}}
|
||||
{{- if .HasOp "delay"}}
|
||||
lea {{.CX}}, [{{.DX}} + su_synthworkspace.size - su_delayline_wrk.filtstate]
|
||||
{{- end}}
|
||||
lea {{.WRK}}, [{{.DX}} + su_synthworkspace.voices] ; WRK points to the first voice
|
||||
{{.Call "su_run_vm"}} ; run through the VM code
|
||||
{{.Pop .AX}}
|
||||
{{- if .Polyphony}}
|
||||
{{- if .SupportsPolyphony}}
|
||||
{{.Pop .AX}}
|
||||
{{- end}}
|
||||
{{- template "output_sound.asm" .}} ; *ptr++ = left, *ptr++ = right
|
||||
@ -64,9 +64,8 @@ su_render_sampleloop: ; loop through every sample in the row
|
||||
cmp eax, {{.Song.TotalRows}}
|
||||
jl su_render_rowloop
|
||||
; rewind the stack the entropy of multiple pop {{.AX}} is probably lower than add
|
||||
{{- $x := .}}
|
||||
{{- range (.Sub (len .Stacklocs) $prologsize | .Count)}}
|
||||
{{$x.Pop $x.AX}}
|
||||
{{- range slice .Stacklocs $prologsize}}
|
||||
{{$.Pop $.AX}}
|
||||
{{- end}}
|
||||
{{- if .Amd64}}
|
||||
{{- if eq .OS "windows"}}
|
||||
@ -89,7 +88,7 @@ su_render_sampleloop: ; loop through every sample in the row
|
||||
; Dirty: pretty much everything
|
||||
;-------------------------------------------------------------------------------
|
||||
{{.Func "su_update_voices"}}
|
||||
{{- if .MultivoiceTracks}}
|
||||
{{- if ne .VoiceTrackBitmask 0}}
|
||||
; The more complicated implementation: one track can trigger multiple voices
|
||||
xor edx, edx
|
||||
mov ebx, {{.Song.PatternRows}} ; we could do xor ebx,ebx; mov bl,PATTERN_SIZE, but that would limit patternsize to 256...
|
||||
@ -198,31 +197,31 @@ su_update_voices_skipadd:
|
||||
db {{.Sequence | toStrings | join ","}}
|
||||
{{- end}}
|
||||
|
||||
{{- if gt (.Song.Patch.SampleOffsets | len) 0}}
|
||||
{{- if gt (.SampleOffsets | len) 0}}
|
||||
;-------------------------------------------------------------------------------
|
||||
; Sample offsets
|
||||
;-------------------------------------------------------------------------------
|
||||
{{.Data "su_sample_offsets"}}
|
||||
{{- range .Song.Patch.SampleOffsets}}
|
||||
{{- range .SampleOffsets}}
|
||||
dd {{.Start}}
|
||||
dw {{.LoopStart}}
|
||||
dw {{.LoopLength}}
|
||||
{{- end}}
|
||||
{{end}}
|
||||
|
||||
{{- if gt (.Song.Patch.DelayTimes | len ) 0}}
|
||||
{{- if gt (.DelayTimes | len ) 0}}
|
||||
;-------------------------------------------------------------------------------
|
||||
; Delay times
|
||||
;-------------------------------------------------------------------------------
|
||||
{{.Data "su_delay_times"}}
|
||||
dw {{.Song.Patch.DelayTimes | toStrings | join ","}}
|
||||
dw {{.DelayTimes | toStrings | join ","}}
|
||||
{{end}}
|
||||
|
||||
;-------------------------------------------------------------------------------
|
||||
; The code for this patch, basically indices to vm jump table
|
||||
;-------------------------------------------------------------------------------
|
||||
{{.Data "su_patch_code"}}
|
||||
db {{.Code | toStrings | join ","}}
|
||||
db {{.Commands | toStrings | join ","}}
|
||||
|
||||
;-------------------------------------------------------------------------------
|
||||
; The parameters / inputs to each opcode
|
||||
|
50
templates/player.h
Normal file
50
templates/player.h
Normal file
@ -0,0 +1,50 @@
|
||||
// auto-generated by Sointu, editing not recommended
|
||||
#ifndef SU_RENDER_H
|
||||
#define SU_RENDER_H
|
||||
|
||||
#define SU_MAX_SAMPLES {{.MaxSamples}}
|
||||
#define SU_BUFFER_LENGTH (SU_MAX_SAMPLES*2)
|
||||
|
||||
#define SU_SAMPLE_RATE 44100
|
||||
#define SU_BPM {{.Song.BPM}}
|
||||
#define SU_PATTERN_SIZE {{.Song.PatternRows}}
|
||||
#define SU_MAX_PATTERNS {{.Song.SequenceLength}}
|
||||
#define SU_TOTAL_ROWS (SU_MAX_PATTERNS*SU_PATTERN_SIZE)
|
||||
#define SU_SAMPLES_PER_ROW (SU_SAMPLE_RATE*4*60/(SU_BPM*16))
|
||||
|
||||
#include <stdint.h>
|
||||
#if UINTPTR_MAX == 0xffffffff
|
||||
#if defined(__clang__) || defined(__GNUC__)
|
||||
#define SU_CALLCONV __attribute__ ((stdcall))
|
||||
#elif defined(_WIN32)
|
||||
#define SU_CALLCONV __stdcall
|
||||
#endif
|
||||
#else
|
||||
#define SU_CALLCONV
|
||||
#endif
|
||||
|
||||
{{- if .Output16Bit}}
|
||||
typedef short SUsample;
|
||||
#define SU_SAMPLE_RANGE 32767.0
|
||||
{{- else}}
|
||||
typedef float SUsample;
|
||||
#define SU_SAMPLE_RANGE 1.0
|
||||
{{- end}}
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
void SU_CALLCONV su_render_song(SUsample *buffer);
|
||||
{{- if gt (.SampleOffsets | len) 0}}
|
||||
void SU_CALLCONV su_load_gmdls();
|
||||
#define SU_LOAD_GMDLS
|
||||
{{- end}}
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
@ -1,4 +1,4 @@
|
||||
{{- if .Opcode "out"}}
|
||||
{{- if .HasOp "out"}}
|
||||
;-------------------------------------------------------------------------------
|
||||
; OUT opcode: outputs and pops the signal
|
||||
;-------------------------------------------------------------------------------
|
||||
@ -26,7 +26,7 @@ su_op_out_mono:
|
||||
{{end}}
|
||||
|
||||
|
||||
{{- if .Opcode "outaux"}}
|
||||
{{- if .HasOp "outaux"}}
|
||||
;-------------------------------------------------------------------------------
|
||||
; OUTAUX opcode: outputs to main and aux1 outputs and pops the signal
|
||||
;-------------------------------------------------------------------------------
|
||||
@ -54,7 +54,7 @@ su_op_outaux_mono:
|
||||
{{end}}
|
||||
|
||||
|
||||
{{- if .Opcode "aux"}}
|
||||
{{- if .HasOp "aux"}}
|
||||
;-------------------------------------------------------------------------------
|
||||
; AUX opcode: outputs the signal to aux (or main) port and pops the signal
|
||||
;-------------------------------------------------------------------------------
|
||||
@ -79,7 +79,7 @@ su_op_aux_mono:
|
||||
{{end}}
|
||||
|
||||
|
||||
{{- if .Opcode "send"}}
|
||||
{{- if .HasOp "send"}}
|
||||
;-------------------------------------------------------------------------------
|
||||
; SEND opcode: adds the signal to a port
|
||||
;-------------------------------------------------------------------------------
|
||||
@ -103,7 +103,7 @@ su_op_aux_mono:
|
||||
fxch ; swap them back: l r
|
||||
su_op_send_mono:
|
||||
{{- end}}
|
||||
{{- if .HasParamValueOtherThan "send" "voice" 0}}
|
||||
{{- if .SupportsParamValueOtherThan "send" "voice" 0}}
|
||||
test {{.AX}}, 0x8000
|
||||
jz su_op_send_skipglobal
|
||||
mov {{.CX}}, [{{.Stack "Synth"}}]
|
||||
|
@ -1,4 +1,4 @@
|
||||
{{if .Opcode "envelope" -}}
|
||||
{{if .HasOp "envelope" -}}
|
||||
;-------------------------------------------------------------------------------
|
||||
; ENVELOPE opcode: pushes an ADSR envelope value on stack [0,1]
|
||||
;-------------------------------------------------------------------------------
|
||||
@ -62,7 +62,7 @@ su_op_envelope_leave2:
|
||||
{{end}}
|
||||
|
||||
|
||||
{{- if .Opcode "noise"}}
|
||||
{{- if .HasOp "noise"}}
|
||||
;-------------------------------------------------------------------------------
|
||||
; NOISE opcode: creates noise
|
||||
;-------------------------------------------------------------------------------
|
||||
@ -91,7 +91,7 @@ su_op_noise_mono:
|
||||
{{end}}
|
||||
|
||||
|
||||
{{- if .Opcode "oscillator"}}
|
||||
{{- if .HasOp "oscillator"}}
|
||||
;-------------------------------------------------------------------------------
|
||||
; OSCILLAT opcode: oscillator, the heart of the synth
|
||||
;-------------------------------------------------------------------------------
|
||||
@ -100,11 +100,10 @@ su_op_noise_mono:
|
||||
;-------------------------------------------------------------------------------
|
||||
{{.Func "su_op_oscillator" "Opcode"}}
|
||||
lodsb ; load the flags
|
||||
%ifdef RUNTIME_TABLES
|
||||
%ifdef INCLUDE_SAMPLES
|
||||
mov {{.DI}}, [{{.SP}} + su_stack.sampleoffs]; we need to put this in a register, as the stereo & unisons screw the stack positions
|
||||
%endif ; ain't we lucky that {{.DI}} was unused throughout
|
||||
%endif
|
||||
{{- if .Library}}
|
||||
mov {{.DI}}, [{{.Stack "SampleTable"}}]; we need to put this in a register, as the stereo & unisons screw the stack positions
|
||||
; ain't we lucky that {{.DI}} was unused throughout
|
||||
{{- end}}
|
||||
fld dword [{{.Input "oscillator" "detune"}}] ; e, where e is the detune [0,1]
|
||||
{{- .Prepare (.Float 0.5)}}
|
||||
fsub dword [{{.Use (.Float 0.5)}}] ; e-.5
|
||||
@ -120,7 +119,7 @@ su_op_noise_mono:
|
||||
fchs ; -d r, negate the detune for second round
|
||||
su_op_oscillat_mono:
|
||||
{{- end}}
|
||||
{{- if .HasParamValueOtherThan "oscillator" "unison" 0}}
|
||||
{{- if .SupportsParamValueOtherThan "oscillator" "unison" 0}}
|
||||
{{.PushRegs .AX "" .WRK "OscWRK" .AX "OscFlags"}}
|
||||
fldz ; 0 d
|
||||
fxch ; d a=0, "accumulated signal"
|
||||
@ -171,7 +170,7 @@ su_op_oscillat_normalized:
|
||||
fadd dword [{{.WRK}}]
|
||||
fst dword [{{.WRK}}]
|
||||
fadd dword [{{.Input "oscillator" "phase"}}]
|
||||
{{- if .HasParamValue "oscillator" "type" .Sample}}
|
||||
{{- if .SupportsParamValue "oscillator" "type" .Sample}}
|
||||
test al, byte 0x80
|
||||
jz short su_op_oscillat_not_sample
|
||||
{{.Call "su_oscillat_sample"}}
|
||||
@ -185,25 +184,25 @@ su_op_oscillat_not_sample:
|
||||
fstp st1
|
||||
fld dword [{{.Input "oscillator" "color"}}] ; // c p
|
||||
; every oscillator test included if needed
|
||||
{{- if .HasParamValue "oscillator" "type" .Sine}}
|
||||
{{- if .SupportsParamValue "oscillator" "type" .Sine}}
|
||||
test al, byte 0x40
|
||||
jz short su_op_oscillat_notsine
|
||||
{{.Call "su_oscillat_sine"}}
|
||||
su_op_oscillat_notsine:
|
||||
{{- end}}
|
||||
{{- if .HasParamValue "oscillator" "type" .Trisaw}}
|
||||
{{- if .SupportsParamValue "oscillator" "type" .Trisaw}}
|
||||
test al, byte 0x20
|
||||
jz short su_op_oscillat_not_trisaw
|
||||
{{.Call "su_oscillat_trisaw"}}
|
||||
su_op_oscillat_not_trisaw:
|
||||
{{- end}}
|
||||
{{- if .HasParamValue "oscillator" "type" .Pulse}}
|
||||
{{- if .SupportsParamValue "oscillator" "type" .Pulse}}
|
||||
test al, byte 0x10
|
||||
jz short su_op_oscillat_not_pulse
|
||||
{{.Call "su_oscillat_pulse"}}
|
||||
su_op_oscillat_not_pulse:
|
||||
{{- end}}
|
||||
{{- if .HasParamValue "oscillator" "type" .Gate}}
|
||||
{{- if .SupportsParamValue "oscillator" "type" .Gate}}
|
||||
test al, byte 0x04
|
||||
jz short su_op_oscillat_not_gate
|
||||
{{.Call "su_oscillat_gate"}}
|
||||
@ -300,12 +299,12 @@ go4kVCO_gate_bit: ; stack: 0/1, let's call it x
|
||||
{{- .PushRegs .AX "SampleAx" .DX "SampleDx" .CX "SampleCx" .BX "SampleBx" | indent 4}} ; edx must be saved, eax & ecx if this is stereo osc
|
||||
push {{.AX}}
|
||||
mov al, byte [{{.VAL}}-4] ; reuse "color" as the sample number
|
||||
%ifdef RUNTIME_TABLES ; when using RUNTIME_TABLES, assumed the sample_offset ptr is in {{.DI}}
|
||||
do{lea {{.DI}}, [}, {{.DI}}, {{.AX}}*8,] ; edi points now to the sample table entry
|
||||
%else
|
||||
{{- if .Library}}
|
||||
lea {{.DI}}, [{{.DI}} + {{.AX}}*8] ; edi points now to the sample table entry
|
||||
{{- else}}
|
||||
{{- .Prepare "su_sample_offsets" | indent 4}}
|
||||
lea {{.DI}}, [{{.Use "su_sample_offsets"}} + {{.AX}}*8]; edi points now to the sample table entry
|
||||
%endif
|
||||
{{- end}}
|
||||
{{- .Float 84.28074964676522 | .Prepare | indent 4}}
|
||||
fmul dword [{{.Float 84.28074964676522 | .Use}}] ; p*r
|
||||
fistp dword [{{.SP}}]
|
||||
@ -329,7 +328,7 @@ su_oscillat_sample_not_looping:
|
||||
{{end}}
|
||||
|
||||
|
||||
{{- if .Opcode "loadval"}}
|
||||
{{- if .HasOp "loadval"}}
|
||||
;-------------------------------------------------------------------------------
|
||||
; LOADVAL opcode
|
||||
;-------------------------------------------------------------------------------
|
||||
@ -356,7 +355,7 @@ su_op_loadval_mono:
|
||||
{{end}}
|
||||
|
||||
|
||||
{{- if .Opcode "receive"}}
|
||||
{{- if .HasOp "receive"}}
|
||||
;-------------------------------------------------------------------------------
|
||||
; RECEIVE opcode
|
||||
;-------------------------------------------------------------------------------
|
||||
@ -387,7 +386,7 @@ su_op_receive_mono:
|
||||
{{end}}
|
||||
|
||||
|
||||
{{- if .Opcode "in"}}
|
||||
{{- if .HasOp "in"}}
|
||||
;-------------------------------------------------------------------------------
|
||||
; IN opcode: inputs and clears a global port
|
||||
;-------------------------------------------------------------------------------
|
||||
|
@ -41,3 +41,13 @@ struc su_delayline_wrk
|
||||
.buffer resd 65536
|
||||
.size:
|
||||
endstruc
|
||||
|
||||
;-------------------------------------------------------------------------------
|
||||
; su_sample_offset struct
|
||||
;-------------------------------------------------------------------------------
|
||||
struc su_sample_offset ; length conveniently 8 bytes, so easy to index
|
||||
.start resd 1
|
||||
.loopstart resw 1
|
||||
.looplength resw 1
|
||||
.size:
|
||||
endstruc
|
Reference in New Issue
Block a user