From 25b06ffc6d1fc2673883e813df959288366aec12 Mon Sep 17 00:00:00 2001 From: Joel Wetzell Date: Tue, 16 Dec 2025 19:24:20 -0600 Subject: [PATCH] split midi.client into input/output --- internal/module/midi-client.go | 119 --------------------------------- internal/module/midi-input.go | 81 ++++++++++++++++++++++ internal/module/midi-ouptut.go | 89 ++++++++++++++++++++++++ 3 files changed, 170 insertions(+), 119 deletions(-) delete mode 100644 internal/module/midi-client.go create mode 100644 internal/module/midi-input.go create mode 100644 internal/module/midi-ouptut.go diff --git a/internal/module/midi-client.go b/internal/module/midi-client.go deleted file mode 100644 index 8a49d0c..0000000 --- a/internal/module/midi-client.go +++ /dev/null @@ -1,119 +0,0 @@ -//go:build cgo - -package module - -import ( - "context" - "fmt" - "log/slog" - - "github.com/jwetzell/showbridge-go/internal/config" - "github.com/jwetzell/showbridge-go/internal/route" - "gitlab.com/gomidi/midi/v2" - _ "gitlab.com/gomidi/midi/v2/drivers/rtmididrv" -) - -type MIDIClient struct { - config config.ModuleConfig - ctx context.Context - router route.RouteIO - InputPort string - OutputPort string - SendFunc func(midi.Message) error -} - -func init() { - RegisterModule(ModuleRegistration{ - //TODO(jwetzell): find a better namespace than "misc" - Type: "midi.client", - New: func(ctx context.Context, config config.ModuleConfig, router route.RouteIO) (Module, error) { - params := config.Params - input, ok := params["input"] - - if !ok { - return nil, fmt.Errorf("midi.client requires a input parameter") - } - - inputString, ok := input.(string) - - if !ok { - return nil, fmt.Errorf("midi.client input must be a string") - } - - output, ok := params["output"] - - if !ok { - return nil, fmt.Errorf("midi.client requires a output parameter") - } - - outputString, ok := output.(string) - - if !ok { - return nil, fmt.Errorf("midi.client output must be a string") - } - - return &MIDIClient{config: config, InputPort: inputString, OutputPort: outputString, ctx: ctx, router: router}, nil - }, - }) -} - -func (mc *MIDIClient) Id() string { - return mc.config.Id -} - -func (mc *MIDIClient) Type() string { - return mc.config.Type -} - -func (mc *MIDIClient) Run() error { - defer midi.CloseDriver() - - in, err := midi.FindInPort(mc.InputPort) - if err != nil { - return fmt.Errorf("midi.client can't find input port: %s", mc.InputPort) - } - - stop, err := midi.ListenTo(in, func(msg midi.Message, timestampms int32) { - if mc.router != nil { - // TODO(jwetzell): unpack MIDI messsage into something more useful? - mc.router.HandleInput(mc.Id(), msg) - } - }, midi.UseSysEx()) - - if err != nil { - return err - } - - defer stop() - - out, err := midi.FindOutPort(mc.OutputPort) - - if err != nil { - return fmt.Errorf("midi.client can't find output port: %s", mc.OutputPort) - } - - send, err := midi.SendTo(out) - if err != nil { - return err - } - - mc.SendFunc = send - - <-mc.ctx.Done() - slog.Debug("router context done in module", "id", mc.Id()) - return nil -} - -func (mc *MIDIClient) Output(payload any) error { - if mc.SendFunc == nil { - return fmt.Errorf("midi.client output is not setup") - } - - payloadMessage, ok := payload.(midi.Message) - - if !ok { - return fmt.Errorf("midi.client can only ouptut midi.Message") - } - - return mc.SendFunc(payloadMessage) -} diff --git a/internal/module/midi-input.go b/internal/module/midi-input.go new file mode 100644 index 0000000..74c4a57 --- /dev/null +++ b/internal/module/midi-input.go @@ -0,0 +1,81 @@ +//go:build cgo + +package module + +import ( + "context" + "fmt" + "log/slog" + + "github.com/jwetzell/showbridge-go/internal/config" + "github.com/jwetzell/showbridge-go/internal/route" + "gitlab.com/gomidi/midi/v2" + _ "gitlab.com/gomidi/midi/v2/drivers/rtmididrv" +) + +type MIDIInput struct { + config config.ModuleConfig + ctx context.Context + router route.RouteIO + Port string + SendFunc func(midi.Message) error +} + +func init() { + RegisterModule(ModuleRegistration{ + Type: "midi.input", + New: func(ctx context.Context, config config.ModuleConfig, router route.RouteIO) (Module, error) { + params := config.Params + port, ok := params["port"] + + if !ok { + return nil, fmt.Errorf("midi.input requires a port parameter") + } + + portString, ok := port.(string) + + if !ok { + return nil, fmt.Errorf("midi.input port must be a string") + } + + return &MIDIInput{config: config, Port: portString, ctx: ctx, router: router}, nil + }, + }) +} + +func (mc *MIDIInput) Id() string { + return mc.config.Id +} + +func (mc *MIDIInput) Type() string { + return mc.config.Type +} + +func (mc *MIDIInput) Run() error { + defer midi.CloseDriver() + + in, err := midi.FindInPort(mc.Port) + if err != nil { + return fmt.Errorf("midi.input can't find input port: %s", mc.Port) + } + + stop, err := midi.ListenTo(in, func(msg midi.Message, timestampms int32) { + if mc.router != nil { + mc.router.HandleInput(mc.Id(), msg) + } + }, midi.UseSysEx()) + + if err != nil { + return err + } + + defer stop() + + <-mc.ctx.Done() + slog.Debug("router context done in module", "id", mc.Id()) + return nil +} + +func (mc *MIDIInput) Output(payload any) error { + return fmt.Errorf("midi.input output is not implemented") +} diff --git a/internal/module/midi-ouptut.go b/internal/module/midi-ouptut.go new file mode 100644 index 0000000..f778567 --- /dev/null +++ b/internal/module/midi-ouptut.go @@ -0,0 +1,89 @@ +//go:build cgo + +package module + +import ( + "context" + "fmt" + "log/slog" + + "github.com/jwetzell/showbridge-go/internal/config" + "github.com/jwetzell/showbridge-go/internal/route" + "gitlab.com/gomidi/midi/v2" + _ "gitlab.com/gomidi/midi/v2/drivers/rtmididrv" +) + +type MIDIOutput struct { + config config.ModuleConfig + ctx context.Context + router route.RouteIO + Port string + SendFunc func(midi.Message) error +} + +func init() { + RegisterModule(ModuleRegistration{ + //TODO(jwetzell): find a better namespace than "misc" + Type: "midi.output", + New: func(ctx context.Context, config config.ModuleConfig, router route.RouteIO) (Module, error) { + params := config.Params + + port, ok := params["port"] + + if !ok { + return nil, fmt.Errorf("midi.output requires a port parameter") + } + + portString, ok := port.(string) + + if !ok { + return nil, fmt.Errorf("midi.output port must be a string") + } + + return &MIDIOutput{config: config, Port: portString, ctx: ctx, router: router}, nil + }, + }) +} + +func (mc *MIDIOutput) Id() string { + return mc.config.Id +} + +func (mc *MIDIOutput) Type() string { + return mc.config.Type +} + +func (mc *MIDIOutput) Run() error { + defer midi.CloseDriver() + + out, err := midi.FindOutPort(mc.Port) + + if err != nil { + return fmt.Errorf("midi.output can't find output port: %s", mc.Port) + } + + send, err := midi.SendTo(out) + if err != nil { + return err + } + + mc.SendFunc = send + + <-mc.ctx.Done() + slog.Debug("router context done in module", "id", mc.Id()) + return nil +} + +func (mc *MIDIOutput) Output(payload any) error { + if mc.SendFunc == nil { + return fmt.Errorf("midi.output output is not setup") + } + + payloadMessage, ok := payload.(midi.Message) + + if !ok { + return fmt.Errorf("midi.output can only ouptut midi.Message") + } + + return mc.SendFunc(payloadMessage) +}