diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 0dcb899..cc917ba 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -162,7 +162,8 @@ regression_test(test_delay_flanger "ENVELOPE;FOP_MULP;PANNING;VCO_SINE;SEND") regression_test(test_envelope_mod "VCO_SINE;ENVELOPE;SEND") regression_test(test_envelope_16bit ENVELOPE "" test_envelope "-i") -regression_test(test_polyphony "ENVELOPE;VCO_SINE") +regression_test(test_polyphony "ENVELOPE;VCO_SINE" POLYPHONY) +regression_test(test_polyphony_init POLYPHONY) regression_test(test_chords "ENVELOPE;VCO_SINE") regression_test(test_speed "ENVELOPE;VCO_SINE") regression_test(test_sync "ENVELOPE" "" "" "-r") diff --git a/tests/expected_output/test_polyphony_init.raw b/tests/expected_output/test_polyphony_init.raw new file mode 100644 index 0000000..f0ee709 Binary files /dev/null and b/tests/expected_output/test_polyphony_init.raw differ diff --git a/tests/test_polyphony_init.yml b/tests/test_polyphony_init.yml new file mode 100644 index 0000000..ca31f49 --- /dev/null +++ b/tests/test_polyphony_init.yml @@ -0,0 +1,18 @@ +bpm: 100 +rowsperbeat: 4 +score: + rowsperpattern: 16 + length: 1 + tracks: + - numvoices: 2 + order: [0] + patterns: [[0,0, 0, 0, 64, 1, 0, 0, 64, 1, 64, 1, 0, 0, 0, 0]] +patch: + - numvoices: 2 + units: + - type: envelope + parameters: {attack: 32, decay: 32, gain: 64, release: 64, stereo: 0, sustain: 64} + - type: envelope + parameters: {attack: 32, decay: 32, gain: 64, release: 64, stereo: 0, sustain: 64} + - type: out + parameters: {gain: 128, stereo: 1} diff --git a/tests/test_render_samples.c b/tests/test_render_samples.c index fba39f6..562e761 100644 --- a/tests/test_render_samples.c +++ b/tests/test_render_samples.c @@ -26,10 +26,11 @@ void SU_CALLCONV su_render_song(float* buffer) { synth->RandSeed = 1; // triger first voice synth->SynthWrk.Voices[0].Note = 64; + synth->SynthWrk.Voices[0].Sustain = 1; samples = SU_LENGTH_IN_SAMPLES / 2; time = INT32_MAX; retval = su_render(synth, buffer, &samples, &time); - synth->SynthWrk.Voices[0].Release++; + synth->SynthWrk.Voices[0].Sustain = 0; buffer = buffer + SU_LENGTH_IN_SAMPLES; samples = SU_LENGTH_IN_SAMPLES / 2; time = INT32_MAX; diff --git a/tests/test_render_samples_api.c b/tests/test_render_samples_api.c index 2dba51a..135862f 100644 --- a/tests/test_render_samples_api.c +++ b/tests/test_render_samples_api.c @@ -37,6 +37,7 @@ int main(int argc, char* argv[]) { buffer = (float*)malloc(2 * sizeof(float) * su_max_samples); // triger first voice synth->SynthWrk.Voices[0].Note = 64; + synth->SynthWrk.Voices[0].Sustain = 1; totalrendered = 0; // First check that when we render using su_render with 0 time // we get nothing done @@ -110,7 +111,7 @@ int main(int argc, char* argv[]) { goto fail; } if (i == 8) - synth->SynthWrk.Voices[0].Release++; + synth->SynthWrk.Voices[0].Sustain = 0; } if (totalrendered != su_max_samples) { diff --git a/vm/compiler/bridge/bridge.go b/vm/compiler/bridge/bridge.go index 094bf7e..cdc1f77 100644 --- a/vm/compiler/bridge/bridge.go +++ b/vm/compiler/bridge/bridge.go @@ -102,6 +102,7 @@ func (bridgesynth *BridgeSynth) Trigger(voice int, note byte) { } s.SynthWrk.Voices[voice] = C.Voice{} s.SynthWrk.Voices[voice].Note = C.int(note) + s.SynthWrk.Voices[voice].Sustain = 1 } // Release is part of C.Synths' implementation of sointu.Synth interface @@ -110,7 +111,7 @@ func (bridgesynth *BridgeSynth) Release(voice int) { if voice < 0 || voice >= len(s.SynthWrk.Voices) { return } - s.SynthWrk.Voices[voice].Release = 1 + s.SynthWrk.Voices[voice].Sustain = 0 } // Update diff --git a/vm/compiler/templates/amd64-386/library.h b/vm/compiler/templates/amd64-386/library.h index a84cb08..6976065 100644 --- a/vm/compiler/templates/amd64-386/library.h +++ b/vm/compiler/templates/amd64-386/library.h @@ -9,7 +9,7 @@ typedef struct Unit { typedef struct Voice { int Note; - int Release; + int Sustain; float Inputs[8]; float Reserved[6]; struct Unit Units[63]; diff --git a/vm/compiler/templates/amd64-386/player.asm b/vm/compiler/templates/amd64-386/player.asm index ba7b921..111ef83 100644 --- a/vm/compiler/templates/amd64-386/player.asm +++ b/vm/compiler/templates/amd64-386/player.asm @@ -137,7 +137,7 @@ su_calculate_voices_loop: ; do { add edi, ebx shl edi, 12 ; each unit = 64 bytes and there are 1<>8, but will it actually be smaller after compression? + stosd ; save sustain + mov ecx, (su_voice.size - su_voice.inputs)/4 ; could be xor ecx, ecx; mov ch,...>>8, but will it actually be smaller after compression? xor eax, eax rep stosd ; clear the workspace of the new voice, retriggering oscillators jmp short su_update_voices_skipadd diff --git a/vm/compiler/templates/amd64-386/sources.asm b/vm/compiler/templates/amd64-386/sources.asm index e0bc1db..85e48ff 100644 --- a/vm/compiler/templates/amd64-386/sources.asm +++ b/vm/compiler/templates/amd64-386/sources.asm @@ -15,9 +15,9 @@ ret su_op_envelope_mono: {{- end}} - mov eax, dword [{{.INP}}-su_voice.inputs+su_voice.release] ; eax = su_instrument.release - test eax, eax ; if (eax == 0) - je su_op_envelope_process ; goto process + mov eax, dword [{{.INP}}-su_voice.inputs+su_voice.sustain] ; eax = su_instrument.sustain + test eax, eax ; if (eax != 0) + jne su_op_envelope_process ; goto process mov al, {{.InputNumber "envelope" "release"}} ; [state]=RELEASE mov dword [{{.WRK}}], eax ; note that mov al, XXX; mov ..., eax is less bytes than doing it directly su_op_envelope_process: diff --git a/vm/compiler/templates/amd64-386/structs.asm b/vm/compiler/templates/amd64-386/structs.asm index b0b11cd..ca9ee73 100644 --- a/vm/compiler/templates/amd64-386/structs.asm +++ b/vm/compiler/templates/amd64-386/structs.asm @@ -12,7 +12,7 @@ endstruc ;------------------------------------------------------------------------------- struc su_voice .note resd 1 - .release resd 1 + .sustain resd 1 .inputs resd 8 .reserved resd 6 ; this is done to so the whole voice is 2^n long, see polyphonic player .workspace resb 63 * su_unit.size diff --git a/vm/compiler/templates/wasm/player.wat b/vm/compiler/templates/wasm/player.wat index 42d538c..b901287 100644 --- a/vm/compiler/templates/wasm/player.wat +++ b/vm/compiler/templates/wasm/player.wat @@ -272,7 +272,7 @@ ) (i32.const 4096) ) - (i32.const 1) + (i32.const 0) ) ;; release the note (if (i32.gt_u (local.get $note) (i32.const {{.Hold}}))(then (local.set $di (i32.add @@ -290,6 +290,7 @@ )) (memory.fill (local.get $di) (i32.const 0) (i32.const 4096)) (i32.store (local.get $di) (local.get $note)) + (i32.store offset=4 (local.get $di) (local.get $note)) (i32.store8 offset={{index .Labels "su_trackcurrentvoice"}} (local.get $tracksRemaining) (local.get $voiceNo)) )) )) @@ -311,10 +312,11 @@ (i32.load8_u offset={{index .Labels "su_patterns"}}) (local.tee $note) (if (i32.ne (i32.const {{.Hold}}))(then - (i32.store offset=4 (local.get $di) (i32.const 1)) ;; release the note + (i32.store offset=4 (local.get $di) (i32.const 0)) ;; release the note (if (i32.gt_u (local.get $note) (i32.const {{.Hold}}))(then (memory.fill (local.get $di) (i32.const 0) (i32.const 4096)) (i32.store (local.get $di) (local.get $note)) + (i32.store offset=4 (local.get $di) (local.get $note)) )) )) (local.set $di (i32.add (local.get $di) (i32.const 4096))) diff --git a/vm/compiler/templates/wasm/sources.wat b/vm/compiler/templates/wasm/sources.wat index 09c2c19..cd7d10a 100644 --- a/vm/compiler/templates/wasm/sources.wat +++ b/vm/compiler/templates/wasm/sources.wat @@ -30,7 +30,7 @@ ;; Stereo: push the envelope valeu on stack twice ;;------------------------------------------------------------------------------- (func $su_op_envelope (param $stereo i32) (local $state i32) (local $level f32) (local $delta f32) - (if (i32.load offset=4 (global.get $voice)) (then ;; if voice.release > 0 + (if (i32.eqz (i32.load offset=4 (global.get $voice))) (then ;; if voice.sustain == 0 (i32.store (global.get $WRK) (i32.const {{.InputNumber "envelope" "release"}})) ;; set envelope state to release )) (local.set $state (i32.load (global.get $WRK))) diff --git a/vm/interpreter.go b/vm/interpreter.go index d46b560..8ebbd27 100644 --- a/vm/interpreter.go +++ b/vm/interpreter.go @@ -41,7 +41,7 @@ type unit struct { type voice struct { note byte - release bool + sustain bool units [MAX_UNITS]unit } @@ -105,10 +105,11 @@ func (s SynthService) Compile(patch sointu.Patch, bpm int) (sointu.Synth, error) func (s *Interpreter) Trigger(voiceIndex int, note byte) { s.synth.voices[voiceIndex] = voice{} s.synth.voices[voiceIndex].note = note + s.synth.voices[voiceIndex].sustain = true } func (s *Interpreter) Release(voiceIndex int) { - s.synth.voices[voiceIndex].release = true + s.synth.voices[voiceIndex].sustain = false } func (s *Interpreter) Update(patch sointu.Patch, bpm int) error { @@ -297,7 +298,7 @@ func (s *Interpreter) Render(buffer []float32, maxtime int) (samples int, time i stack = append(stack, synth.outputs[channel]) synth.outputs[channel] = 0 case opEnvelope: - if voices[0].release { + if !voices[0].sustain { unit.state[0] = envStateRelease // set state to release } state := unit.state[0]