feat!: both native & Go synths are included in the same executables

Closes #200
This commit is contained in:
5684185+vsariola@users.noreply.github.com
2025-07-10 17:46:00 +03:00
parent 13102aa7d6
commit 3163f46447
21 changed files with 106 additions and 83 deletions

View File

@ -39,33 +39,19 @@ jobs:
strategy: strategy:
matrix: matrix:
config: config:
- os: windows-latest
asmnasm: C:\Users\runneradmin\nasm\nasm
output: sointu-track.exe
params: cmd/sointu-track/main.go
ldflags: -H=windowsgui
- os: windows-latest - os: windows-latest
asmnasm: C:\Users\runneradmin\nasm\nasm asmnasm: C:\Users\runneradmin\nasm\nasm
output: sointu-compile.exe output: sointu-compile.exe
params: cmd/sointu-compile/main.go params: cmd/sointu-compile/main.go
- os: windows-latest - os: windows-latest
asmnasm: C:\Users\runneradmin\nasm\nasm asmnasm: C:\Users\runneradmin\nasm\nasm
output: sointu-track-native.exe output: sointu-track.exe
params: -tags=native cmd/sointu-track/main.go params: -tags=native cmd/sointu-track/main.go
ldflags: -H=windowsgui ldflags: -H=windowsgui
- os: windows-latest - os: windows-latest
asmnasm: C:\Users\runneradmin\nasm\nasm asmnasm: C:\Users\runneradmin\nasm\nasm
output: sointu-vsti.dll output: sointu-vsti.dll
params: -buildmode=c-shared -tags=plugin ./cmd/sointu-vsti/
- os: windows-latest
asmnasm: C:\Users\runneradmin\nasm\nasm
output: sointu-vsti-native.dll
params: -buildmode=c-shared -tags="plugin,native" ./cmd/sointu-vsti/ params: -buildmode=c-shared -tags="plugin,native" ./cmd/sointu-vsti/
- os: ubuntu-latest
asmnasm: /home/runner/nasm/nasm
output: sointu-track
params: cmd/sointu-track/main.go
packages: libegl-dev libvulkan-dev libxkbcommon-x11-dev libwayland-dev libasound2-dev libx11-xcb-dev libxcursor-dev libxfixes-dev
- os: ubuntu-latest - os: ubuntu-latest
asmnasm: /home/runner/nasm/nasm asmnasm: /home/runner/nasm/nasm
output: sointu-compile output: sointu-compile
@ -73,43 +59,27 @@ jobs:
packages: libegl-dev libvulkan-dev libxkbcommon-x11-dev libwayland-dev libasound2-dev libx11-xcb-dev libxcursor-dev libxfixes-dev packages: libegl-dev libvulkan-dev libxkbcommon-x11-dev libwayland-dev libasound2-dev libx11-xcb-dev libxcursor-dev libxfixes-dev
- os: ubuntu-latest - os: ubuntu-latest
asmnasm: /home/runner/nasm/nasm asmnasm: /home/runner/nasm/nasm
output: sointu-track-native output: sointu-track
params: -tags=native cmd/sointu-track/main.go params: -tags=native cmd/sointu-track/main.go
packages: libegl-dev libvulkan-dev libxkbcommon-x11-dev libwayland-dev libasound2-dev libx11-xcb-dev libxcursor-dev libxfixes-dev packages: libegl-dev libvulkan-dev libxkbcommon-x11-dev libwayland-dev libasound2-dev libx11-xcb-dev libxcursor-dev libxfixes-dev
- os: ubuntu-latest - os: ubuntu-latest
asmnasm: /home/runner/nasm/nasm asmnasm: /home/runner/nasm/nasm
output: sointu-vsti.so output: sointu-vsti.so
params: -buildmode=c-shared -tags=plugin ./cmd/sointu-vsti/
packages: libegl-dev libvulkan-dev libxkbcommon-x11-dev libwayland-dev libasound2-dev libx11-xcb-dev libxcursor-dev libxfixes-dev
- os: ubuntu-latest
asmnasm: /home/runner/nasm/nasm
output: sointu-vsti-native.so
params: -buildmode=c-shared -tags="plugin,native" ./cmd/sointu-vsti/ params: -buildmode=c-shared -tags="plugin,native" ./cmd/sointu-vsti/
packages: libegl-dev libvulkan-dev libxkbcommon-x11-dev libwayland-dev libasound2-dev libx11-xcb-dev libxcursor-dev libxfixes-dev packages: libegl-dev libvulkan-dev libxkbcommon-x11-dev libwayland-dev libasound2-dev libx11-xcb-dev libxcursor-dev libxfixes-dev
- os: macos-latest
asmnasm: /Users/runner/nasm/nasm
output: sointu-track
params: cmd/sointu-track/main.go
- os: macos-latest - os: macos-latest
asmnasm: /Users/runner/nasm/nasm asmnasm: /Users/runner/nasm/nasm
output: sointu-compile output: sointu-compile
params: cmd/sointu-compile/main.go params: cmd/sointu-compile/main.go
- os: macos-13 - os: macos-13
asmnasm: /Users/runner/nasm/nasm asmnasm: /Users/runner/nasm/nasm
output: sointu-track-native output: sointu-track
params: -tags=native cmd/sointu-track/main.go params: -tags=native cmd/sointu-track/main.go
- os: macos-13 - os: macos-13
asmnasm: /Users/runner/nasm/nasm asmnasm: /Users/runner/nasm/nasm
output: sointu-vsti.a output: sointu-vsti.a
bundleoutput: sointu-vsti bundleoutput: sointu-vsti
MACOSX_DEPLOYMENT_TARGET: 11 MACOSX_DEPLOYMENT_TARGET: 11
params: -buildmode=c-archive -tags=plugin ./cmd/sointu-vsti/
bundle: true
- os: macos-13
asmnasm: /Users/runner/nasm/nasm
output: sointu-vsti-native.a
bundleoutput: sointu-vsti-native
MACOSX_DEPLOYMENT_TARGET: 11
params: -buildmode=c-archive -tags="plugin,native" ./cmd/sointu-vsti/ params: -buildmode=c-archive -tags="plugin,native" ./cmd/sointu-vsti/
bundle: true bundle: true
steps: steps:

View File

@ -109,6 +109,9 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
([#156][i156]) ([#156][i156])
### Changed ### Changed
- Native version of the tracker/VSTi was removed. Instead, you can change
between the two versions of the synth on the fly, by clicking on the "Synth"
option under the CPU group in the song panel ([#200][i200])
- Send amount defaults to 64 = 0.0 ([#178][i178]) - Send amount defaults to 64 = 0.0 ([#178][i178])
- BREAKING CHANGE: the negbandpass and neghighpass parameters of the filter unit - BREAKING CHANGE: the negbandpass and neghighpass parameters of the filter unit
were removed. Setting bandpass or highpass to -1 achieves now the same end were removed. Setting bandpass or highpass to -1 achieves now the same end
@ -360,3 +363,4 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
[i186]: https://github.com/vsariola/sointu/issues/186 [i186]: https://github.com/vsariola/sointu/issues/186
[i192]: https://github.com/vsariola/sointu/issues/192 [i192]: https://github.com/vsariola/sointu/issues/192
[i196]: https://github.com/vsariola/sointu/issues/196 [i196]: https://github.com/vsariola/sointu/issues/196
[i200]: https://github.com/vsariola/sointu/issues/200

View File

@ -82,7 +82,7 @@ for the audio, so the portability is currently limited by these.
#### Prerequisites #### Prerequisites
- [go](https://golang.org/) - [go](https://golang.org/)
- If you want to use the x86 assembly written synthesizer, to test that the - If you want to also use the x86 assembly written synthesizer, to test that the
patch also works once compiled: patch also works once compiled:
- Follow the instructions to build the [x86 native virtual machine](#native-virtual-machine) - Follow the instructions to build the [x86 native virtual machine](#native-virtual-machine)
before building the tracker. before building the tracker.
@ -110,7 +110,7 @@ go build -o sointu-track.exe cmd/sointu-track/main.go
On other platforms than Windows, replace `-o sointu-track.exe` with On other platforms than Windows, replace `-o sointu-track.exe` with
`-o sointu-track`. `-o sointu-track`.
If you want to use the [x86 native virtual machine](#native-virtual-machine), If you want to include the [x86 native virtual machine](#native-virtual-machine),
add `-tags=native` to all the commands e.g. add `-tags=native` to all the commands e.g.
``` ```
@ -133,7 +133,7 @@ a dynamically linked library and ran inside a VST host.
if it is not set and go fails to find the compiler, go just excludes if it is not set and go fails to find the compiler, go just excludes
all files with `import "C"` from the build, resulting in lots of all files with `import "C"` from the build, resulting in lots of
errors about missing types. errors about missing types.
- If you want to use the x86 assembly written synthesizer: - If you want to build the VSTI with the native x86 assembly written synthesizer:
- Follow the instructions to build the [x86 native virtual machine](#native-virtual-machine) - Follow the instructions to build the [x86 native virtual machine](#native-virtual-machine)
before building the plugin itself before building the plugin itself
@ -218,7 +218,9 @@ machine, through cgo, instead of using the Go written bytecode interpreter. With
the latest Go compiler, the native virtual machine is actually slower than the the latest Go compiler, the native virtual machine is actually slower than the
Go-written one, but importantly, the native virtual machine allows you to test Go-written one, but importantly, the native virtual machine allows you to test
that the patch also works within the stack limits of the x87 virtual machine, that the patch also works within the stack limits of the x87 virtual machine,
which is the VM used in the compiled intros. which is the VM used in the compiled intros. In the tracker/VSTi, you can switch
between the native synth and the Go synth under the CPU panel in the Song
settings.
Before you can actually run it, you need to build the bridge using CMake (thus, Before you can actually run it, you need to build the bridge using CMake (thus,
***this will not work with go get***). ***this will not work with go get***).

View File

@ -67,6 +67,7 @@ type (
// Synther 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. // Patch is malformed.
Synther interface { Synther interface {
Name() string // Name of the synther, e.g. "Go" or "Native"
Synth(patch Patch, bpm int) (Synth, error) Synth(patch Patch, bpm int) (Synth, error)
} }
) )

View File

@ -1,7 +0,0 @@
//go:build !native
package cmd
import "github.com/vsariola/sointu/vm"
var MainSynther = vm.GoSynther{}

View File

@ -4,12 +4,13 @@ import (
"encoding/json" "encoding/json"
"flag" "flag"
"fmt" "fmt"
"github.com/vsariola/sointu/cmd"
"io/ioutil" "io/ioutil"
"os" "os"
"path/filepath" "path/filepath"
"strings" "strings"
"github.com/vsariola/sointu/cmd"
"gopkg.in/yaml.v3" "gopkg.in/yaml.v3"
"github.com/vsariola/sointu" "github.com/vsariola/sointu"
@ -29,12 +30,20 @@ func main() {
wavOut := flag.Bool("w", false, "Output the rendered song as .wav file. By default, saves stereo float32 buffer to disk.") wavOut := flag.Bool("w", false, "Output the rendered song as .wav file. By default, saves stereo float32 buffer to disk.")
pcm := flag.Bool("c", false, "Convert audio to 16-bit signed PCM when outputting.") pcm := flag.Bool("c", false, "Convert audio to 16-bit signed PCM when outputting.")
versionFlag := flag.Bool("v", false, "Print version.") versionFlag := flag.Bool("v", false, "Print version.")
syntherInt := flag.Int("synth", 0, "Select the synther to use. By default, uses the first one in the list of available synthers.")
flag.Usage = printUsage flag.Usage = printUsage
flag.Parse() flag.Parse()
if *versionFlag { if *versionFlag {
fmt.Println(version.VersionOrHash) fmt.Println(version.VersionOrHash)
os.Exit(0) os.Exit(0)
} }
if *syntherInt < 0 || *syntherInt >= len(cmd.Synthers) {
fmt.Fprintf(os.Stderr, "synth index %d is out of range; available synthers:\n", *syntherInt)
for i, s := range cmd.Synthers {
fmt.Fprintf(os.Stderr, " %d: %s\n", i, s.Name())
}
os.Exit(1)
}
if flag.NArg() == 0 || *help { if flag.NArg() == 0 || *help {
flag.Usage() flag.Usage()
os.Exit(0) os.Exit(0)
@ -93,7 +102,7 @@ func main() {
return fmt.Errorf("the song could not be parsed as .json (%v) or .yml (%v)", errJSON, errYaml) return fmt.Errorf("the song could not be parsed as .json (%v) or .yml (%v)", errJSON, errYaml)
} }
} }
buffer, err := sointu.Play(cmd.MainSynther, song, nil) // render the song to calculate its length buffer, err := sointu.Play(cmd.Synthers[*syntherInt], song, nil) // render the song to calculate its length
if err != nil { if err != nil {
return fmt.Errorf("sointu.Play failed: %v", err) return fmt.Errorf("sointu.Play failed: %v", err)
} }

View File

@ -49,8 +49,8 @@ func main() {
midiContext := cmd.NewMidiContext(broker) midiContext := cmd.NewMidiContext(broker)
defer midiContext.Close() defer midiContext.Close()
midiContext.TryToOpenBy(*defaultMidiInput, *firstMidiInput) midiContext.TryToOpenBy(*defaultMidiInput, *firstMidiInput)
model := tracker.NewModel(broker, cmd.MainSynther, midiContext, recoveryFile) model := tracker.NewModel(broker, cmd.Synthers, midiContext, recoveryFile)
player := tracker.NewPlayer(broker, cmd.MainSynther) player := tracker.NewPlayer(broker, cmd.Synthers[0])
detector := tracker.NewDetector(broker) detector := tracker.NewDetector(broker)
go detector.Run() go detector.Run()

View File

@ -46,8 +46,8 @@ func init() {
recoveryFile = filepath.Join(configDir, "sointu", "sointu-vsti-recovery-"+hex.EncodeToString(randBytes)) recoveryFile = filepath.Join(configDir, "sointu", "sointu-vsti-recovery-"+hex.EncodeToString(randBytes))
} }
broker := tracker.NewBroker() broker := tracker.NewBroker()
model := tracker.NewModel(broker, cmd.MainSynther, cmd.NewMidiContext(broker), recoveryFile) model := tracker.NewModel(broker, cmd.Synthers, cmd.NewMidiContext(broker), recoveryFile)
player := tracker.NewPlayer(broker, cmd.MainSynther) player := tracker.NewPlayer(broker, cmd.Synthers[0])
detector := tracker.NewDetector(broker) detector := tracker.NewDetector(broker)
go detector.Run() go detector.Run()
@ -63,11 +63,11 @@ func init() {
buf := make(sointu.AudioBuffer, 1024) buf := make(sointu.AudioBuffer, 1024)
var totalFrames int64 = 0 var totalFrames int64 = 0
return vst2.Plugin{ return vst2.Plugin{
UniqueID: PLUGIN_ID, UniqueID: [4]byte{'S', 'n', 't', 'u'},
Version: version, Version: version,
InputChannels: 0, InputChannels: 0,
OutputChannels: 2, OutputChannels: 2,
Name: PLUGIN_NAME, Name: "Sointu",
Vendor: "vsariola/sointu", Vendor: "vsariola/sointu",
Category: vst2.PluginCategorySynth, Category: vst2.PluginCategorySynth,
Flags: vst2.PluginIsSynth, Flags: vst2.PluginIsSynth,

View File

@ -1,6 +0,0 @@
//go:build native
package main
var PLUGIN_ID = [4]byte{'S', 'n', 't', 'N'}
var PLUGIN_NAME = "Sointu Native"

View File

@ -1,6 +0,0 @@
//go:build !native
package main
var PLUGIN_ID = [4]byte{'S', 'n', 't', 'u'}
var PLUGIN_NAME = "Sointu"

10
cmd/synthers.go Normal file
View File

@ -0,0 +1,10 @@
package cmd
import (
"github.com/vsariola/sointu"
"github.com/vsariola/sointu/vm"
)
var Synthers = []sointu.Synther{
vm.GoSynther{},
}

View File

@ -4,4 +4,6 @@ package cmd
import "github.com/vsariola/sointu/vm/compiler/bridge" import "github.com/vsariola/sointu/vm/compiler/bridge"
var MainSynther = bridge.NativeSynther{} func init() {
Synthers = append(Synthers, bridge.NativeSynther{})
}

View File

@ -82,7 +82,7 @@ func (m *Model) WriteWav(w io.WriteCloser, pcm16 bool) {
b := make([]byte, 32+2) b := make([]byte, 32+2)
rand.Read(b) rand.Read(b)
name := fmt.Sprintf("%x", b)[2 : 32+2] name := fmt.Sprintf("%x", b)[2 : 32+2]
data, err := sointu.Play(m.synther, song, func(p float32) { data, err := sointu.Play(m.synthers[m.syntherIndex], song, func(p float32) {
txt := fmt.Sprintf("Exporting song: %.0f%%", p*100) txt := fmt.Sprintf("Exporting song: %.0f%%", p*100)
TrySend(m.broker.ToModel, MsgToModel{Data: Alert{Message: txt, Priority: Info, Name: name, Duration: defaultAlertDuration}}) TrySend(m.broker.ToModel, MsgToModel{Data: Alert{Message: txt, Priority: Info, Name: name, Duration: defaultAlertDuration}})
}) // render the song to calculate its length }) // render the song to calculate its length

View File

@ -21,9 +21,11 @@ type SongPanel struct {
ScopeExpander *Expander ScopeExpander *Expander
LoudnessExpander *Expander LoudnessExpander *Expander
PeakExpander *Expander PeakExpander *Expander
CPUExpander *Expander
WeightingTypeBtn *Clickable WeightingTypeBtn *Clickable
OversamplingBtn *Clickable OversamplingBtn *Clickable
SynthBtn *Clickable
BPM *NumericUpDownState BPM *NumericUpDownState
RowsPerPattern *NumericUpDownState RowsPerPattern *NumericUpDownState
@ -50,11 +52,13 @@ func NewSongPanel(tr *Tracker) *SongPanel {
WeightingTypeBtn: new(Clickable), WeightingTypeBtn: new(Clickable),
OversamplingBtn: new(Clickable), OversamplingBtn: new(Clickable),
SynthBtn: new(Clickable),
SongSettingsExpander: &Expander{Expanded: true}, SongSettingsExpander: &Expander{Expanded: true},
ScopeExpander: &Expander{}, ScopeExpander: &Expander{},
LoudnessExpander: &Expander{}, LoudnessExpander: &Expander{},
PeakExpander: &Expander{}, PeakExpander: &Expander{},
CPUExpander: &Expander{},
} }
return ret return ret
} }
@ -66,6 +70,10 @@ func (s *SongPanel) Update(gtx C, t *Tracker) {
for s.OversamplingBtn.Clicked(gtx) { for s.OversamplingBtn.Clicked(gtx) {
t.Model.Oversampling().SetValue(!t.Oversampling().Value()) t.Model.Oversampling().SetValue(!t.Oversampling().Value())
} }
for s.SynthBtn.Clicked(gtx) {
r := t.Model.SyntherIndex().Range()
t.Model.SyntherIndex().SetValue((t.SyntherIndex().Value()+1)%(r.Max-r.Min+1) + r.Min)
}
} }
func (s *SongPanel) Layout(gtx C) D { func (s *SongPanel) Layout(gtx C) D {
@ -102,6 +110,14 @@ func (t *SongPanel) layoutSongOptions(gtx C) D {
} }
oversamplingBtn := Btn(tr.Theme, &tr.Theme.Button.Text, t.OversamplingBtn, oversamplingTxt, "") oversamplingBtn := Btn(tr.Theme, &tr.Theme.Button.Text, t.OversamplingBtn, oversamplingTxt, "")
cpuload := tr.Model.CPULoad()
cpuLabel := Label(tr.Theme, &tr.Theme.SongPanel.RowValue, fmt.Sprintf("%.0f %%", cpuload*100))
if cpuload >= 1 {
cpuLabel.Color = tr.Theme.SongPanel.ErrorColor
}
synthBtn := Btn(tr.Theme, &tr.Theme.Button.Text, t.SynthBtn, tr.Model.SyntherName(), "")
return layout.Flex{Axis: layout.Vertical}.Layout(gtx, return layout.Flex{Axis: layout.Vertical}.Layout(gtx,
layout.Rigid(func(gtx C) D { layout.Rigid(func(gtx C) D {
return t.SongSettingsExpander.Layout(gtx, tr.Theme, "Song", return t.SongSettingsExpander.Layout(gtx, tr.Theme, "Song",
@ -130,17 +146,19 @@ func (t *SongPanel) layoutSongOptions(gtx C) D {
step := NumUpDown(tr.Step(), tr.Theme, t.Step, "Cursor step") step := NumUpDown(tr.Step(), tr.Theme, t.Step, "Cursor step")
return layoutSongOptionRow(gtx, tr.Theme, "Cursor step", step.Layout) return layoutSongOptionRow(gtx, tr.Theme, "Cursor step", step.Layout)
}), }),
layout.Rigid(func(gtx C) D {
cpuload := tr.Model.CPULoad()
label := Label(tr.Theme, &tr.Theme.SongPanel.RowValue, fmt.Sprintf("%.0f %%", cpuload*100))
if cpuload >= 1 {
label.Color = tr.Theme.SongPanel.ErrorColor
}
return layoutSongOptionRow(gtx, tr.Theme, "CPU load", label.Layout)
}),
) )
}) })
}), }),
layout.Rigid(func(gtx C) D {
return t.CPUExpander.Layout(gtx, tr.Theme, "CPU", cpuLabel.Layout,
func(gtx C) D {
return layout.Flex{Axis: layout.Vertical, Alignment: layout.End}.Layout(gtx,
layout.Rigid(func(gtx C) D { return layoutSongOptionRow(gtx, tr.Theme, "Load", cpuLabel.Layout) }),
layout.Rigid(func(gtx C) D { return layoutSongOptionRow(gtx, tr.Theme, "Synth", synthBtn.Layout) }),
)
},
)
}),
layout.Rigid(func(gtx C) D { layout.Rigid(func(gtx C) D {
return t.LoudnessExpander.Layout(gtx, tr.Theme, "Loudness", return t.LoudnessExpander.Layout(gtx, tr.Theme, "Loudness",
func(gtx C) D { func(gtx C) D {

View File

@ -113,7 +113,6 @@ func NewTracker(model *tracker.Model) *Tracker {
Duration: 10 * time.Second, Duration: 10 * time.Second,
}) })
} }
t.TrackEditor.scrollTable.Focus()
return t return t
} }

View File

@ -33,6 +33,7 @@ type (
Step Model Step Model
Octave Model Octave Model
DetectorWeighting Model DetectorWeighting Model
SyntherIndex Model
) )
func MakeInt(value IntValue) Int { func MakeInt(value IntValue) Int {
@ -81,6 +82,7 @@ func (m *Model) RowsPerBeat() Int { return MakeInt((*RowsPerBeat)(m)) }
func (m *Model) Step() Int { return MakeInt((*Step)(m)) } func (m *Model) Step() Int { return MakeInt((*Step)(m)) }
func (m *Model) Octave() Int { return MakeInt((*Octave)(m)) } func (m *Model) Octave() Int { return MakeInt((*Octave)(m)) }
func (m *Model) DetectorWeighting() Int { return MakeInt((*DetectorWeighting)(m)) } func (m *Model) DetectorWeighting() Int { return MakeInt((*DetectorWeighting)(m)) }
func (m *Model) SyntherIndex() Int { return MakeInt((*SyntherIndex)(m)) }
// BeatsPerMinuteInt // BeatsPerMinuteInt
@ -205,3 +207,17 @@ func (v *TrackVoices) Range() IntRange {
} }
return IntRange{1, (*Model)(v).remainingVoices(v.linkInstrTrack, true) + v.d.Song.Score.Tracks[t].NumVoices} return IntRange{1, (*Model)(v).remainingVoices(v.linkInstrTrack, true) + v.d.Song.Score.Tracks[t].NumVoices}
} }
// SyntherIndex
func (v *SyntherIndex) Value() int { return v.syntherIndex }
func (v *SyntherIndex) Range() IntRange { return IntRange{0, len(v.synthers) - 1} }
func (v *Model) SyntherName() string { return v.synthers[v.syntherIndex].Name() }
func (v *SyntherIndex) SetValue(value int) bool {
if value < 0 || value >= len(v.synthers) {
return false
}
v.syntherIndex = value
TrySend(v.broker.ToPlayer, any(v.synthers[value]))
return true
}

View File

@ -77,7 +77,9 @@ type (
alerts []Alert alerts []Alert
dialog Dialog dialog Dialog
synther sointu.Synther // the synther used to create new synths
syntherIndex int // the index of the synther used to create new synths
synthers []sointu.Synther // the synther used to create new synths
broker *Broker broker *Broker
@ -171,9 +173,9 @@ func (m *Model) Quitted() bool { return m.quitted }
func (m *Model) DetectorResult() DetectorResult { return m.detectorResult } func (m *Model) DetectorResult() DetectorResult { return m.detectorResult }
// NewModelPlayer creates a new model and a player that communicates with it // NewModelPlayer creates a new model and a player that communicates with it
func NewModel(broker *Broker, synther sointu.Synther, midiContext MIDIContext, recoveryFilePath string) *Model { func NewModel(broker *Broker, synthers []sointu.Synther, midiContext MIDIContext, recoveryFilePath string) *Model {
m := new(Model) m := new(Model)
m.synther = synther m.synthers = synthers
m.MIDI = midiContext m.MIDI = midiContext
m.broker = broker m.broker = broker
m.d.Octave = 4 m.d.Octave = 4

View File

@ -7,6 +7,7 @@ import (
"io" "io"
"testing" "testing"
"github.com/vsariola/sointu"
"github.com/vsariola/sointu/tracker" "github.com/vsariola/sointu/tracker"
"github.com/vsariola/sointu/vm" "github.com/vsariola/sointu/vm"
) )
@ -251,10 +252,10 @@ func FuzzModel(f *testing.F) {
f.Add(seed) f.Add(seed)
f.Fuzz(func(t *testing.T, slice []byte) { f.Fuzz(func(t *testing.T, slice []byte) {
reader := bytes.NewReader(slice) reader := bytes.NewReader(slice)
synther := vm.GoSynther{} synthers := []sointu.Synther{vm.GoSynther{}}
broker := tracker.NewBroker() broker := tracker.NewBroker()
model := tracker.NewModel(broker, synther, tracker.NullMIDIContext{}, "") model := tracker.NewModel(broker, synthers, tracker.NullMIDIContext{}, "")
player := tracker.NewPlayer(broker, synther) player := tracker.NewPlayer(broker, synthers[0])
buf := make([][2]float32, 2048) buf := make([][2]float32, 2048)
closeChan := make(chan struct{}) closeChan := make(chan struct{})
go func() { go func() {

View File

@ -268,6 +268,10 @@ loop:
} }
p.recording = Recording{} // reset recording p.recording = Recording{} // reset recording
} }
case sointu.Synther:
p.synther = m
p.synth = nil
p.compileOrUpdateSynth()
default: default:
// ignore unknown messages // ignore unknown messages
} }

View File

@ -18,6 +18,8 @@ type NativeSynther struct {
type NativeSynth C.Synth type NativeSynth C.Synth
func (s NativeSynther) Name() string { return "Native" }
func (s NativeSynther) Synth(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) synth, err := Synth(patch, bpm)
return synth, err return synth, err

View File

@ -93,6 +93,8 @@ success:
f.Read(su_sample_table[:]) f.Read(su_sample_table[:])
} }
func (s GoSynther) Name() string { return "Go" }
func (s GoSynther) Synth(patch sointu.Patch, bpm int) (sointu.Synth, error) { func (s GoSynther) Synth(patch sointu.Patch, bpm int) (sointu.Synth, error) {
bytecode, err := NewBytecode(patch, AllFeatures{}, bpm) bytecode, err := NewBytecode(patch, AllFeatures{}, bpm)
if err != nil { if err != nil {