jazz-soft / JZZ

MIDI library for Node.js and web-browsers
http://jazz-soft.net/doc/JZZ/
MIT License
523 stars 27 forks source link

Problem using ports that have the same name #38

Open martyphelan opened 3 years ago

martyphelan commented 3 years ago

I have 4 USB/Midi keyboards - 2 using MIO's and 2 using Arduino's. They are all functional with other software.

JZZ sees all the devices, but only connects to the first device of each type no matter with port # I give it.

Below is the list of ports from JZZ().info().inputs

0: {id: "Midi Through Port-0", name: "Midi Through Port-0", manufacturer: "", version: "ALSA library version 1.1.9", engine: "webmidi"} 1: {id: "mio MIDI 1", name: "mio MIDI 1", manufacturer: "iConnectivity", version: "ALSA library version 1.1.9", engine: "webmidi"} 2: {id: "Arduino Due MIDI 1", name: "Arduino Due MIDI 1", manufacturer: "Arduino LLC", version: "ALSA library version 1.1.9", engine: "webmidi"} 3: {id: "Arduino Due MIDI 1", name: "Arduino Due MIDI 1", manufacturer: "Arduino LLC", version: "ALSA library version 1.1.9", engine: "webmidi"} 4: {id: "mio MIDI 1", name: "mio MIDI 1", manufacturer: "iConnectivity", version: "ALSA library version 1.1.9", engine: "webmidi"}

I can successfully use the below code to display midi events from ports 1 or 2. But when I try port 3 or 4, they get the events from 1 or 2.

function onRecord() { console.log("Recording on port 3"); JZZ().openMidiIn(3).or('Cannot open MIDI In port!') .and(function() { console.log('MIDI-In: ', this.name()); }) .connect(function(msg) { console.log(msg.toString()); }) .wait(10000).close();
}

If I physically unplug devices 1 & 2 then I can get midi events from devices 3 & 4. Again, when using them with other midi programs they all work together fine.

I suspect that this may have something to do with all of them being assigned the same "id".

Environment: Linux Ubuntu 20.04 JZZ current version from CDN (jsdelivr) Google Chrome: Version 89.0.4389.72 (Official Build) (64-bit) Jazz-MIDI plugin for Chrome: Version 1.0.2.0

jazz-soft commented 3 years ago

Hi! Thank you for sharing... Unfortunately, there is no quick solution at the moment. Current version of JZZ has problems handling multiple ports with the same names. I hope I can fix that in the future versions. Is it possible to rename your ports?

martyphelan commented 3 years ago

Thanks for the prompt reply! At this time I do not know where/how to change the port names/ids.

For a workaround, I ended up configuring additional ALSA midi thru ports and used aconnect to connect the "ghosted" keyboards to those thru ports. The additional thru ports each had unique names. For those that might want to do this, simply create a configuration file: /etc/modprobe.d/my-midi-thru-ports.conf containing "options snd-seq-dummy ports=4" (ports=as many as you want). Then reboot.

If JZZ is relying on the "engine" for the ids and names, that may be where the problem is. In my case it is using "webmidi" as its engine. Not sure who/where to go for webmidi code.

I believe the problem is the assignment of non-unique id's in the "engine". Per the Web MIDI API W3C working draft 17, "A unique ID of the port. ... The User Agent MUST ensure that the id is unique to only that port."

ALSA maintains unique ids like "14:0" for each midi port. Any clues where I might go to pursue this problem?

Thanks, Marty

T-vK commented 2 years ago

Is this still an issue? I'm currently in the situation where I need to allow the user to specify which midi device exactly to use. I was thinking about a combination of name and index, but my understanding is that the onChange listener for example only reports info objects which do not have the index stored in them, but just the name.

I'm currently currently using the JZZ class in a project where it needs to automatically connect to a given MIDI device and also automatically reconnect as soon as possible after a disconnect. I was thinking that a combination of name and a number (e.g. Foo 1, Foo 2) could help connecting and reconnecting to a specific MIDI device. But I think this method would be flawed. E.g. when onChange is fired, how am I supposed to know if an added device is the one that I want to reconenct to or if it just happens to have the same name?

const jzz = JZZ()

const midiDevice = {
    name: 'CH345 MIDI 1',
    index: 2
} // Second MIDI Input device with name "CH345 MIDI 1"

function onReceiveMsg(msg) {
    console.log(msg)
}

function onMidiDeviceChange(changes) {
        if (changes.inputs.added.length>0) {
            const matchingDevices = changes.inputs.added.filter(dev=>dev.name()===midiDevice.name)

            if () { // How do I know if matchingDevices contains the device I was intending to stay connected to? 
                autoConnect() 
            }
        }
}

function autoConnect() {
    const availableMidiInPortsInfo = jzz.info().inputs
    const matchingMidiInPortsInfo = availableMidiInPortsInfo.filter(p=>p.name()===midiDevice.name)
    if (matchingMidiInPortsInfo.length === 0) {
        console.error('MIDI device not found...')
        autoConnect()
    } else {
        const desiredMidiInPortInfo = matchingMidiInPortsInfo[midiDevice.index]
        const desiredMidiInPortIndex = availableMidiInPortsInfo.indexOf(desiredMidiInPortInfo)
        const desiredMidiInPort = jzz.openMidiIn(desiredMidiInPortIndex)
        desiredMidiInPort.connect(onReceiveMsg)
    }
}

jzz.onChange(onMidiDeviceChange)
jzz.refresh()
autoConnect()
jazz-soft commented 2 years ago

I still don't have a good solution. I guess I'm going to increase the priority...