From 602b3b05cc348ced53b76a7cde19c79421d2bdc1 Mon Sep 17 00:00:00 2001 From: "5684185+vsariola@users.noreply.github.com" <5684185+vsariola@users.noreply.github.com> Date: Fri, 20 Jun 2025 19:38:06 +0300 Subject: [PATCH] feat(tracker): compile with midi support only when CGO is available Also add the midi context to the VSTI, so VSTI can use MIDI if they wish so. --- cmd/midi_cgo.go | 12 ++++++++++++ cmd/midi_not_cgo.go | 12 ++++++++++++ cmd/sointu-track/main.go | 5 ++--- cmd/sointu-vsti/main.go | 10 +--------- tracker/model.go | 8 ++++++++ tracker/model_test.go | 10 +--------- tracker/player.go | 6 ++++++ 7 files changed, 42 insertions(+), 21 deletions(-) create mode 100644 cmd/midi_cgo.go create mode 100644 cmd/midi_not_cgo.go diff --git a/cmd/midi_cgo.go b/cmd/midi_cgo.go new file mode 100644 index 0000000..8ad7e18 --- /dev/null +++ b/cmd/midi_cgo.go @@ -0,0 +1,12 @@ +//go:build cgo + +package cmd + +import ( + "github.com/vsariola/sointu/tracker" + "github.com/vsariola/sointu/tracker/gomidi" +) + +func NewMidiContext(broker *tracker.Broker) tracker.MIDIContext { + return gomidi.NewContext(broker) +} diff --git a/cmd/midi_not_cgo.go b/cmd/midi_not_cgo.go new file mode 100644 index 0000000..c19ee9b --- /dev/null +++ b/cmd/midi_not_cgo.go @@ -0,0 +1,12 @@ +//go:build !cgo + +package cmd + +import ( + "github.com/vsariola/sointu/tracker" +) + +func NewMidiContext(broker *tracker.Broker) tracker.MIDIContext { + // with no cgo, we cannot use MIDI, so return a null context + return tracker.NullMIDIContext{} +} diff --git a/cmd/sointu-track/main.go b/cmd/sointu-track/main.go index 77bcfc8..9a331ba 100644 --- a/cmd/sointu-track/main.go +++ b/cmd/sointu-track/main.go @@ -16,7 +16,6 @@ import ( "github.com/vsariola/sointu/oto" "github.com/vsariola/sointu/tracker" "github.com/vsariola/sointu/tracker/gioui" - "github.com/vsariola/sointu/tracker/gomidi" ) var cpuprofile = flag.String("cpuprofile", "", "write cpu profile to `file`") @@ -47,7 +46,7 @@ func main() { recoveryFile = filepath.Join(configDir, "Sointu", "sointu-track-recovery") } broker := tracker.NewBroker() - midiContext := gomidi.NewContext(broker) + midiContext := cmd.NewMidiContext(broker) defer midiContext.Close() midiContext.TryToOpenBy(*defaultMidiInput, *firstMidiInput) model := tracker.NewModel(broker, cmd.MainSynther, midiContext, recoveryFile) @@ -65,7 +64,7 @@ func main() { trackerUi := gioui.NewTracker(model) audioCloser := audioContext.Play(func(buf sointu.AudioBuffer) error { - player.Process(buf, midiContext) + player.Process(buf, tracker.NullPlayerProcessContext{}) return nil }) diff --git a/cmd/sointu-vsti/main.go b/cmd/sointu-vsti/main.go index 988b0a1..b2f20f6 100644 --- a/cmd/sointu-vsti/main.go +++ b/cmd/sointu-vsti/main.go @@ -24,16 +24,8 @@ type ( eventIndex int host vst2.Host } - - NullMIDIContext struct{} ) -func (m NullMIDIContext) InputDevices(yield func(tracker.MIDIDevice) bool) {} - -func (m NullMIDIContext) Close() {} - -func (m NullMIDIContext) HasDeviceOpen() bool { return false } - func (c *VSTIProcessContext) BPM() (bpm float64, ok bool) { timeInfo := c.host.GetTimeInfo(vst2.TempoValid) if timeInfo == nil || timeInfo.Flags&vst2.TempoValid == 0 || timeInfo.Tempo == 0 { @@ -54,7 +46,7 @@ func init() { recoveryFile = filepath.Join(configDir, "sointu", "sointu-vsti-recovery-"+hex.EncodeToString(randBytes)) } broker := tracker.NewBroker() - model := tracker.NewModel(broker, cmd.MainSynther, NullMIDIContext{}, recoveryFile) + model := tracker.NewModel(broker, cmd.MainSynther, cmd.NewMidiContext(broker), recoveryFile) player := tracker.NewPlayer(broker, cmd.MainSynther) detector := tracker.NewDetector(broker) go detector.Run() diff --git a/tracker/model.go b/tracker/model.go index 268cc83..9e34336 100644 --- a/tracker/model.go +++ b/tracker/model.go @@ -117,8 +117,11 @@ type ( InputDevices(yield func(MIDIDevice) bool) Close() HasDeviceOpen() bool + TryToOpenBy(name string, first bool) } + NullMIDIContext struct{} + MIDIDevice interface { String() string Open() error @@ -385,6 +388,11 @@ func (d *modelData) Copy() modelData { return ret } +func (m NullMIDIContext) InputDevices(yield func(MIDIDevice) bool) {} +func (m NullMIDIContext) Close() {} +func (m NullMIDIContext) HasDeviceOpen() bool { return false } +func (m NullMIDIContext) TryToOpenBy(name string, first bool) {} + func (m *Model) resetSong() { m.d.Song = defaultSong.Copy() for _, instr := range m.d.Song.Patch { diff --git a/tracker/model_test.go b/tracker/model_test.go index 510a0f5..dccc2f0 100644 --- a/tracker/model_test.go +++ b/tracker/model_test.go @@ -13,18 +13,10 @@ import ( type NullContext struct{} -func (NullContext) FinishBlock(frame int) {} - func (NullContext) BPM() (bpm float64, ok bool) { return 0, false } -func (NullContext) InputDevices(yield func(tracker.MIDIDevice) bool) {} - -func (NullContext) HasDeviceOpen() bool { return false } - -func (NullContext) Close() {} - type modelFuzzState struct { model *tracker.Model clipboard []byte @@ -261,7 +253,7 @@ func FuzzModel(f *testing.F) { reader := bytes.NewReader(slice) synther := vm.GoSynther{} broker := tracker.NewBroker() - model := tracker.NewModel(broker, synther, NullContext{}, "") + model := tracker.NewModel(broker, synther, tracker.NullMIDIContext{}, "") player := tracker.NewPlayer(broker, synther) buf := make([][2]float32, 2048) closeChan := make(chan struct{}) diff --git a/tracker/player.go b/tracker/player.go index de4adbb..c759f56 100644 --- a/tracker/player.go +++ b/tracker/player.go @@ -51,6 +51,8 @@ type ( BPM() (bpm float64, ok bool) } + NullPlayerProcessContext struct{} + // NoteEvent describes triggering or releasing of a note. The timestamps are // in frames, and relative to the clock of the event source. Different // sources can use different clocks. Player tries to adjust the timestamps @@ -200,6 +202,10 @@ func (p *Player) advanceRow() { p.send(nil) // just send volume and song row information } +func (p NullPlayerProcessContext) BPM() (bpm float64, ok bool) { + return 0, false // no BPM available +} + func (p *Player) processMessages(context PlayerProcessContext) { loop: for { // process new message