sointu/render.asm
Veikko Sariola bcbb5aaf19 feat: Delays and samples are now working through the bridge.
One should call bridge.Init() once during the initialization of the program to load the static sample table. On linux, bridge.Init() does nothing.
2020-11-08 16:03:10 +02:00

216 lines
7.0 KiB
NASM

; 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_SAMPLES
%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
%define INCLUDE_GMDLS
%include "sointu/footer.inc"
section .text
struc su_sampleoff
.start resd 1
.loopstart resw 1
.looplength resw 1
.size:
endstruc
struc su_synth
.synthwrk resb su_synthworkspace.size
.delaywrks resb su_delayline_wrk.size * 64
.delaytimes resw 768
.sampleoffs resb su_sampleoff.size * 256
.randseed resd 1
.globaltime resd 1
.commands resb 32 * 64
.values resb 32 * 64 * 8
.polyphony resd 1
.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
push _SI ; push the pointer to samples
push _BX ; push the pointer to 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
xor eax, eax ; rowtick starts at 0
su_render_samples_loop:
cmp eax, [_SP] ; 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
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]
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]
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
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
xor eax, eax ; TODO: set eax to possible error code, now just 0
%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
ret
%endif