From 0a67129a0c9b0728ac1b6bcca6a99b4706d38d0c Mon Sep 17 00:00:00 2001 From: "5684185+vsariola@users.noreply.github.com" <5684185+vsariola@users.noreply.github.com> Date: Wed, 18 Oct 2023 17:32:13 +0300 Subject: [PATCH] refactor!: rename SynthService to Synther and related types The -er suffix is more idiomatic for single method interfaces, and the interface is not doing much more than converting the patch to a synth. Names were updated throughout the project to reflect this change. In particular, the "Service" in SynthService was not telling anything helpful. --- ...rvice_native.go => main_synther_native.go} | 2 +- ...ce_other.go => main_synther_not_native.go} | 2 +- cmd/sointu-play/main.go | 2 +- cmd/sointu-track/main.go | 4 ++-- cmd/sointu-vsti/main.go | 4 ++-- synth.go | 12 +++++------ tracker/gioui/files.go | 2 +- tracker/gioui/tracker.go | 6 +++--- tracker/player.go | 10 +++++----- .../bridge/{bridge.go => native_synth.go} | 18 ++++++++--------- .../{bridge_test.go => native_synth_test.go} | 4 ++-- vm/{interpreter.go => go_synth.go} | 20 +++++++++---------- vm/{interpreter_test.go => go_synth_test.go} | 2 +- 13 files changed, 44 insertions(+), 44 deletions(-) rename cmd/{default_service_native.go => main_synther_native.go} (66%) rename cmd/{default_service_other.go => main_synther_not_native.go} (65%) rename vm/compiler/bridge/{bridge.go => native_synth.go} (92%) rename vm/compiler/bridge/{bridge_test.go => native_synth_test.go} (98%) rename vm/{interpreter.go => go_synth.go} (95%) rename vm/{interpreter_test.go => go_synth_test.go} (98%) diff --git a/cmd/default_service_native.go b/cmd/main_synther_native.go similarity index 66% rename from cmd/default_service_native.go rename to cmd/main_synther_native.go index ca25034..10f07e0 100644 --- a/cmd/default_service_native.go +++ b/cmd/main_synther_native.go @@ -4,4 +4,4 @@ package cmd import "github.com/vsariola/sointu/vm/compiler/bridge" -var DefaultService = bridge.BridgeService{} +var MainSynther = bridge.NativeSynther{} diff --git a/cmd/default_service_other.go b/cmd/main_synther_not_native.go similarity index 65% rename from cmd/default_service_other.go rename to cmd/main_synther_not_native.go index 7415688..f4a1c65 100644 --- a/cmd/default_service_other.go +++ b/cmd/main_synther_not_native.go @@ -4,4 +4,4 @@ package cmd import "github.com/vsariola/sointu/vm" -var DefaultService = vm.SynthService{} +var MainSynther = vm.GoSynther{} diff --git a/cmd/sointu-play/main.go b/cmd/sointu-play/main.go index fce0aa7..0bb5761 100644 --- a/cmd/sointu-play/main.go +++ b/cmd/sointu-play/main.go @@ -88,7 +88,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.BridgeService{}, song, !*unreleased) // render the song to calculate its length + buffer, err := sointu.Play(bridge.NativeSynther{}, song, !*unreleased) // render the song to calculate its length if err != nil { return fmt.Errorf("sointu.Play failed: %v", err) } diff --git a/cmd/sointu-track/main.go b/cmd/sointu-track/main.go index b9680f3..6583b1b 100644 --- a/cmd/sointu-track/main.go +++ b/cmd/sointu-track/main.go @@ -57,8 +57,8 @@ func main() { recoveryFile = filepath.Join(configDir, "Sointu", "sointu-track-recovery") } model := tracker.NewModel(modelMessages, playerMessages, recoveryFile) - player := tracker.NewPlayer(cmd.DefaultService, playerMessages, modelMessages) - tracker := gioui.NewTracker(model, cmd.DefaultService) + player := tracker.NewPlayer(cmd.MainSynther, playerMessages, modelMessages) + tracker := gioui.NewTracker(model, cmd.MainSynther) output := audioContext.Output() defer output.Close() go func() { diff --git a/cmd/sointu-vsti/main.go b/cmd/sointu-vsti/main.go index 4f3670e..1842c71 100644 --- a/cmd/sointu-vsti/main.go +++ b/cmd/sointu-vsti/main.go @@ -62,8 +62,8 @@ func init() { recoveryFile = filepath.Join(configDir, "Sointu", "sointu-vsti-recovery-"+hex.EncodeToString(randBytes)) } model := tracker.NewModel(modelMessages, playerMessages, recoveryFile) - player := tracker.NewPlayer(cmd.DefaultService, playerMessages, modelMessages) - tracker := gioui.NewTracker(model, cmd.DefaultService) + player := tracker.NewPlayer(cmd.MainSynther, playerMessages, modelMessages) + tracker := gioui.NewTracker(model, cmd.MainSynther) tracker.SetInstrEnlarged(true) // start the vsti with the instrument editor enlarged go tracker.Main() context := VSTIProcessContext{make([]vst2.MIDIEvent, 100), h} diff --git a/synth.go b/synth.go index fde22b7..215c698 100644 --- a/synth.go +++ b/synth.go @@ -33,10 +33,10 @@ type ( Release(voice int) } - // SynthService compiles a given Patch into a Synth, throwing errors if the + // Synther compiles a given Patch into a Synth, throwing errors if the // Patch is malformed. - SynthService interface { - Compile(patch Patch, bpm int) (Synth, error) + Synther interface { + Synth(patch Patch, bpm int) (Synth, error) } ) @@ -53,18 +53,18 @@ func Render(synth Synth, buffer AudioBuffer) error { return nil } -// Play plays the Song using a given SynthService, returning the stereo audio +// Play plays the Song using a given Synther, returning the stereo audio // buffer and the sync buffer as a result (and possible errors). Passing // 'release' as true means that all the notes are released when the synth is // created. The default behaviour during runtime rendering is to leave them // playing, meaning that envelopes start attacking right away unless an explicit // note release is put to every track. -func Play(synthService SynthService, song Song, release bool) (AudioBuffer, error) { +func Play(synther Synther, song Song, release bool) (AudioBuffer, error) { err := song.Validate() if err != nil { return nil, err } - synth, err := synthService.Compile(song.Patch, song.BPM) + synth, err := synther.Synth(song.Patch, song.BPM) if err != nil { return nil, fmt.Errorf("sointu.Play failed: %v", err) } diff --git a/tracker/gioui/files.go b/tracker/gioui/files.go index 643de0f..b211609 100644 --- a/tracker/gioui/files.go +++ b/tracker/gioui/files.go @@ -140,7 +140,7 @@ func (t *Tracker) saveSong(w io.WriteCloser) bool { } func (t *Tracker) exportWav(w io.WriteCloser, pcm16 bool) { - data, err := sointu.Play(t.synthService, t.Song(), true) // render the song to calculate its length + data, err := sointu.Play(t.synther, t.Song(), true) // render the song to calculate its length if err != nil { t.Alert.Update(fmt.Sprintf("Error rendering the song during export: %v", err), Error, time.Second*3) return diff --git a/tracker/gioui/tracker.go b/tracker/gioui/tracker.go index 1881e0f..68313da 100644 --- a/tracker/gioui/tracker.go +++ b/tracker/gioui/tracker.go @@ -64,7 +64,7 @@ type Tracker struct { quitted bool unmarshalRecoveryChannel chan []byte marshalRecoveryChannel chan (chan []byte) - synthService sointu.SynthService + synther sointu.Synther *trackerModel } @@ -116,7 +116,7 @@ func (t *Tracker) UnmarshalContent(bytes []byte) error { return errors.New("was able to unmarshal a song, but the bpm was 0") } -func NewTracker(model *tracker.Model, synthService sointu.SynthService) *Tracker { +func NewTracker(model *tracker.Model, synther sointu.Synther) *Tracker { t := &Tracker{ Theme: material.NewTheme(), BPM: new(NumberInput), @@ -146,7 +146,7 @@ func NewTracker(model *tracker.Model, synthService sointu.SynthService) *Tracker TrackEditor: NewTrackEditor(), errorChannel: make(chan error, 32), - synthService: synthService, + synther: synther, trackerModel: model, marshalRecoveryChannel: make(chan (chan []byte)), diff --git a/tracker/player.go b/tracker/player.go index fbef8ed..71a516f 100644 --- a/tracker/player.go +++ b/tracker/player.go @@ -29,7 +29,7 @@ type ( recordingFrames int recordingEvents []PlayerProcessEvent - synthService sointu.SynthService + synther sointu.Synther playerMessages chan<- PlayerMessage modelMessages <-chan interface{} } @@ -85,11 +85,11 @@ type ( const NUM_RENDER_TRIES = 10000 -func NewPlayer(synthService sointu.SynthService, playerMessages chan<- PlayerMessage, modelMessages <-chan interface{}) *Player { +func NewPlayer(synther sointu.Synther, playerMessages chan<- PlayerMessage, modelMessages <-chan interface{}) *Player { p := &Player{ playerMessages: playerMessages, modelMessages: modelMessages, - synthService: synthService, + synther: synther, volume: Volume{Average: [2]float64{1e-9, 1e-9}, Peak: [2]float64{1e-9, 1e-9}}, } return p @@ -308,10 +308,10 @@ func (p *Player) compileOrUpdateSynth() { } } else { var err error - p.synth, err = p.synthService.Compile(p.patch, p.bpm) + p.synth, err = p.synther.Synth(p.patch, p.bpm) if err != nil { p.synth = nil - p.trySend(PlayerCrashMessage{fmt.Errorf("synthService.Compile: %w", err)}) + p.trySend(PlayerCrashMessage{fmt.Errorf("synther.Synth: %w", err)}) return } for i := 0; i < 32; i++ { diff --git a/vm/compiler/bridge/bridge.go b/vm/compiler/bridge/native_synth.go similarity index 92% rename from vm/compiler/bridge/bridge.go rename to vm/compiler/bridge/native_synth.go index 1cca9a7..3d3d365 100644 --- a/vm/compiler/bridge/bridge.go +++ b/vm/compiler/bridge/native_synth.go @@ -13,17 +13,17 @@ import ( "github.com/vsariola/sointu/vm" ) -type BridgeService struct { +type NativeSynther struct { } -type BridgeSynth C.Synth +type NativeSynth C.Synth -func (s BridgeService) Compile(patch sointu.Patch, bpm int) (sointu.Synth, error) { +func (s NativeSynther) Synth(patch sointu.Patch, bpm int) (sointu.Synth, error) { synth, err := Synth(patch, bpm) return synth, err } -func Synth(patch sointu.Patch, bpm int) (*BridgeSynth, error) { +func Synth(patch sointu.Patch, bpm int) (*NativeSynth, error) { s := new(C.Synth) if n := patch.NumDelayLines(); n > 64 { return nil, fmt.Errorf("native bridge has currently a hard limit of 64 delaylines; patch uses %v", n) @@ -55,7 +55,7 @@ func Synth(patch sointu.Patch, bpm int) (*BridgeSynth, error) { s.NumVoices = C.uint(comPatch.NumVoices) s.Polyphony = C.uint(comPatch.PolyphonyBitmask) s.RandSeed = 1 - return (*BridgeSynth)(s), nil + return (*NativeSynth)(s), nil } // Render renders until the buffer is full or the modulated time is reached, whichever @@ -79,7 +79,7 @@ func Synth(patch sointu.Patch, bpm int) (*BridgeSynth, 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 (bridgesynth *BridgeSynth) Render(buffer sointu.AudioBuffer, maxtime int) (int, int, error) { +func (bridgesynth *NativeSynth) Render(buffer sointu.AudioBuffer, maxtime int) (int, int, error) { synth := (*C.Synth)(bridgesynth) // TODO: syncBuffer is not getting passed to cgo; do we want to even try to support the syncing with the native bridge if len(buffer)%1 == 1 { @@ -95,7 +95,7 @@ func (bridgesynth *BridgeSynth) Render(buffer sointu.AudioBuffer, maxtime int) ( } // Trigger is part of C.Synths' implementation of sointu.Synth interface -func (bridgesynth *BridgeSynth) Trigger(voice int, note byte) { +func (bridgesynth *NativeSynth) Trigger(voice int, note byte) { s := (*C.Synth)(bridgesynth) if voice < 0 || voice >= len(s.SynthWrk.Voices) { return @@ -106,7 +106,7 @@ func (bridgesynth *BridgeSynth) Trigger(voice int, note byte) { } // Release is part of C.Synths' implementation of sointu.Synth interface -func (bridgesynth *BridgeSynth) Release(voice int) { +func (bridgesynth *NativeSynth) Release(voice int) { s := (*C.Synth)(bridgesynth) if voice < 0 || voice >= len(s.SynthWrk.Voices) { return @@ -115,7 +115,7 @@ func (bridgesynth *BridgeSynth) Release(voice int) { } // Update -func (bridgesynth *BridgeSynth) Update(patch sointu.Patch, bpm int) error { +func (bridgesynth *NativeSynth) Update(patch sointu.Patch, bpm int) error { s := (*C.Synth)(bridgesynth) if n := patch.NumDelayLines(); n > 64 { return fmt.Errorf("native bridge has currently a hard limit of 64 delaylines; patch uses %v", n) diff --git a/vm/compiler/bridge/bridge_test.go b/vm/compiler/bridge/native_synth_test.go similarity index 98% rename from vm/compiler/bridge/bridge_test.go rename to vm/compiler/bridge/native_synth_test.go index 1e8fa13..5654b32 100644 --- a/vm/compiler/bridge/bridge_test.go +++ b/vm/compiler/bridge/native_synth_test.go @@ -40,7 +40,7 @@ func TestOscillatSine(t *testing.T) { }}} tracks := []sointu.Track{{NumVoices: 1, Order: []int{0}, Patterns: []sointu.Pattern{{64, 0, 68, 0, 32, 0, 0, 0, 75, 0, 78, 0, 0, 0, 0, 0}}}} song := sointu.Song{BPM: 100, RowsPerBeat: 4, Score: sointu.Score{RowsPerPattern: 16, Length: 1, Tracks: tracks}, Patch: patch} - buffer, err := sointu.Play(bridge.BridgeService{}, song, false) + buffer, err := sointu.Play(bridge.NativeSynther{}, song, false) if err != nil { t.Fatalf("Render failed: %v", err) } @@ -95,7 +95,7 @@ func TestAllRegressionTests(t *testing.T) { if err != nil { t.Fatalf("could not parse the .yml file: %v", err) } - buffer, err := sointu.Play(bridge.BridgeService{}, song, false) + buffer, err := sointu.Play(bridge.NativeSynther{}, song, false) buffer = buffer[:song.Score.LengthInRows()*song.SamplesPerRow()] // extend to the nominal length always. if err != nil { t.Fatalf("Play failed: %v", err) diff --git a/vm/interpreter.go b/vm/go_synth.go similarity index 95% rename from vm/interpreter.go rename to vm/go_synth.go index 6eba690..b99399f 100644 --- a/vm/interpreter.go +++ b/vm/go_synth.go @@ -13,22 +13,22 @@ import ( //go:generate go run generate/generate.go -// Interpreter is a pure-Go bytecode interpreter for the Sointu VM bytecode. It +// GoSynth is a pure-Go bytecode interpreter for the Sointu VM bytecode. It // can only simulate bytecode compiled for AllFeatures, as the opcodes hard // coded in it for speed. If you are interested exactly how opcodes / units -// work, studying Interpreter.Render is a good place to start. +// work, studying GoSynth.Render is a good place to start. // // Internally, it uses software stack with practically no limitations in the // number of signals, so be warned that if you compose patches for it, they // might not work with the x87 implementation, as it has only 8-level stack. -type Interpreter struct { +type GoSynth struct { bytePatch BytePatch stack []float32 synth synth delaylines []delayline } -type SynthService struct { +type GoSynther struct { } const MAX_VOICES = 32 @@ -92,27 +92,27 @@ func Synth(patch sointu.Patch, bpm int) (sointu.Synth, error) { if err != nil { return nil, fmt.Errorf("error compiling %v", err) } - ret := &Interpreter{bytePatch: *bytePatch, stack: make([]float32, 0, 4), delaylines: make([]delayline, patch.NumDelayLines())} + ret := &GoSynth{bytePatch: *bytePatch, stack: make([]float32, 0, 4), delaylines: make([]delayline, patch.NumDelayLines())} ret.synth.randSeed = 1 return ret, nil } -func (s SynthService) Compile(patch sointu.Patch, bpm int) (sointu.Synth, error) { +func (s GoSynther) Synth(patch sointu.Patch, bpm int) (sointu.Synth, error) { synth, err := Synth(patch, bpm) return synth, err } -func (s *Interpreter) Trigger(voiceIndex int, note byte) { +func (s *GoSynth) 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) { +func (s *GoSynth) Release(voiceIndex int) { s.synth.voices[voiceIndex].sustain = false } -func (s *Interpreter) Update(patch sointu.Patch, bpm int) error { +func (s *GoSynth) Update(patch sointu.Patch, bpm int) error { bytePatch, err := Encode(patch, AllFeatures{}, bpm) if err != nil { return fmt.Errorf("error compiling %v", err) @@ -140,7 +140,7 @@ func (s *Interpreter) Update(patch sointu.Patch, bpm int) error { return nil } -func (s *Interpreter) Render(buffer sointu.AudioBuffer, maxtime int) (samples int, time int, renderError error) { +func (s *GoSynth) Render(buffer sointu.AudioBuffer, maxtime int) (samples int, time int, renderError error) { defer func() { if err := recover(); err != nil { renderError = fmt.Errorf("render panicced: %v", err) diff --git a/vm/interpreter_test.go b/vm/go_synth_test.go similarity index 98% rename from vm/interpreter_test.go rename to vm/go_synth_test.go index 0f6d0a1..1dece5a 100644 --- a/vm/interpreter_test.go +++ b/vm/go_synth_test.go @@ -43,7 +43,7 @@ func TestAllRegressionTests(t *testing.T) { if err != nil { t.Fatalf("could not parse the .yml file: %v", err) } - buffer, err := sointu.Play(vm.SynthService{}, song, false) + buffer, err := sointu.Play(vm.GoSynther{}, song, false) buffer = buffer[:song.Score.LengthInRows()*song.SamplesPerRow()] // extend to the nominal length always. if err != nil { t.Fatalf("Play failed: %v", err)