From c158cb1e0d384113db61e3d734c975d2ac4da382 Mon Sep 17 00:00:00 2001 From: Ernest Micklei Date: Thu, 26 Sep 2024 15:28:52 +0200 Subject: [PATCH] work on midi settings --- .gitignore | 2 +- dsl/utils_test.go | 5 ++- go.mod | 6 ++-- go.sum | 5 ++- midi/cli.go | 66 ++++++++++++++---------------------- midi/midi_event.go | 33 ++++-------------- midi/output_device.go | 2 +- midi/registry_device.go | 2 ++ midi/transport/m_listener.go | 3 +- midi/transport/rt.go | 2 +- midi/transport/rt_info.go | 25 +++++++++----- ui/cli/app.go | 15 +++++--- ui/cli/command.go | 2 +- 13 files changed, 74 insertions(+), 94 deletions(-) diff --git a/.gitignore b/.gitignore index 7bd2e4c..1630070 100644 --- a/.gitignore +++ b/.gitignore @@ -11,4 +11,4 @@ cmd/melrose/melrose-udp dsl/debug.test core/debug.test ui/img/*.png -since.log +since.log \ No newline at end of file diff --git a/dsl/utils_test.go b/dsl/utils_test.go index 160aeab..3852824 100644 --- a/dsl/utils_test.go +++ b/dsl/utils_test.go @@ -21,6 +21,8 @@ func testContext() core.Context { } } +var _ core.AudioDevice = (*testAudioDevice)(nil) + type testAudioDevice struct{} func (t testAudioDevice) Command(args []string) notify.Message { return nil } @@ -36,7 +38,8 @@ func (t testAudioDevice) OnKey(ctx core.Context, deviceID int, channel int, note } func (t testAudioDevice) Schedule(event core.TimelineEvent, beginAt time.Time) {} func (t testAudioDevice) Reset() {} -func (t testAudioDevice) Close() error { return nil } +func (t testAudioDevice) Close() error { return nil } { return nil } + func checkError(t *testing.T, err error) { t.Helper() diff --git a/go.mod b/go.mod index 6389a11..97f1b80 100644 --- a/go.mod +++ b/go.mod @@ -1,8 +1,8 @@ module github.com/emicklei/melrose -go 1.22 +go 1.22.2 -toolchain go1.22.5 +toolchain go1.23.0 require ( github.com/Try431/EasyMIDI v1.0.3 @@ -11,7 +11,7 @@ require ( github.com/expr-lang/expr v1.16.9 github.com/fogleman/gg v1.3.0 github.com/peterh/liner v1.2.2 - gitlab.com/gomidi/rtmididrv v0.15.0 + gitlab.com/gomidi/midi/v2 v2.2.10 ) require ( diff --git a/go.sum b/go.sum index 8558437..0a79693 100644 --- a/go.sum +++ b/go.sum @@ -20,9 +20,8 @@ github.com/peterh/liner v1.2.2/go.mod h1:xFwJyiKIXJZUKItq5dGHZSTBRAuG/CpeNpWLyiN github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ= github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= -gitlab.com/gomidi/midi v1.21.0/go.mod h1:3ohtNOhqoSakkuLG/Li1OI6I3J1c2LErnJF5o/VBq1c= -gitlab.com/gomidi/rtmididrv v0.15.0 h1:52Heco8Y3Jjcl4t0yDUVikOxfI8FMF1Zq+qsG++TUeo= -gitlab.com/gomidi/rtmididrv v0.15.0/go.mod h1:p/6IL1LGgj7utcv3wXudsDWiD9spgAdn0O8LDsGIPG0= +gitlab.com/gomidi/midi/v2 v2.2.10 h1:u9D+5TM0vkFWF5DcO6xGKG99ERYqksh6wPj2X2Rx5A8= +gitlab.com/gomidi/midi/v2 v2.2.10/go.mod h1:ENtYaJPOwb2N+y7ihv/L7R4GtWjbknouhIIkMrJ5C0g= golang.org/x/image v0.18.0 h1:jGzIakQa/ZXI1I0Fxvaa9W7yP25TqT6cHIHn+6CqvSQ= golang.org/x/image v0.18.0/go.mod h1:4yyo5vMFQjVjUcVk4jEQcU9MGy/rulF5WvUILseCM2E= golang.org/x/sys v0.0.0-20211117180635-dee7805ff2e1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= diff --git a/midi/cli.go b/midi/cli.go index 5d75cee..517fad4 100644 --- a/midi/cli.go +++ b/midi/cli.go @@ -10,43 +10,10 @@ import ( func (r *DeviceRegistry) HandleSetting(name string, values []interface{}) error { switch name { case "echo": - if len(values) != 1 { - return fmt.Errorf("one argument expected") - } - enable, ok := values[0].(bool) - if !ok { - return fmt.Errorf("boolean device argument expected, got %T", values[0]) - } - od, _ := r.Output(r.defaultOutputID) - od.echo = enable - notify.Infof("echo notes is enabled: %v", enable) - case "echo.toggle": if len(values) != 0 { return fmt.Errorf("no argument expected") } - // input - id, err := r.Input(r.defaultInputID) - if err == nil { - id.echo = !id.echo - if id.echo { - id.listener.Add(DefaultEchoListener) - id.listener.Start() - } else { - id.listener.Remove(DefaultEchoListener) - // id.listener.Stop() - } - notify.Infof("echo input notes from device %d is enabled: %v", id.id, id.echo) - } else { - notify.Infof("echo input notes is disabled ; no input device") - } - // output - od, err := r.Output(r.defaultOutputID) - if err == nil { - od.echo = !od.echo - notify.Infof("echo output notes from device %d is enabled: %v", od.id, od.echo) - } else { - notify.Infof("echo output notes is disabled ; no output device") - } + r.toggleEchoNotesForDevices() case "midi.in": if len(values) != 1 { return fmt.Errorf("one argument expected") @@ -122,7 +89,7 @@ func (r *DeviceRegistry) Command(args []string) notify.Message { return nil } if len(args) == 1 && args[0] == "e" { - r.HandleSetting("echo.toggle", []interface{}{}) + r.HandleSetting("echo", []interface{}{}) return nil } if len(args) == 1 && args[0] == "r" { @@ -138,7 +105,7 @@ func (r *DeviceRegistry) Command(args []string) notify.Message { func (r *DeviceRegistry) printInfo() { r.streamRegistry.transport.PrintInfo(r.defaultInputID, r.defaultOutputID) - notify.PrintHighlighted("current defaults:") + notify.PrintHighlighted("default settings:") _, err := r.Input(r.defaultInputID) if err == nil { fmt.Printf(" input device = %d\n", r.defaultInputID) @@ -148,7 +115,7 @@ func (r *DeviceRegistry) printInfo() { od, err := r.Output(r.defaultOutputID) if err == nil { fmt.Printf("output device = %d, channel = %d\n", r.defaultOutputID, od.defaultChannel) - fmt.Printf(" echo notes = %v\n", od.echo) + fmt.Printf(" echo MIDI = %v\n", od.echo) } else { fmt.Printf(" no output device (restart?)\n") } @@ -156,9 +123,26 @@ func (r *DeviceRegistry) printInfo() { fmt.Println() notify.PrintHighlighted("change:") - fmt.Println("set('midi.in',) --- change the default MIDI input device id (or e.g. \":m i 1\")") - fmt.Println("set('midi.out',) --- change the default MIDI output device id (or e.g. \":m o 1\")") + fmt.Println("set('midi.in',) --- change the default MIDI input device id (or use e.g. \":m i 1\")") + fmt.Println("set('midi.out',) --- change the default MIDI output device id (or use e.g. \":m o 1\")") fmt.Println("set('midi.out.channel',,) --- change the default MIDI channel for an output device id") - fmt.Println("set('echo.toggle') --- toggle printing the notes (or \":m e\" )") - fmt.Println("set('echo',true) --- true = print the notes") + fmt.Println("set('echo') --- toggle printing the notes (or use \":m e\" )") +} + +func (r *DeviceRegistry) toggleEchoNotesForDevices() { + for _, each := range r.in { + each.echo = !each.echo + if each.echo { + each.listener.Add(DefaultEchoListener) + each.listener.Start() + } else { + each.listener.Remove(DefaultEchoListener) + // each.listener.Stop() + } + notify.Infof("echo input notes from device %d is enabled: %v", each.id, each.echo) + } + for _, each := range r.out { + each.echo = !each.echo + notify.Infof("echo output notes from device %d is enabled: %v", each.id, each.echo) + } } diff --git a/midi/midi_event.go b/midi/midi_event.go index 5523fc6..579ceea 100644 --- a/midi/midi_event.go +++ b/midi/midi_event.go @@ -1,7 +1,6 @@ package midi import ( - "bytes" "fmt" "time" @@ -21,9 +20,9 @@ type midiEvent struct { mustHandle core.Condition } -func (m midiEvent) NoteChangesDo(block func(core.NoteChange)) { +func (m midiEvent) NoteChangesDo(callback func(core.NoteChange)) { for _, each := range m.which { - block(core.NewNoteChange(m.onoff == noteOn, each, m.velocity)) + callback(core.NewNoteChange(m.onoff == noteOn, each, m.velocity)) } } @@ -32,16 +31,13 @@ func (m midiEvent) Handle(tim *core.Timeline, when time.Time) { if m.mustHandle != nil && m.onoff == noteOn && !m.mustHandle() { return } - if len(m.echoString) > 0 { - fmt.Fprintf(notify.Console.DeviceOut, " %s", m.echoString) - } status := m.onoff | int64(m.channel-1) for _, each := range m.which { if err := m.out.WriteShort(status, each, m.velocity); err != nil { notify.Errorf("failed to write MIDI data, error:%v", err) } } - if core.IsDebug() { + if m.echoString != "" { m.log(status, when) } } @@ -51,22 +47,12 @@ func (m midiEvent) log(status int64, when time.Time) { if m.onoff == noteOff { onoff = "off" } - var echos bytes.Buffer - for i, each := range m.which { - if i > 0 { - fmt.Fprintf(&echos, " ") - } - n, _ := core.MIDItoNote(0.25, int(each), core.Normal) // TODO - fmt.Fprintf(&echos, "%s", n.String()) - } - fmt.Fprintf(notify.Console.StandardOut, "midi.note: t=%s dev=%d ch=%d seq='%s' %s=%d,%v,%d\n", - when.Format("04:05.000"), m.device, m.channel, echos.String(), onoff, status, m.which, m.velocity) + fmt.Fprintf(notify.Console.StandardOut, "%s dev=%d ch=%d seq='%s' %s=%d,%v,%d\n", + when.Format("04:05.000"), m.device, m.channel, m.echoString, onoff, status, m.which, m.velocity) } func (m midiEvent) asNoteoff() midiEvent { m.onoff = noteOff - // do not echo OFF - m.echoString = "" return m } @@ -77,11 +63,4 @@ type restEvent struct { func (r restEvent) NoteChangesDo(block func(core.NoteChange)) {} -func (r restEvent) Handle(tim *core.Timeline, when time.Time) { - if r.mustHandle != nil && !r.mustHandle() { - return - } - if len(r.echoString) > 0 { - fmt.Fprintf(notify.Console.DeviceOut, " %s", r.echoString) - } -} +func (r restEvent) Handle(tim *core.Timeline, when time.Time) {} diff --git a/midi/output_device.go b/midi/output_device.go index 8b5b7be..540e8f9 100644 --- a/midi/output_device.go +++ b/midi/output_device.go @@ -40,7 +40,7 @@ func (d *OutputDevice) Reset() { // send note off all to all channels for current device for c := 1; c <= 16; c++ { if err := d.stream.WriteShort(controlChange|int64(c-1), noteAllOff, 0); err != nil { - notify.Console.Errorf("device.%d: portmidi write error:%v", d.id, err) + notify.Console.Errorf("device.%d: midi write error:%v", d.id, err) } } } diff --git a/midi/registry_device.go b/midi/registry_device.go index be7d998..dedfaae 100644 --- a/midi/registry_device.go +++ b/midi/registry_device.go @@ -11,6 +11,8 @@ import ( "github.com/emicklei/tre" ) +var _ core.AudioDevice = (*DeviceRegistry)(nil) + type DeviceRegistry struct { mutex *sync.RWMutex in map[int]*InputDevice diff --git a/midi/transport/m_listener.go b/midi/transport/m_listener.go index e4c8a41..3a5307e 100644 --- a/midi/transport/m_listener.go +++ b/midi/transport/m_listener.go @@ -80,8 +80,7 @@ func (l *mListener) HandleMIDIMessage(status int16, nr int, data2 int) { ch := int(int16(0x0F)&status) + 1 // controlChange before noteOn - isControlChange := (status & controlChange) == controlChange - if isControlChange { + if (status & controlChange) == controlChange { for _, each := range l.noteListeners { each.ControlChange(ch, nr, int(data2)) } diff --git a/midi/transport/rt.go b/midi/transport/rt.go index 08cb9a8..f776d99 100644 --- a/midi/transport/rt.go +++ b/midi/transport/rt.go @@ -6,7 +6,7 @@ package transport import ( "github.com/emicklei/melrose/core" "github.com/emicklei/melrose/notify" - "gitlab.com/gomidi/rtmididrv/imported/rtmidi" + "gitlab.com/gomidi/midi/v2/drivers/rtmididrv/imported/rtmidi" ) func init() { Initializer = rtInitialize } diff --git a/midi/transport/rt_info.go b/midi/transport/rt_info.go index b90cf8c..d2031a3 100644 --- a/midi/transport/rt_info.go +++ b/midi/transport/rt_info.go @@ -8,11 +8,11 @@ import ( "log" "github.com/emicklei/melrose/notify" - "gitlab.com/gomidi/rtmididrv/imported/rtmidi" + "gitlab.com/gomidi/midi/v2/drivers/rtmididrv/imported/rtmidi" ) -func (t RtmidiTransporter) PrintInfo(inID, outID int) { - notify.PrintHighlighted("available input:") +func (t RtmidiTransporter) PrintInfo(defaultInID, defaultOutID int) { + notify.PrintHighlighted("available inputs:") in, err := rtmidi.NewMIDIInDefault() if err != nil { @@ -21,18 +21,22 @@ func (t RtmidiTransporter) PrintInfo(inID, outID int) { defer in.Close() ports, err := in.PortCount() if err != nil { - log.Fatalln("can't get number of in ports: ", err) + log.Fatalln("can't get number of input ports: ", err) } for i := 0; i < ports; i++ { name, err := in.PortName(i) if err != nil { name = "" } - fmt.Printf(" set('midi.in',%d) : %s\n", i, name) + isCurrent := "" + if i == defaultInID { + isCurrent = " (default)" + } + fmt.Printf(" set('midi.in',%d) --- set MIDI input from %s%s\n", i, name, isCurrent) } fmt.Println() - notify.PrintHighlighted("available output:") + notify.PrintHighlighted("available outputs:") { // Outs out, err := rtmidi.NewMIDIOutDefault() @@ -42,7 +46,7 @@ func (t RtmidiTransporter) PrintInfo(inID, outID int) { defer out.Close() ports, err := out.PortCount() if err != nil { - log.Fatalln("can't get number of out ports: ", err) + log.Fatalln("can't get number of output ports: ", err) } for i := 0; i < ports; i++ { @@ -50,7 +54,12 @@ func (t RtmidiTransporter) PrintInfo(inID, outID int) { if err != nil { name = "" } - fmt.Printf("set('midi.out',%d) : %s\n", i, name) + isCurrent := "" + if i == defaultInID { + isCurrent = " (default)" + } + + fmt.Printf("set('midi.out',%d) --- set MIDI output to%s%s\n", i, name, isCurrent) } } fmt.Println() diff --git a/ui/cli/app.go b/ui/cli/app.go index 5250d0f..d54c383 100644 --- a/ui/cli/app.go +++ b/ui/cli/app.go @@ -78,14 +78,19 @@ func repl(line *liner.State, ctx core.Context) { } } if strings.HasSuffix(entry, "!") { - // create hidden variable - // assign it the value of the expression before ! - // open the browser on it - if result, err := eval.RecoveringEvaluateStatement(entry[:len(entry)-2]); err != nil { + + if len(entry) == 1 { + notify.Errorf("missing expression before '!'") + continue + } + if result, err := eval.RecoveringEvaluateStatement(entry[:len(entry)-1]); err != nil { notify.Print(notify.NewError(err)) - return + continue } else { if result != nil { + // create hidden variable + // assign it the value of the expression before ! + // open the browser on it ctx.Variables().Put("_", result) open("http://localhost:8118/v1/notes?var=_") continue diff --git a/ui/cli/command.go b/ui/cli/command.go index a24efd2..732e065 100644 --- a/ui/cli/command.go +++ b/ui/cli/command.go @@ -28,7 +28,7 @@ func cmdFunctions() map[string]Command { cmds[":q"] = Command{Description: "quit"} // no Func because it is handled in the main loop cmds[":d"] = Command{Description: "toggle debug lines", Func: handleToggleDebug} cmds[":p"] = Command{Description: "list all running", Func: handleListAllRunning} - cmds[":e"] = Command{Description: "echo notes", Func: handleEchoNotes} + cmds[":e"] = Command{Description: "echo MIDI", Func: handleEchoNotes} return cmds }