From 9678108fd1dcf614ea09e670901b9eb9d583de2e Mon Sep 17 00:00:00 2001 From: qm210 Date: Tue, 22 Oct 2024 01:04:39 +0200 Subject: [PATCH] feat: envelopexp ported to go synth --- cmd/sointu-play/main.go | 4 ++-- vm/go_synth.go | 19 +++++++++++++------ 2 files changed, 15 insertions(+), 8 deletions(-) diff --git a/cmd/sointu-play/main.go b/cmd/sointu-play/main.go index aea297a..52359e3 100644 --- a/cmd/sointu-play/main.go +++ b/cmd/sointu-play/main.go @@ -4,6 +4,7 @@ import ( "encoding/json" "flag" "fmt" + "github.com/vsariola/sointu/cmd" "io/ioutil" "os" "path/filepath" @@ -14,7 +15,6 @@ import ( "github.com/vsariola/sointu" "github.com/vsariola/sointu/oto" "github.com/vsariola/sointu/version" - "github.com/vsariola/sointu/vm/compiler/bridge" ) func main() { @@ -93,7 +93,7 @@ func main() { return fmt.Errorf("the song could not be parsed as .json (%v) or .yml (%v)", errJSON, errYaml) } } - buffer, err := sointu.Play(bridge.NativeSynther{}, song, nil) // render the song to calculate its length + buffer, err := sointu.Play(cmd.MainSynther, song, nil) // render the song to calculate its length if err != nil { return fmt.Errorf("sointu.Play failed: %v", err) } diff --git a/vm/go_synth.go b/vm/go_synth.go index ffcbdad..ecc2857 100644 --- a/vm/go_synth.go +++ b/vm/go_synth.go @@ -330,25 +330,27 @@ func (s *GoSynth) Render(buffer sointu.AudioBuffer, maxtime int) (samples int, t stack = append(stack, output) } case opEnvelopexp: - // TODO @qm210 - for now, this just clones the envelope so everything is wired up, but IT IS NOT IMPLEMENTED YET - // is ignoring for now - // - params[1] - attack shape parameter (center value should mean "linear") - // - params[3] - decay shape parameter (center value should mean "linear") if !voices[0].sustain { unit.state[0] = envStateRelease // set state to release } state := unit.state[0] level := unit.state[1] + exponent := float64(1) + baseline := float32(0) switch state { case envStateAttack: + exponent = scaledEnvelopExponent(params[1]) level += nonLinearMap(params[0]) if level >= 1 { level = 1 state = envStateDecay } case envStateDecay: + exponent = scaledEnvelopExponent(params[3]) + sustain := params[4] + baseline = sustain level -= nonLinearMap(params[2]) - if sustain := params[4]; level <= sustain { + if level <= sustain { level = sustain } case envStateRelease: @@ -359,7 +361,8 @@ func (s *GoSynth) Render(buffer sointu.AudioBuffer, maxtime int) (samples int, t } unit.state[0] = state unit.state[1] = level - output := level * params[6] + expLevel := float32(math.Pow(float64(level), exponent)) + output := (baseline + (1-baseline)*expLevel) * params[6] stack = append(stack, output) if stereo { stack = append(stack, output) @@ -646,6 +649,10 @@ func nonLinearMap(value float32) float32 { return float32(math.Exp2(float64(-24 * value))) } +func scaledEnvelopExponent(value float32) float64 { + return math.Pow(2, 6*(0.5-float64(value))) +} + func clip(value float32) float32 { if value < -1 { return -1