feat(GamePad): Implemented mappings

This commit is contained in:
2025-08-04 14:36:35 +02:00
parent 3942b6d8b2
commit 945a78c463
5 changed files with 87 additions and 13 deletions

View File

@@ -1,25 +1,31 @@
package main
import (
"log"
"gitlab.com/gomidi/midi/v2"
"github.com/bendahl/uinput"
)
type Controller struct {
midiInput *MidiInput
mappings []Mapping
abortChan chan interface{}
virtGamepad uinput.Gamepad
}
func NewController(portName string) (*Controller, error) {
func NewController(portName string, vendorID, productID uint16) (*Controller, error) {
midiInput, err := NewMidiInput(portName)
if err != nil {
return nil, err
}
virtGamepad, err := uinput.CreateGamepad("/dev/uinput", []byte(portName), vendorID, productID)
if err != nil {
return nil, err
}
abortChan := make(chan interface{})
controller := &Controller{midiInput, nil, abortChan}
controller := &Controller{midiInput, nil, abortChan, virtGamepad}
go func() {
for {
@@ -42,12 +48,11 @@ func (c *Controller) AddMapping(mapping Mapping) {
func (c Controller) Stop() {
c.midiInput.Stop()
c.abortChan <- struct{}{}
c.virtGamepad.Close()
}
func (c Controller) update(msg midi.Message) {
for _, mapping := range c.mappings {
if mapping.Is(msg) {
log.Println("Mapping triggered!\n")
}
mapping.TriggerIfMatch(msg, c.virtGamepad)
}
}

5
go.mod
View File

@@ -2,4 +2,7 @@ module datalore/midi-hid
go 1.24.5
require gitlab.com/gomidi/midi/v2 v2.3.16
require (
github.com/bendahl/uinput v1.7.0
gitlab.com/gomidi/midi/v2 v2.3.16
)

2
go.sum
View File

@@ -1,2 +1,4 @@
github.com/bendahl/uinput v1.7.0 h1:nA4fm8Wu8UYNOPykIZm66nkWEyvxzfmJ8YC02PM40jg=
github.com/bendahl/uinput v1.7.0/go.mod h1:Np7w3DINc9wB83p12fTAM3DPPhFnAKP0WTXRqCQJ6Z8=
gitlab.com/gomidi/midi/v2 v2.3.16 h1:yufWSENyjnJ4LFQa9BerzUm4E4aLfTyzw5nmnCteO0c=
gitlab.com/gomidi/midi/v2 v2.3.16/go.mod h1:jDpP4O4skYi+7iVwt6Zyp18bd2M4hkjtMuw2cmgKgfw=

12
main.go
View File

@@ -5,6 +5,7 @@ import (
"time"
"gitlab.com/gomidi/midi/v2"
"github.com/bendahl/uinput"
)
func must[T any](obj T, err error) T {
@@ -19,12 +20,11 @@ func main() {
defer midi.CloseDriver()
log.Println("Starting...")
controller := must(NewController("DJControl Inpulse 500 MIDI 1"))
controller.AddMapping(ButtonMapping{1, 7}) // play left
controller.AddMapping(ControlMapping{1, 0}) // volume left
controller := must(NewController("DJControl Inpulse 500 MIDI 1", 0x45e, 0x285)) // mimics xbox 360 controller
defer controller.Stop()
controller.AddMapping(ButtonMapping{"Play left", 1, 7, uinput.ButtonSouth})
controller.AddMapping(ControlMapping{"Volume left", 1, 0, LeftY, false})
time.Sleep(time.Second * 20)
log.Println("Stopping...")
controller.Stop()
log.Println("Stopped.")
}

View File

@@ -1,16 +1,22 @@
package main
import (
"fmt"
"gitlab.com/gomidi/midi/v2"
"github.com/bendahl/uinput"
)
type Mapping interface {
Is(midi.Message) bool
TriggerIfMatch(midi.Message, uinput.Gamepad) error
}
type ButtonMapping struct {
comment string
midiChannel uint8
midiKey uint8
gamepadKey int
}
func (m ButtonMapping) Is(msg midi.Message) bool {
@@ -24,9 +30,36 @@ func (m ButtonMapping) Is(msg midi.Message) bool {
}
}
func (m ButtonMapping) TriggerIfMatch(msg midi.Message, virtGamepad uinput.Gamepad) error {
if m.Is(msg) {
switch msg.Type() {
case midi.NoteOnMsg:
return virtGamepad.ButtonDown(m.gamepadKey)
case midi.NoteOffMsg:
return virtGamepad.ButtonUp(m.gamepadKey)
default:
return fmt.Errorf("Invalid message type triggered ButtonMapping")
}
}
return nil
}
type ControllerAxis int
const (
LeftX ControllerAxis = iota
LeftY
RightX
RightY
)
type ControlMapping struct {
comment string
midiChannel uint8
midiController uint8
axis ControllerAxis
isSigned bool
}
func (m ControlMapping) Is(msg midi.Message) bool {
@@ -38,3 +71,34 @@ func (m ControlMapping) Is(msg midi.Message) bool {
return false
}
}
func (m ControlMapping) TriggerIfMatch(msg midi.Message, virtGamepad uinput.Gamepad) error {
if m.Is(msg) {
var (
valueAbsolute uint8
valueNormalised float32
)
msg.GetControlChange(nil, nil, &valueAbsolute)
// value is 0-127, normalise
valueNormalised = float32(valueAbsolute) / 127
if m.isSigned {
valueNormalised *= 2
valueNormalised -= 1
}
switch m.axis {
case LeftX:
return virtGamepad.LeftStickMoveX(valueNormalised)
case LeftY:
return virtGamepad.LeftStickMoveY(valueNormalised)
case RightX:
return virtGamepad.RightStickMoveX(valueNormalised)
case RightY:
return virtGamepad.RightStickMoveY(valueNormalised)
}
}
return nil
}