package hardware import ( "context" "fmt" "regexp" "strconv" "strings" "github.com/mattrtaylor/go-rtmidi" ) /* DMXConnect DMXUSBProtocol.go Copyright (c) Valentin Boulanger Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0.txt Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ // 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 midiPeripherals[portName] = NewMIDIPeripheral(name, location) } // 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 }