mirror of
https://github.com/vsariola/sointu.git
synced 2026-04-07 14:42:58 -04:00
refactor(tracker): use strings to identify MIDI ports
This commit is contained in:
parent
651ceb3cbb
commit
173648fbdb
@ -20,8 +20,7 @@ import (
|
|||||||
|
|
||||||
var cpuprofile = flag.String("cpuprofile", "", "write cpu profile to `file`")
|
var cpuprofile = flag.String("cpuprofile", "", "write cpu profile to `file`")
|
||||||
var memprofile = flag.String("memprofile", "", "write memory profile to `file`")
|
var memprofile = flag.String("memprofile", "", "write memory profile to `file`")
|
||||||
var defaultMidiInput = flag.String("midi-input", "", "connect MIDI input to matching device name")
|
var defaultMidiInput = flag.String("midi-input", "", "connect MIDI input to matching device name prefix")
|
||||||
var firstMidiInput = flag.Bool("first-midi-input", false, "connect MIDI input to first device found")
|
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
flag.Parse()
|
flag.Parse()
|
||||||
@ -48,7 +47,17 @@ func main() {
|
|||||||
broker := tracker.NewBroker()
|
broker := tracker.NewBroker()
|
||||||
midiContext := cmd.NewMidiContext(broker)
|
midiContext := cmd.NewMidiContext(broker)
|
||||||
defer midiContext.Close()
|
defer midiContext.Close()
|
||||||
midiContext.TryToOpenBy(*defaultMidiInput, *firstMidiInput)
|
if isFlagPassed("midi-input") {
|
||||||
|
input, ok := tracker.FindMIDIDeviceByPrefix(midiContext, *defaultMidiInput)
|
||||||
|
if ok {
|
||||||
|
err := midiContext.Open(input)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("failed to open MIDI input '%s': %v", input, err)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
log.Printf("no MIDI input device found with prefix '%s'", *defaultMidiInput)
|
||||||
|
}
|
||||||
|
}
|
||||||
model := tracker.NewModel(broker, cmd.Synthers, midiContext, recoveryFile)
|
model := tracker.NewModel(broker, cmd.Synthers, midiContext, recoveryFile)
|
||||||
player := tracker.NewPlayer(broker, cmd.Synthers[0])
|
player := tracker.NewPlayer(broker, cmd.Synthers[0])
|
||||||
detector := tracker.NewDetector(broker)
|
detector := tracker.NewDetector(broker)
|
||||||
@ -96,3 +105,13 @@ func main() {
|
|||||||
}()
|
}()
|
||||||
app.Main()
|
app.Main()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func isFlagPassed(name string) bool {
|
||||||
|
found := false
|
||||||
|
flag.Visit(func(f *flag.Flag) {
|
||||||
|
if f.Name == name {
|
||||||
|
found = true
|
||||||
|
}
|
||||||
|
})
|
||||||
|
return found
|
||||||
|
}
|
||||||
|
|||||||
@ -620,16 +620,16 @@ func (m *Model) ShowLicense() Action { return MakeAction((*showLicense)(m)) }
|
|||||||
func (m *showLicense) Do() { m.dialog = License }
|
func (m *showLicense) Do() { m.dialog = License }
|
||||||
|
|
||||||
type selectMidiInput struct {
|
type selectMidiInput struct {
|
||||||
Item MIDIDevice
|
Item string
|
||||||
*Model
|
*Model
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Model) SelectMidiInput(item MIDIDevice) Action {
|
func (m *Model) SelectMidiInput(item string) Action {
|
||||||
return MakeAction(selectMidiInput{Item: item, Model: m})
|
return MakeAction(selectMidiInput{Item: item, Model: m})
|
||||||
}
|
}
|
||||||
func (s selectMidiInput) Do() {
|
func (s selectMidiInput) Do() {
|
||||||
m := s.Model
|
m := s.Model
|
||||||
if err := s.Item.Open(); err == nil {
|
if err := s.Model.MIDI.Open(s.Item); err == nil {
|
||||||
message := fmt.Sprintf("Opened MIDI device: %s", s.Item)
|
message := fmt.Sprintf("Opened MIDI device: %s", s.Item)
|
||||||
m.Alerts().Add(message, Info)
|
m.Alerts().Add(message, Info)
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@ -480,7 +480,7 @@ func NewMenuBar(tr *Tracker) *MenuBar {
|
|||||||
}
|
}
|
||||||
for input := range tr.MIDI.InputDevices {
|
for input := range tr.MIDI.InputDevices {
|
||||||
ret.midiMenuItems = append(ret.midiMenuItems,
|
ret.midiMenuItems = append(ret.midiMenuItems,
|
||||||
MenuItem(tr.SelectMidiInput(input), input.String(), "", icons.ImageControlPoint),
|
MenuItem(tr.SelectMidiInput(input), input, "", icons.ImageControlPoint),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
return ret
|
return ret
|
||||||
|
|||||||
@ -9,7 +9,6 @@ import "C"
|
|||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/vsariola/sointu/tracker"
|
"github.com/vsariola/sointu/tracker"
|
||||||
"gitlab.com/gomidi/midi/v2"
|
"gitlab.com/gomidi/midi/v2"
|
||||||
@ -23,14 +22,9 @@ type (
|
|||||||
currentIn drivers.In
|
currentIn drivers.In
|
||||||
broker *tracker.Broker
|
broker *tracker.Broker
|
||||||
}
|
}
|
||||||
|
|
||||||
RTMIDIDevice struct {
|
|
||||||
context *RTMIDIContext
|
|
||||||
in drivers.In
|
|
||||||
}
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func (m *RTMIDIContext) InputDevices(yield func(tracker.MIDIDevice) bool) {
|
func (m *RTMIDIContext) InputDevices(yield func(string) bool) {
|
||||||
if m.driver == nil {
|
if m.driver == nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -38,9 +32,8 @@ func (m *RTMIDIContext) InputDevices(yield func(tracker.MIDIDevice) bool) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
for i := 0; i < len(ins); i++ {
|
for _, in := range ins {
|
||||||
device := RTMIDIDevice{context: m, in: ins[i]}
|
if !yield(in.String()) {
|
||||||
if !yield(device) {
|
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -56,34 +49,43 @@ func NewContext(broker *tracker.Broker) *RTMIDIContext {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Open an input device while closing the currently open if necessary.
|
// Open an input device while closing the currently open if necessary.
|
||||||
func (m RTMIDIDevice) Open() error {
|
func (m *RTMIDIContext) Open(name string) error {
|
||||||
if m.context.currentIn == m.in {
|
if m.currentIn != nil && m.currentIn.String() == name {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
if m.context.driver == nil {
|
if m.driver == nil {
|
||||||
return errors.New("no driver available")
|
return errors.New("no driver available")
|
||||||
}
|
}
|
||||||
if m.context.HasDeviceOpen() {
|
if m.IsOpen() {
|
||||||
m.context.currentIn.Close()
|
m.currentIn.Close()
|
||||||
}
|
}
|
||||||
m.context.currentIn = m.in
|
m.currentIn = nil
|
||||||
err := m.in.Open()
|
ins, err := m.driver.Ins()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
m.context.currentIn = nil
|
return fmt.Errorf("retrieving MIDI inputs failed: %w", err)
|
||||||
return fmt.Errorf("opening MIDI input failed: %W", err)
|
|
||||||
}
|
}
|
||||||
_, err = midi.ListenTo(m.in, m.context.HandleMessage)
|
for _, in := range ins {
|
||||||
|
if in.String() == name {
|
||||||
|
m.currentIn = in
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if m.currentIn == nil {
|
||||||
|
return fmt.Errorf("MIDI input device not found: %s", name)
|
||||||
|
}
|
||||||
|
err = m.currentIn.Open()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
m.in.Close()
|
m.currentIn = nil
|
||||||
m.context.currentIn = nil
|
return fmt.Errorf("opening MIDI input failed: %w", err)
|
||||||
|
}
|
||||||
|
_, err = midi.ListenTo(m.currentIn, m.HandleMessage)
|
||||||
|
if err != nil {
|
||||||
|
m.currentIn.Close()
|
||||||
|
m.currentIn = nil
|
||||||
|
return fmt.Errorf("listening to MIDI input failed: %w", err)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d RTMIDIDevice) String() string {
|
|
||||||
return d.in.String()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *RTMIDIContext) HandleMessage(msg midi.Message, timestampms int32) {
|
func (m *RTMIDIContext) HandleMessage(msg midi.Message, timestampms int32) {
|
||||||
var channel, key, velocity uint8
|
var channel, key, velocity uint8
|
||||||
if msg.GetNoteOn(&channel, &key, &velocity) {
|
if msg.GetNoteOn(&channel, &key, &velocity) {
|
||||||
@ -109,23 +111,6 @@ func (c *RTMIDIContext) Close() {
|
|||||||
c.driver.Close()
|
c.driver.Close()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *RTMIDIContext) HasDeviceOpen() bool {
|
func (c *RTMIDIContext) IsOpen() bool {
|
||||||
return c.currentIn != nil && c.currentIn.IsOpen()
|
return c.currentIn != nil && c.currentIn.IsOpen()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *RTMIDIContext) TryToOpenBy(namePrefix string, takeFirst bool) {
|
|
||||||
if namePrefix == "" && !takeFirst {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
for input := range c.InputDevices {
|
|
||||||
if takeFirst || strings.HasPrefix(input.String(), namePrefix) {
|
|
||||||
input.Open()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if takeFirst {
|
|
||||||
fmt.Errorf("Could not find any MIDI Input.\n")
|
|
||||||
} else {
|
|
||||||
fmt.Errorf("Could not find any default MIDI Input starting with \"%s\".\n", namePrefix)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@ -6,6 +6,7 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"github.com/vsariola/sointu"
|
"github.com/vsariola/sointu"
|
||||||
)
|
)
|
||||||
@ -126,19 +127,14 @@ type (
|
|||||||
Dialog int
|
Dialog int
|
||||||
|
|
||||||
MIDIContext interface {
|
MIDIContext interface {
|
||||||
InputDevices(yield func(MIDIDevice) bool)
|
InputDevices(yield func(string) bool)
|
||||||
|
Open(name string) error
|
||||||
Close()
|
Close()
|
||||||
HasDeviceOpen() bool
|
IsOpen() bool
|
||||||
TryToOpenBy(name string, first bool)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
NullMIDIContext struct{}
|
NullMIDIContext struct{}
|
||||||
|
|
||||||
MIDIDevice interface {
|
|
||||||
String() string
|
|
||||||
Open() error
|
|
||||||
}
|
|
||||||
|
|
||||||
InstrumentTab int
|
InstrumentTab int
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -221,6 +217,15 @@ func NewModel(broker *Broker, synthers []sointu.Synther, midiContext MIDIContext
|
|||||||
return m
|
return m
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func FindMIDIDeviceByPrefix(c MIDIContext, prefix string) (input string, ok bool) {
|
||||||
|
for input := range c.InputDevices {
|
||||||
|
if strings.HasPrefix(input, prefix) {
|
||||||
|
return input, true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return "", false
|
||||||
|
}
|
||||||
|
|
||||||
func (m *Model) change(kind string, t ChangeType, severity ChangeSeverity) func() {
|
func (m *Model) change(kind string, t ChangeType, severity ChangeSeverity) func() {
|
||||||
if m.changeLevel == 0 {
|
if m.changeLevel == 0 {
|
||||||
m.changeType = NoChange
|
m.changeType = NoChange
|
||||||
@ -434,10 +439,10 @@ func (d *modelData) Copy() modelData {
|
|||||||
return ret
|
return ret
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m NullMIDIContext) InputDevices(yield func(MIDIDevice) bool) {}
|
func (m NullMIDIContext) InputDevices(yield func(string) bool) {}
|
||||||
|
func (m NullMIDIContext) Open(name string) error { return nil }
|
||||||
func (m NullMIDIContext) Close() {}
|
func (m NullMIDIContext) Close() {}
|
||||||
func (m NullMIDIContext) HasDeviceOpen() bool { return false }
|
func (m NullMIDIContext) IsOpen() bool { return false }
|
||||||
func (m NullMIDIContext) TryToOpenBy(name string, first bool) {}
|
|
||||||
|
|
||||||
func (m *Model) resetSong() {
|
func (m *Model) resetSong() {
|
||||||
m.d.Song = defaultSong.Copy()
|
m.d.Song = defaultSong.Copy()
|
||||||
|
|||||||
Reference in New Issue
Block a user