Skip to content

Commit

Permalink
work on midi settings
Browse files Browse the repository at this point in the history
  • Loading branch information
emicklei committed Sep 26, 2024
1 parent fa2873f commit c158cb1
Show file tree
Hide file tree
Showing 13 changed files with 74 additions and 94 deletions.
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,4 @@ cmd/melrose/melrose-udp
dsl/debug.test
core/debug.test
ui/img/*.png
since.log
since.log
5 changes: 4 additions & 1 deletion dsl/utils_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 }
Expand All @@ -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 }

Check failure on line 41 in dsl/utils_test.go

View workflow job for this annotation

GitHub Actions / build

expected ';', found '{'


func checkError(t *testing.T, err error) {
t.Helper()
Expand Down
6 changes: 3 additions & 3 deletions go.mod
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -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 (
Expand Down
5 changes: 2 additions & 3 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -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=
Expand Down
66 changes: 25 additions & 41 deletions midi/cli.go
Original file line number Diff line number Diff line change
Expand Up @@ -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")
Expand Down Expand Up @@ -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" {
Expand All @@ -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)
Expand All @@ -148,17 +115,34 @@ 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")
}

fmt.Println()

notify.PrintHighlighted("change:")
fmt.Println("set('midi.in',<device-id>) --- change the default MIDI input device id (or e.g. \":m i 1\")")
fmt.Println("set('midi.out',<device-id>) --- change the default MIDI output device id (or e.g. \":m o 1\")")
fmt.Println("set('midi.in',<device-id>) --- change the default MIDI input device id (or use e.g. \":m i 1\")")
fmt.Println("set('midi.out',<device-id>) --- change the default MIDI output device id (or use e.g. \":m o 1\")")
fmt.Println("set('midi.out.channel',<device-id>,<nr>) --- 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)
}
}
33 changes: 6 additions & 27 deletions midi/midi_event.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package midi

import (
"bytes"
"fmt"
"time"

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

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

Expand All @@ -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) {}
2 changes: 1 addition & 1 deletion midi/output_device.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
}
}
Expand Down
2 changes: 2 additions & 0 deletions midi/registry_device.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ import (
"github.com/emicklei/tre"
)

var _ core.AudioDevice = (*DeviceRegistry)(nil)

type DeviceRegistry struct {
mutex *sync.RWMutex
in map[int]*InputDevice
Expand Down
3 changes: 1 addition & 2 deletions midi/transport/m_listener.go
Original file line number Diff line number Diff line change
Expand Up @@ -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))
}
Expand Down
2 changes: 1 addition & 1 deletion midi/transport/rt.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 }
Expand Down
25 changes: 17 additions & 8 deletions midi/transport/rt_info.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -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()
Expand All @@ -42,15 +46,20 @@ 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++ {
name, err := out.PortName(i)
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()
Expand Down
15 changes: 10 additions & 5 deletions ui/cli/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion ui/cli/command.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
}

Expand Down

0 comments on commit c158cb1

Please sign in to comment.