feat(tracker): ability to bind MIDI controllers to parameters

Closes #152
This commit is contained in:
5684185+vsariola@users.noreply.github.com
2026-01-31 23:18:14 +02:00
parent 6e8acc8f9b
commit f2ef57a845
15 changed files with 311 additions and 55 deletions

View File

@ -287,6 +287,12 @@ func (t *Tracker) KeyEvent(e key.Event, gtx C) {
t.MIDI().Refresh().Do()
case "ToggleMIDIInputtingNotes":
t.MIDI().InputtingNotes().Toggle()
case "ToggleMIDIBinding":
t.MIDI().Binding().Toggle()
case "MIDIUnbind":
t.MIDI().Unbind().Do()
case "MIDIUnbindAll":
t.MIDI().UnbindAll().Do()
default:
if len(action) > 4 && action[:4] == "Note" {
val, err := strconv.Atoi(string(action[4:]))

View File

@ -28,6 +28,9 @@
- { key: "E", shortcut: true, action: "InstrEnlargedToggle" }
- { key: "K", shortcut: true, action: "LinkInstrTrackToggle" }
- { key: "W", shortcut: true, action: "Quit" }
- { key: "B", shortcut: true, action: "ToggleMIDIBinding" }
- { key: "U", shortcut: true, action: "MIDIUnbind" }
- { key: "U", shortcut: true, shift: true, action: "MIDIUnbindAll" }
- { key: "Space", action: "PlayingToggleUnfollow" }
- { key: "Space", shift: true, action: "PlayingToggleFollow" }
- { key: "F1", action: "OrderEditorFocus" }

View File

@ -32,7 +32,7 @@ func (t *Keyboard[T]) Press(key T, ev tracker.NoteEvent) {
ev.Source = t // set the source to this keyboard
ev.On = true
ev.Timestamp = t.now()
if tracker.TrySend(t.broker.ToPlayer, any(ev)) {
if tracker.TrySend(t.broker.ToPlayer, any(&ev)) {
t.pressed[key] = ev
}
}
@ -42,7 +42,7 @@ func (t *Keyboard[T]) Release(key T) {
if ev, ok := t.pressed[key]; ok {
ev.Timestamp = t.now()
ev.On = false // the pressed contains the event we need to send to release the note
tracker.TrySend(t.broker.ToPlayer, any(ev))
tracker.TrySend(t.broker.ToPlayer, any(&ev))
delete(t.pressed, key)
}
}

View File

@ -144,7 +144,7 @@ func (te *NoteEditor) Layout(gtx layout.Context) layout.Dimensions {
}
copy(t.noteEvents, t.noteEvents[1:])
t.noteEvents = t.noteEvents[:len(t.noteEvents)-1]
tracker.TrySend(t.Broker().ToPlayer, any(ev))
tracker.TrySend(t.Broker().ToPlayer, any(&ev))
}
defer clip.Rect(image.Rect(0, 0, gtx.Constraints.Max.X, gtx.Constraints.Max.Y)).Push(gtx.Ops).Pop()

View File

@ -508,9 +508,13 @@ func (t *MenuBar) Layout(gtx C) D {
midiBtn := MenuBtn(&t.MenuStates[2], &t.Clickables[2], "MIDI")
midiFC := layout.Rigid(func(gtx C) D {
return midiBtn.Layout(gtx,
ActionMenuChild(tr.MIDI().Refresh(), "Refresh port list", keyActionMap["MIDIRefresh"], icons.NavigationRefresh),
BoolMenuChild(tr.MIDI().InputtingNotes(), "Use for note input", keyActionMap["ToggleMIDIInputtingNotes"], icons.NavigationCheck),
BoolMenuChild(tr.MIDI().Binding(), "Bind to controller", keyActionMap["ToggleMIDIBinding"], icons.NavigationCheck),
ActionMenuChild(tr.MIDI().Unbind(), "Unbind", keyActionMap["MIDIUnbind"], icons.ImageLeakRemove),
ActionMenuChild(tr.MIDI().UnbindAll(), "Unbind all", keyActionMap["MIDIUnbindAll"], icons.ImageLeakRemove),
DividerMenuChild(),
BoolMenuChild(tr.MIDI().InputtingNotes(), "Input notes", keyActionMap["ToggleMIDIInputtingNotes"], icons.NavigationCheck),
DividerMenuChild(),
ActionMenuChild(tr.MIDI().Refresh(), "Refresh", keyActionMap["MIDIRefresh"], icons.NavigationRefresh),
IntMenuChild(tr.MIDI().Input(), icons.NavigationCheck),
)
})

View File

@ -156,7 +156,7 @@ menu:
shortcut: { textsize: 16, color: *mediumemphasis, shadowcolor: *black }
hover: { r: 100, g: 140, b: 255, a: 48 }
disabled: *disabled
width: 200
width: 240
height: 300
preset:
text: { textsize: 16, color: *highemphasis, shadowcolor: *black }

View File

@ -145,8 +145,8 @@ func (t *Tracker) Main() {
select {
case e := <-t.Broker().ToGUI:
switch e := e.(type) {
case tracker.NoteEvent:
t.noteEvents = append(t.noteEvents, e)
case *tracker.NoteEvent:
t.noteEvents = append(t.noteEvents, *e)
case tracker.MsgToGUI:
switch e.Kind {
case tracker.GUIMessageCenterOnRow:
@ -274,7 +274,7 @@ func (t *Tracker) Layout(gtx layout.Context) {
ev.Source = t
copy(t.noteEvents, t.noteEvents[1:])
t.noteEvents = t.noteEvents[:len(t.noteEvents)-1]
tracker.TrySend(t.Broker().ToPlayer, any(ev))
tracker.TrySend(t.Broker().ToPlayer, any(&ev))
}
}