diff --git a/bridge/bridge.go b/bridge/bridge.go index 4be0067..a162039 100644 --- a/bridge/bridge.go +++ b/bridge/bridge.go @@ -10,6 +10,8 @@ import ( import "C" // SynthState contains the entire state of sointu sound engine +type Synth C.Synth // hide C.Synth, explicit cast is still possible if needed + type SynthState C.SynthState // hide C.Synthstate, explicit cast is still possible if needed // Opcode is a single byte, representing the virtual machine commands used in Sointu @@ -87,12 +89,12 @@ func (o Opcode) Mono() Opcode { // buffer float32 slice to fill with rendered samples. Stereo signal, so // should have even length. // Returns an error if something went wrong. -func (s *SynthState) Render(buffer []float32) error { +func (synth *Synth) Render(state *SynthState, buffer []float32) error { if len(buffer)%1 == 1 { return errors.New("Render writes stereo signals, so buffer should have even length") } maxSamples := len(buffer) / 2 - errcode := C.su_render((*C.SynthState)(s), (*C.float)(&buffer[0]), C.int(maxSamples)) + errcode := C.su_render((*C.Synth)(synth), (*C.SynthState)(state), (*C.float)(&buffer[0]), C.int(maxSamples)) if errcode > 0 { return errors.New("Render failed") } @@ -116,30 +118,30 @@ func (s *SynthState) Render(buffer []float32) error { // time > maxtime, as it is modulated and the time could advance by 2 or more, so the loop // exit condition would fire when the time is already past maxtime. // Under no conditions, nsamples >= len(buffer)/2 i.e. guaranteed to never overwrite the buffer. -func (s *SynthState) RenderTime(buffer []float32, maxtime int) (int, int, error) { +func (synth *Synth) RenderTime(state *SynthState, buffer []float32, maxtime int) (int, int, error) { if len(buffer)%1 == 1 { return -1, -1, errors.New("RenderTime writes stereo signals, so buffer should have even length") } samples := C.int(len(buffer) / 2) time := C.int(maxtime) - errcode := int(C.su_render_time((*C.SynthState)(s), (*C.float)(&buffer[0]), &samples, &time)) + errcode := int(C.su_render_time((*C.Synth)(synth), (*C.SynthState)(state), (*C.float)(&buffer[0]), &samples, &time)) if errcode > 0 { return -1, -1, errors.New("RenderTime failed") } return int(samples), int(time), nil } -func (s *SynthState) SetPatch(patch Patch) error { +func Compile(patch Patch) (*Synth, error) { totalVoices := 0 commands := make([]Opcode, 0) values := make([]byte, 0) polyphonyBitmask := 0 for _, instr := range patch { if len(instr.Units) > 63 { - return errors.New("An instrument can have a maximum of 63 units") + return nil, errors.New("An instrument can have a maximum of 63 units") } if instr.NumVoices < 1 { - return errors.New("Each instrument must have at least 1 voice") + return nil, errors.New("Each instrument must have at least 1 voice") } for _, unit := range instr.Units { commands = append(commands, unit.Command) @@ -153,35 +155,35 @@ func (s *SynthState) SetPatch(patch Patch) error { polyphonyBitmask <<= 1 } if totalVoices > 32 { - return errors.New("Sointu does not support more than 32 concurrent voices") + return nil, errors.New("Sointu does not support more than 32 concurrent voices") } if len(commands) > 2048 { // TODO: 2048 could probably be pulled automatically from cgo - return errors.New("The patch would result in more than 2048 commands") + return nil, errors.New("The patch would result in more than 2048 commands") } if len(values) > 16384 { // TODO: 16384 could probably be pulled automatically from cgo - return errors.New("The patch would result in more than 16384 values") + return nil, errors.New("The patch would result in more than 16384 values") } - cs := (*C.SynthState)(s) + s := new(Synth) for i := range commands { - cs.Commands[i] = (C.uchar)(commands[i]) + s.Commands[i] = (C.uchar)(commands[i]) } for i := range values { - cs.Values[i] = (C.uchar)(values[i]) + s.Values[i] = (C.uchar)(values[i]) } - cs.NumVoices = C.uint(totalVoices) - cs.Polyphony = C.uint(polyphonyBitmask) - return nil + s.NumVoices = C.uint(totalVoices) + s.Polyphony = C.uint(polyphonyBitmask) + return s, nil } func (s *SynthState) Trigger(voice int, note byte) { cs := (*C.SynthState)(s) - cs.Synth.Voices[voice] = C.Voice{} - cs.Synth.Voices[voice].Note = C.int(note) + cs.SynthWrk.Voices[voice] = C.Voice{} + cs.SynthWrk.Voices[voice].Note = C.int(note) } func (s *SynthState) Release(voice int) { cs := (*C.SynthState)(s) - cs.Synth.Voices[voice].Release = 1 + cs.SynthWrk.Voices[voice].Release = 1 } func NewSynthState() *SynthState { diff --git a/bridge/bridge_test.go b/bridge/bridge_test.go index 216b1da..0da25b0 100644 --- a/bridge/bridge_test.go +++ b/bridge/bridge_test.go @@ -21,22 +21,25 @@ const su_max_samples = SAMPLES_PER_ROW * TOTAL_ROWS // const bufsize = su_max_samples * 2 func TestBridge(t *testing.T) { - s := bridge.NewSynthState() - s.SetPatch([]bridge.Instrument{ + patch := []bridge.Instrument{ bridge.Instrument{1, []bridge.Unit{ bridge.Unit{bridge.Envelope, []byte{64, 64, 64, 80, 128}}, bridge.Unit{bridge.Envelope, []byte{95, 64, 64, 80, 128}}, bridge.Unit{bridge.Out.Stereo(), []byte{128}}, - }}, - }) - s.Trigger(0, 64) + }}} + synth, err := bridge.Compile(patch) + if err != nil { + t.Fatalf("bridge compile error: %v", err) + } + state := bridge.NewSynthState() + state.Trigger(0, 64) buffer := make([]float32, 2*su_max_samples) - err := s.Render(buffer[:len(buffer)/2]) + err = synth.Render(state, buffer[:len(buffer)/2]) if err != nil { t.Fatalf("first render gave an error") } - s.Release(0) - err = s.Render(buffer[len(buffer)/2:]) + state.Release(0) + err = synth.Render(state, buffer[len(buffer)/2:]) if err != nil { t.Fatalf("first render gave an error") } diff --git a/include/sointu.h b/include/sointu.h index 74ca9b0..ae7840f 100644 --- a/include/sointu.h +++ b/include/sointu.h @@ -15,14 +15,6 @@ typedef struct Voice { struct Unit Units[63]; } Voice; -typedef struct Synth { - unsigned char Curvoices[32]; - float Left; - float Right; - float Aux[6]; - struct Voice Voices[32]; -} Synth; - typedef struct DelayWorkspace { float Buffer[65536]; float Dcin; @@ -30,16 +22,27 @@ typedef struct DelayWorkspace { float Filtstate; } DelayWorkspace; +typedef struct SynthWorkspace { + unsigned char Curvoices[32]; + float Left; + float Right; + float Aux[6]; + struct Voice Voices[32]; +} SynthWorkspace; + typedef struct SynthState { - struct Synth Synth; - 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. + 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 int RandSeed; + unsigned int GlobalTick; +} SynthState; + +typedef struct Synth { unsigned char Commands[32 * 64]; unsigned char Values[32 * 64 * 8]; unsigned int Polyphony; unsigned int NumVoices; - unsigned int RandSeed; - unsigned int GlobalTick; -} SynthState; +} Synth; #pragma pack(pop) #if UINTPTR_MAX == 0xffffffff // are we 32-bit? @@ -56,11 +59,12 @@ typedef struct SynthState { extern void CALLCONV su_load_gmdls(void); #endif -// int su_render(SynthState* synthState, float* buffer, int samples): -// Renders 'samples' number of samples to the buffer, using and modifying -// the synthesizer state in synthState. +// int su_render(Synth* synth,SynthState* synthState, float* buffer, int samples): +// Renders 'samples' number of 'samples' to the 'buffer', using 'synth'. +// Modifies 'synthState' and fills the 'buffer'. // // Parameters: +// synth pointer to the synthesizer used. Won't get modified by the call. // synthState pointer to current synthState. RandSeed should be > 0 e.g. 1 // buffer audio sample buffer, L R L R ... // samples maximum number of samples to be rendered. WARNING: buffer @@ -69,15 +73,16 @@ extern void CALLCONV su_load_gmdls(void); // Returns error code: // 0 everything ok // (returns always 0 as no errors are implemented yet) -int CALLCONV su_render(SynthState* synthState, float* buffer, int samples); +int CALLCONV su_render(Synth* synth,SynthState* synthState, float* buffer, int samples); -// int su_render_time(SynthState* synthState, float* buffer, int* samples, int* time): +// int su_render_time(Synth* synth,SynthState* synthState, 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. Won't get modified by the call. // synthState pointer to current synthState. RandSeed should be > 0 e.g. 1 // Also synthState->SamplesPerRow cannot be 0 or nothing will be // rendered; either set it to INT32_MAX to always render full @@ -99,7 +104,7 @@ int CALLCONV su_render(SynthState* synthState, float* buffer, int samples); // Returns error code: // 0 everything ok // (no actual errors implemented yet) -int CALLCONV su_render_time(SynthState* synthState, float* buffer, int* samples, int* time); +int CALLCONV su_render_time(Synth* synth,SynthState* synthState, float* buffer, int* samples, int* time); // Arithmetic opcode ids extern const int su_add_id; diff --git a/song/song.go b/song/song.go index 67ffb1a..b24cc9d 100644 --- a/song/song.go +++ b/song/song.go @@ -86,13 +86,11 @@ func (s *Song) FirstTrackVoice(track int) int { return ret } -func (s *Song) Render() ([]float32, error) { +func (s *Song) Render(synth *bridge.Synth, state *bridge.SynthState) ([]float32, error) { err := s.Validate() if err != nil { return nil, err } - synth := bridge.NewSynthState() - synth.SetPatch(s.Patch) curVoices := make([]int, len(s.Tracks)) for i := range curVoices { curVoices[i] = s.FirstTrackVoice(i) @@ -113,17 +111,17 @@ func (s *Song) Render() ([]float32, error) { if note == 1 { // anything but hold causes an action. continue // TODO: can hold be actually something else than 1? } - synth.Release(curVoices[t]) + state.Release(curVoices[t]) if note > 1 { curVoices[t]++ first := s.FirstTrackVoice(t) if curVoices[t] >= first+s.Tracks[t].NumVoices { curVoices[t] = first } - synth.Trigger(curVoices[t], note) + state.Trigger(curVoices[t], note) } } - samples, _, _ := synth.RenderTime(buffer[2*totaln:], rowtime) + samples, _, _ := synth.RenderTime(state, buffer[2*totaln:], rowtime) totaln += samples } return buffer, nil diff --git a/song/song_test.go b/song/song_test.go index d803f7e..b837d5c 100644 --- a/song/song_test.go +++ b/song/song_test.go @@ -38,7 +38,12 @@ func TestSongRender(t *testing.T) { if err != nil { t.Fatalf("NewSong failed: %v", err) } - buffer, err := song.Render() + synth, err := bridge.Compile(patch) + if err != nil { + t.Fatalf("Compiling patch failed: %v", err) + } + state := bridge.NewSynthState() + buffer, err := song.Render(synth, state) if err != nil { t.Fatalf("Render failed: %v", err) } diff --git a/src/opcodes/sinks_footer.inc b/src/opcodes/sinks_footer.inc index 37c578a..7f28217 100644 --- a/src/opcodes/sinks_footer.inc +++ b/src/opcodes/sinks_footer.inc @@ -17,8 +17,8 @@ EXPORT MANGLE_FUNC(su_op_out,0) ; l r su_op_out_mono: %endif fmul dword [INP + su_out_ports.gain] ; g*l - fadd dword [_AX + su_synth.left] ; g*l+o - fstp dword [_AX + su_synth.left] ; o'=g*l+o + fadd dword [_AX + su_synthworkspace.left] ; g*l+o + fstp dword [_AX + su_synthworkspace.left] ; o'=g*l+o ret %endif ; SU_OUT_ID > -1 @@ -43,11 +43,11 @@ EXPORT MANGLE_FUNC(su_op_outaux,0) ; l r %endif fld st0 ; l l fmul dword [INP + su_outaux_ports.outgain] ; g*l - fadd dword [_AX + su_synth.left] ; g*l+o - fstp dword [_AX + su_synth.left] ; o'=g*l+o + fadd dword [_AX + su_synthworkspace.left] ; g*l+o + fstp dword [_AX + su_synthworkspace.left] ; o'=g*l+o fmul dword [INP + su_outaux_ports.auxgain] ; h*l - fadd dword [_AX + su_synth.aux] ; h*l+a - fstp dword [_AX + su_synth.aux] ; a'=h*l+a + fadd dword [_AX + su_synthworkspace.aux] ; h*l+a + fstp dword [_AX + su_synthworkspace.aux] ; a'=h*l+a ret %endif ; SU_OUTAUX_ID > -1 @@ -72,8 +72,8 @@ EXPORT MANGLE_FUNC(su_op_aux,0) ; l r su_op_aux_mono: %endif fmul dword [INP + su_aux_ports.gain] ; g*l - fadd dword [_DI + su_synth.left + _AX*4] ; g*l+o - fstp dword [_DI + su_synth.left + _AX*4] ; o'=g*l+o + fadd dword [_DI + su_synthworkspace.left + _AX*4] ; g*l+o + fstp dword [_DI + su_synthworkspace.left + _AX*4] ; o'=g*l+o ret %endif ; SU_AUX_ID > -1 diff --git a/src/opcodes/sinks_header.inc b/src/opcodes/sinks_header.inc index 2e10b7c..c0c0b01 100644 --- a/src/opcodes/sinks_header.inc +++ b/src/opcodes/sinks_header.inc @@ -114,7 +114,7 @@ endstruc %define AMOUNT(val) val %define LOCALPORT(unit,port) ((unit+1)*su_unit.size + su_unit.ports)/4 + port -%define GLOBALPORT(voice,unit,port) SEND_GLOBAL + (su_synth.voices+voice*su_voice.size+su_voice.workspace+unit*su_unit.size + su_unit.ports)/4 + port +%define GLOBALPORT(voice,unit,port) SEND_GLOBAL + (su_synthworkspace.voices+voice*su_voice.size+su_voice.workspace+unit*su_unit.size + su_unit.ports)/4 + port %define OUTPORT 0 %define SEND_POP 0x8000 %define SEND_GLOBAL 0x4000 diff --git a/src/opcodes/sources_footer.inc b/src/opcodes/sources_footer.inc index c645d0f..8430aac 100644 --- a/src/opcodes/sources_footer.inc +++ b/src/opcodes/sources_footer.inc @@ -406,13 +406,13 @@ EXPORT MANGLE_FUNC(su_op_in,0) sub _DI, 4 su_op_in_right: xor ecx, ecx - fld dword [_DI + su_synth.right + _AX*4] - mov dword [_DI + su_synth.right + _AX*4], ecx + fld dword [_DI + su_synthworkspace.right + _AX*4] + mov dword [_DI + su_synthworkspace.right + _AX*4], ecx %else xor ecx, ecx mov _DI, [_SP + su_stack.synth] - fld dword [_DI + su_synth.left + _AX*4] - mov dword [_DI + su_synth.left + _AX*4], ecx + fld dword [_DI + su_synthworkspace.left + _AX*4] + mov dword [_DI + su_synthworkspace.left + _AX*4], ecx %endif ret diff --git a/src/sointu.asm b/src/sointu.asm index 790ba9d..dd877d9 100644 --- a/src/sointu.asm +++ b/src/sointu.asm @@ -26,37 +26,50 @@ USE_OUT section .text struc su_synth_state - .synth resb su_synth.size - .delaywrks resb su_delayline_wrk.size * 64 - .commands resb 32 * 64 - .values resb 32 * 64 * 8 - .polyphony resd 1 - .numvoices resd 1 + .synthwrk resb su_synthworkspace.size + .delaywrks resb su_delayline_wrk.size * 64 .randseed resd 1 .globaltime resd 1 endstruc +struc su_synth + .commands resb 32 * 64 + .values resb 32 * 64 * 8 + .polyphony resd 1 + .numvoices resd 1 +endstruc + SECT_TEXT(sursampl) -EXPORT MANGLE_FUNC(su_render_time,16) +EXPORT MANGLE_FUNC(su_render_time,20) %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 + pushad ; push registers + mov eax, [esp + 4 + 32] ; eax = &synth + mov ecx, [esp + 8 + 32] ; ecx = &synthState + mov edx, [esp + 12 + 32] ; edx = &buffer + mov esi, [esp + 16 + 32] ; esi = &samples + mov ebx, [esp + 20 + 32] ; ebx = &time %else - %ifidn __OUTPUT_FORMAT__,win64 ; win64 ABI: rcx = &synthstate, rdx = &buffer, r8 = &bufsize, r9 = &time + %ifidn __OUTPUT_FORMAT__,win64 + ; win64 ABI parameter order: RCX, RDX, R8, R9; rest in stack (right-to-left, note shadow space!) + ; here: rcx = &synth, rdx = &synthstate, r8 = &buffer, r9 = &bufsize, stack1 = &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 = &synthstate, rsi = &buffer, rdx = &samples, rcx = &time + mov rbx, [rsp + 0x28 + PUSH_REG_SIZE(4)] ; rbx = &time, note the shadow space so &time is at rsp + 0x28 + mov rax, rcx + mov rcx, rdx + mov rdx, r8 + mov rsi, r9 + %else + ; System V ABI parameter order: RDI, RSI, RDX, RCX, R8, R9; rest in stack (right-to-left) + ; here: rdi = &synth, rsi = &synthstate, rdx = &buffer, rcx = &samples, r8 = &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 + mov rax, rdi + mov rbx, r8 + xchg rcx, rsi %endif %endif + ; _AX = &synth, _BX == &time, _CX == &synthstate, _DX == &buf, _SI == &samples, + push _AX ; push the pointer to synth to stack push _SI ; push the pointer to samples push _BX ; push the pointer to time xor eax, eax ; samplenumber starts at 0 @@ -80,23 +93,24 @@ su_render_samples_loop: jge su_render_samples_time_finish ; goto finish inc eax ; time++ inc dword [_SP + PTRSIZE*6] ; samples++ - mov _CX, [_SP + PTRSIZE*3] + mov _CX, [_SP + PTRSIZE*3] ; _CX = &synthstate + mov _BP, [_SP + PTRSIZE*9] ; _BP = &synth push _AX ; push rowtick - mov eax, [_CX + su_synth_state.polyphony] + mov eax, [_BP + su_synth.polyphony] push _AX ;polyphony - mov eax, [_CX + su_synth_state.numvoices] + mov eax, [_BP + su_synth.numvoices] push _AX ;numvoices - lea _DX, [_CX+ su_synth_state.synth] - lea COM, [_CX+ su_synth_state.commands] - lea VAL, [_CX+ su_synth_state.values] - lea WRK, [_DX + su_synth.voices] - lea _CX, [_CX+ su_synth_state.delaywrks - su_delayline_wrk.filtstate] + lea _DX, [_CX + su_synth_state.synthwrk] + lea COM, [_BP + su_synth.commands] + lea VAL, [_BP + su_synth.values] + lea WRK, [_DX + su_synthworkspace.voices] ; _BP and WRK are actually the same thing so here _BP gets overwritten + lea _CX, [_CX + su_synth_state.delaywrks - su_delayline_wrk.filtstate] call MANGLE_FUNC(su_run_vm,0) pop _AX pop _AX mov _DI, [_SP + PTRSIZE*5] ; edi containts buffer ptr mov _CX, [_SP + PTRSIZE*4] - lea _SI, [_CX + su_synth_state.synth + su_synth.left] + lea _SI, [_CX + su_synth_state.synthwrk + su_synthworkspace.left] movsd ; copy left channel to output buffer movsd ; copy right channel to output buffer mov [_SP + PTRSIZE*5], _DI ; save back the updated ptr @@ -121,11 +135,12 @@ su_render_samples_time_finish: pop _SI ; pop the pointer to samples mov dword [_SI], edx ; *samples = samples rendered mov dword [_BX], eax ; *time = time ticks rendered + pop _AX ; pop the synth pointer 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 + ret 20 %else %ifidn __OUTPUT_FORMAT__,win64 pop_registers rdi, rsi, rbx, rbp ; win64 ABI: these registers are non-volatile @@ -135,47 +150,44 @@ su_render_samples_time_finish: ret %endif -EXPORT MANGLE_FUNC(su_render,12) +EXPORT MANGLE_FUNC(su_render,16) %if BITS == 32 ; stdcall mov eax, 0x7FFFFFFF ; don't care about time, just try to fill the buffer push eax - mov eax, [esp + 8] ; eax = &synthState - mov ecx, [esp + 12] ; ecx = &buffer - mov edx, [esp + 16] ; edx = samples - push edx - lea edx, [esp + 4] - push edx - lea edx, [esp + 4] - push edx - push ecx - push eax + push esp + lea eax, [esp + 24] + push eax + push dword [esp + 24] + push dword [esp + 24] + push dword [esp + 24] %else - %ifidn __OUTPUT_FORMAT__,win64 ; win64 ABI: rdx = bufsize, r8 = &buffer, rcx = &synthstate - push r8 - mov r8, _SP - mov r9, 0x7FFFFFFF ; don't care about time, just try to fill the buffer - push r9 - mov r9, _SP ; still, we have to pass a pointer to time, so pointer to stack - %else ; System V ABI: rdi = &synthstate, rsi = &buffer, rdx = samples - push rdx - mov rdx, _SP - mov rcx, 0x7FFFFFFF ; don't care about time, just try to fill the buffer + %ifidn __OUTPUT_FORMAT__,win64 + ; win64 ABI parameter order: RCX, RDX, R8, R9; rest in stack (right-to-left, note shadow space!) + ; here: rcx = &synth, rdx = &synthstate, r8 = &buffer, r9 = samples + ; put the values in shadow space and get pointers to them + mov [_SP+0x8],r9 + lea r9, [_SP+0x8] + mov [_SP+0x10], dword 0x7FFFFFFF ; don't care about time, just try to fill the buffer + lea r10, [_SP+0x10] + mov [_SP+0x20], r10 + %else + ; System V ABI parameter order: RDI, RSI, RDX, RCX, R8, R9; rest in stack (right-to-left) + ; here: rdi = &synth, rsi = &synthstate, rdx = &buffer, rcx = samples push rcx - mov rcx, _SP ; still, we have to pass a pointer to time, so pointer to stack + mov rcx, _SP ; pass a pointer to samples instead of direct value + mov r8, 0x7FFFFFFF ; don't care about time, just try to fill the buffer + push r8 + mov r8, _SP ; still, we have to pass a pointer to time, so pointer to stack %endif %endif - call MANGLE_FUNC(su_render_time,16) + call MANGLE_FUNC(su_render_time,20) %if BITS == 32 ; stdcall pop ecx - pop ecx - ret 12 + ret 16 %else - %ifidn __OUTPUT_FORMAT__,win64 ; win64 ABI: rdx = bufsize, r8 = &buffer, rcx = &synthstate - pop r9 - pop r8 - %else + %ifnidn __OUTPUT_FORMAT__,win64 ; win64 ABI: rdx = bufsize, r8 = &buffer, rcx = &synthstate pop rcx - pop rdx + pop r8 %endif ret %endif diff --git a/src/sointu_footer.inc b/src/sointu_footer.inc index aeb9d07..4daba8a 100644 --- a/src/sointu_footer.inc +++ b/src/sointu_footer.inc @@ -168,7 +168,7 @@ endstruc ;=============================================================================== SECT_BSS(susynth) -su_synth_obj resb su_synth.size +su_synth_obj resb su_synthworkspace.size %if DELAY_ID > -1 ; if we use delay, then the synth obj should be immediately followed by the delay workspaces resb NUM_DELAY_LINES*su_delayline_wrk.size @@ -287,7 +287,7 @@ EXPORT MANGLE_FUNC(su_power,0) %ifndef SU_USE_16BIT_OUTPUT %ifndef SU_CLIP_OUTPUT ; The modern way. No need to clip; OS can do it. mov _DI, [_SP+su_stack.bufferptr - su_stack.output_sound] ; edi containts ptr - mov _SI, PTRWORD su_synth_obj + su_synth.left + mov _SI, PTRWORD su_synth_obj + su_synthworkspace.left movsd ; copy left channel to output buffer movsd ; copy right channel to output buffer mov [_SP+su_stack.bufferptr - su_stack.output_sound], _DI ; save back the updated ptr @@ -300,10 +300,10 @@ EXPORT MANGLE_FUNC(su_power,0) xor _CX,_CX xor eax,eax %%loop: ; loop over two channels, left & right - do fld dword [,su_synth_obj+su_synth.left,_CX*4,] + do fld dword [,su_synth_obj+su_synthworkspace.left,_CX*4,] call su_clip fstp dword [_SI] - do mov dword [,su_synth_obj+su_synth.left,_CX*4,{],eax} ; clear the sample so the VM is ready to write it + do mov dword [,su_synth_obj+su_synthworkspace.left,_CX*4,{],eax} ; clear the sample so the VM is ready to write it add _SI,4 cmp ecx,2 jl %%loop @@ -311,7 +311,7 @@ EXPORT MANGLE_FUNC(su_power,0) %endif %else ; 16-bit output, always clipped. This is a bit legacy method. mov _SI, [_SP+su_stack.bufferptr - su_stack.output_sound] ; esi points to the output buffer - mov _DI, PTRWORD su_synth_obj+su_synth.left + mov _DI, PTRWORD su_synth_obj+su_synthworkspace.left mov ecx, 2 %%loop: ; loop over two channels, left & right fld dword [_DI] @@ -361,9 +361,9 @@ su_render_sampleloop: ; loop through every sample in the row mov COM, PTRWORD MANGLE_DATA(su_commands) ; COM points to vm code mov VAL, PTRWORD MANGLE_DATA(su_params) ; VAL points to unit params %if DELAY_ID > -1 - lea _CX, [_DX + su_synth.size - su_delayline_wrk.filtstate] + lea _CX, [_DX + su_synthworkspace.size - su_delayline_wrk.filtstate] %endif - lea WRK, [_DX + su_synth.voices] ; WRK points to the first voice + lea WRK, [_DX + su_synthworkspace.voices] ; WRK points to the first voice call MANGLE_FUNC(su_run_vm,0) ; run through the VM code pop _AX %ifdef INCLUDE_POLYPHONY @@ -424,7 +424,7 @@ su_calculate_voices_loop: ; do { mov edi, ecx add edi, ebx shl edi, MAX_UNITS_SHIFT + 6 ; each unit = 64 bytes and there are 1<Commands, commands, sizeof(commands)); + memcpy(synth->Values, values, sizeof(values)); + synth->NumVoices = 1; + synth->Polyphony = 0; + // initialize SynthState + synthState = (SynthState*)malloc(sizeof(SynthState)); memset(synthState, 0, sizeof(SynthState)); - memcpy(synthState->Commands, commands, sizeof(commands)); - memcpy(synthState->Values, values, sizeof(values)); synthState->RandSeed = 1; - synthState->NumVoices = 1; - synthState->Synth.Voices[0].Note = 64; - retval = su_render(synthState, buffer, su_max_samples / 2); - synthState->Synth.Voices[0].Release++; + // triger first voice + synthState->SynthWrk.Voices[0].Note = 64; + retval = su_render(synth, synthState, buffer, su_max_samples / 2); + synthState->SynthWrk.Voices[0].Release++; buffer = buffer + su_max_samples; - retval = su_render(synthState, buffer, su_max_samples / 2); + retval = su_render(synth, synthState, buffer, su_max_samples / 2); + free(synth); free(synthState); return; } diff --git a/tests/test_render_samples_api.c b/tests/test_render_samples_api.c index 3755b14..1003a16 100644 --- a/tests/test_render_samples_api.c +++ b/tests/test_render_samples_api.c @@ -11,6 +11,7 @@ const int su_max_samples = SAMPLES_PER_ROW * TOTAL_ROWS; int main(int argc, char* argv[]) { + Synth* synth; SynthState* synthState; float* buffer; const unsigned char commands[] = { su_envelope_id, // MONO @@ -24,21 +25,27 @@ int main(int argc, char* argv[]) { int time; int samples; int totalrendered; - int retval; + int retval; + // initialize Synth + synth = (Synth*)malloc(sizeof(Synth)); + memcpy(synth->Commands, commands, sizeof(commands)); + memcpy(synth->Values, values, sizeof(values)); + synth->NumVoices = 1; + synth->Polyphony = 0; + // initialize SynthState synthState = (SynthState*)malloc(sizeof(SynthState)); - buffer = (float*)malloc(2 * sizeof(float) * su_max_samples); memset(synthState, 0, sizeof(SynthState)); - memcpy(synthState->Commands, commands, sizeof(commands)); - memcpy(synthState->Values, values, sizeof(values)); synthState->RandSeed = 1; - synthState->NumVoices = 1; - synthState->Synth.Voices[0].Note = 64; + // initialize Buffer + buffer = (float*)malloc(2 * sizeof(float) * su_max_samples); + // triger first voice + synthState->SynthWrk.Voices[0].Note = 64; totalrendered = 0; // First check that when we render using su_render_time with 0 time // we get nothing done samples = su_max_samples; time = 0; - errcode = su_render_time(synthState, buffer, &samples, &time); + errcode = su_render_time(synth, synthState, buffer, &samples, &time); if (errcode != 0) { printf("su_render_time returned error"); @@ -58,7 +65,7 @@ int main(int argc, char* argv[]) { // we get nothing done samples = 0; time = INT32_MAX; - errcode = su_render_time(synthState, buffer, &samples, &time); + errcode = su_render_time(synth, synthState, buffer, &samples, &time); if (errcode != 0) { printf("su_render_time returned error"); @@ -81,7 +88,7 @@ int main(int argc, char* argv[]) { // check that buffer full samples = 1; time = INT32_MAX; - su_render_time(synthState, &buffer[totalrendered*2], &samples, &time); + su_render_time(synth, synthState, &buffer[totalrendered*2], &samples, &time); totalrendered += samples; if (samples != 1) { @@ -95,7 +102,7 @@ int main(int argc, char* argv[]) { } samples = SAMPLES_PER_ROW - 1; time = INT32_MAX; - su_render_time(synthState, &buffer[totalrendered * 2], &samples, &time); + su_render_time(synth, synthState, &buffer[totalrendered * 2], &samples, &time); totalrendered += samples; if (samples != SAMPLES_PER_ROW - 1) { @@ -108,7 +115,7 @@ int main(int argc, char* argv[]) { goto fail; } if (i == 8) - synthState->Synth.Voices[0].Release++; + synthState->SynthWrk.Voices[0].Release++; } if (totalrendered != su_max_samples) { @@ -117,6 +124,7 @@ int main(int argc, char* argv[]) { } retval = 0; finish: + free(synth); free(synthState); free(buffer); return retval;