package hardware import ( "context" "fmt" "regexp" "strconv" "strings" "github.com/mattrtaylor/go-rtmidi" ) // MIDIDriver represents how the protocol is defined type MIDIDriver struct { peripherals map[string]Peripheral // The list of peripherals } // NewMIDIDriver creates a new DMXUSB protocol func NewMIDIDriver() *MIDIDriver { return &MIDIDriver{ peripherals: make(map[string]Peripheral), } } // Initialize initializes the MIDI driver func (d *MIDIDriver) Initialize() error { fmt.Println("MIDI driver initialized") return nil } // GetName returns the name of the driver func (d *MIDIDriver) GetName() string { return "MIDI" } // GetPeripheral gets the peripheral that correspond to the specified ID func (d *MIDIDriver) GetPeripheral(peripheralID string) (Peripheral, bool) { // Return the specified peripheral peripheral := d.peripherals[peripheralID] if peripheral == nil { return nil, false } return peripheral, true } func splitStringAndNumber(input string) (string, int, error) { // Regular expression to match the text part and the number at the end re := regexp.MustCompile(`^(.*?)(\d+)$`) matches := re.FindStringSubmatch(input) // Check if the regex found both a text part and a number if len(matches) == 3 { // matches[1]: text part (might contain trailing spaces) // matches[2]: numeric part as a string textPart := strings.TrimSpace(matches[1]) // Remove any trailing spaces from the text numberPart, err := strconv.Atoi(matches[2]) if err != nil { return "", 0, err // Return error if the number conversion fails } return textPart, numberPart, nil } // Return an error if no trailing number is found return "", 0, fmt.Errorf("no number found at the end of the string") } // Scan scans the interfaces compatible with the MIDI protocol func (d *MIDIDriver) Scan(ctx context.Context) error { midiPeripherals := make(map[string]Peripheral) fmt.Println("Opening MIDI scanner port...") midiScanner, err := rtmidi.NewMIDIInDefault() if err != nil { return fmt.Errorf("Error when opening the MIDI scanner: %s", err) } defer midiScanner.Close() fmt.Println("Scanning MIDI devices...") devicesCount, err := midiScanner.PortCount() if err != nil { return fmt.Errorf("Error when scanning MIDI devices: %s", err) } for i := 0; i < devicesCount; i++ { portName, err := midiScanner.PortName(i) if err != nil { portName = "Unknown device 0" } // Separate data name, location, err := splitStringAndNumber(portName) if err != nil { return fmt.Errorf("Unable to get information from the string: %s", err) } fmt.Printf("New MIDI device found: %s on %i\n", name, location) // Add the peripheral to the temporary list sn := strings.ToLower(strings.Replace(name, " ", "_", -1)) midiPeripherals[sn] = NewMIDIPeripheral(name, location, sn) } // Compare with the current peripherals to detect arrivals/removals removedList, addedList := comparePeripherals(d.peripherals, midiPeripherals) // Emit the events emitPeripheralsEvents(ctx, removedList, PeripheralRemoval) emitPeripheralsEvents(ctx, addedList, PeripheralArrival) // Store the new peripherals list d.peripherals = midiPeripherals return nil } // CreatePeripheral is not implemented here func (d *MIDIDriver) CreatePeripheral(context.Context) (Peripheral, error) { return nil, nil } // RemovePeripheral is not implemented here func (d *MIDIDriver) RemovePeripheral(serialNumber string) error { return nil }