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. */ // MIDIFinder represents how the protocol is defined type MIDIFinder struct { peripherals map[string]Peripheral // The list of peripherals } // NewMIDIFinder creates a new DMXUSB protocol func NewMIDIFinder() *MIDIFinder { return &MIDIFinder{ peripherals: make(map[string]Peripheral), } } // Initialize initializes the DMX-USB finder func (f *MIDIFinder) Initialize() error { fmt.Println("MIDI finder initialized") return nil } // GetName returns the name of the finder func (f *MIDIFinder) GetName() string { return "MIDI" } 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 (f *MIDIFinder) 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(f.peripherals, midiPeripherals) // Emit the events emitPeripheralsEvents(ctx, removedList, PeripheralRemoval) emitPeripheralsEvents(ctx, addedList, PeripheralArrival) // Store the new peripherals list f.peripherals = midiPeripherals return nil }