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.
This commit is contained in:
5684185+vsariola@users.noreply.github.com
2025-06-20 19:38:06 +03:00
parent 3881b8eb22
commit 602b3b05cc
7 changed files with 42 additions and 21 deletions

12
cmd/midi_cgo.go Normal file
View File

@ -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)
}

12
cmd/midi_not_cgo.go Normal file
View File

@ -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{}
}

View File

@ -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
})

View File

@ -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()

View File

@ -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 {

View File

@ -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{})

View File

@ -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