justinlatimer / node-midi

A node.js wrapper for RtMidi providing MIDI I/O
MIT License
745 stars 118 forks source link

Disconnect/Reconnect events? #226

Open tiagosiebler opened 2 years ago

tiagosiebler commented 2 years ago

Firstly, thanks for making this! Having fun with an 8x8 neotrellis button matrix over midi.

If I disconnect the device (after successfully connecting before), everything works perfectly once I reconnect the device.

I was hoping to send midi events to the device after it reconnects (e.g. to reset device state such as brightness, colour, etc per button). Is there a way to detect if a midi device disconnected & reconnected? I had a brief glance at the c++ implementation of input.cpp but I lack familiarity with how that might look.

I would imagine to emit an event for this, so an implementation like mine can use that as a trigger to re-sync all device states.

aolsenjazz commented 2 years ago

The only way I've found to do this (using this MIDI library) is to constantly poll available devices. I've done this in a project of mine and made a similar service available here.

Likely not the solution you're looking for, but I was in a similar situation.

tiagosiebler commented 2 years ago

The only way I've found to do this (using this MIDI library) is to constantly poll available devices. I've done this in a project of mine and made a similar service available here.

Likely not the solution you're looking for, but I was in a similar situation.

Thank you, cool projects too!

GottZ commented 1 year ago

.. well the console says MidiOutWinMM::sendMessage: error sending MIDI message. when sending data to a disconnected device.

it would be much cleaner if this message was thrown into a error handler on midi.Output().. like literally.. output.on("error"... or something.

this is kind of annoying tbh.

also polling the device list is not really a good idea.

imagine a usb midi device disconnecting for a second due to a loose usb connection.. it could re-appear in polling before you check the device list if it's firmware takes no time to initialize.

@tiagosiebler: rtmidi-python exits when this occurs..

so.. it is catchable somwhow. (i'm digging through sources atm)

there is also this in node-midi:

https://github.com/justinlatimer/node-midi/blob/1356985b1fc0aef919dc0e7abb8918ebe83522be/src/input.cpp#L217-L226

https://github.com/justinlatimer/node-midi/blob/1356985b1fc0aef919dc0e7abb8918ebe83522be/src/output.cpp#L145-L154

which does not reset to false when I unplug my midi device:

image

let tick = 0;
while(true) {
    tick++;
    tick %= 2;
    try {
        console.log("input:", input.isPortOpen());
        console.log("output:", output.isPortOpen());
        output.sendMessage([176,71,tick * 127]);
    } catch (e) {
        console.log("such e", e);
    }
    await sleep(200);
}

first of all, I'd suggest upgrading to rtmidi 6.0.0 http://www.music.mcgill.ca/~gary/rtmidi/ which has been released on 3rd of august this year.

secondly I suggest adding this to node-midi https://github.com/justinlatimer/rtmidi/blob/c01a721217ab5b83a44e9b01f94cbea2e952a995/RtMidi.h#L79-L119

the authors page also lists a couple error handling cases. might be a good idea to include them in node-midi. http://www.music.mcgill.ca/~gary/rtmidi/index.html#error

will leave for today in half an hour so.. I'll fork this tomorrow and see what I can do.. (cuz when I set my laptop into sleep, I want it to reconnect after it wakes back up)

edit: this half hour is over now.. my solution to this problem so far: image

const sleep = time => new Promise(resolve => setTimeout(resolve, time));

const input = new midi.Input();
const output = new midi.Output();

const getMidiDevices = () => {
    const inp = {}, out = {};
    const inpc = input.getPortCount();
    const outc = output.getPortCount();
    for (let i = 0; i < inpc; i++) {
        inp[input.getPortName(i)] = i;
    }
    for (let i = 0; i < outc; i++) {
        out[output.getPortName(i)] = i;
    }

    return {inp, inpc, out, outc};
};

let midiDevices = getMidiDevices();
console.log(midiDevices);

input.openPort(midiDevices.inp.nanoKONTROL2);
output.openPort(midiDevices.out.nanoKONTROL2);

// ...
// various stuff that just sends commands to voicemeeter etc.
// ...

let tick = 0;
while(true) {
    tick++;
    tick %= 2;
    try {
        const old = JSON.stringify(midiDevices);
        midiDevices = getMidiDevices();
        if (old !== JSON.stringify(midiDevices)) {
            console.log("change detected:", midiDevices);

            console.log(midiDevices.inp);
            if (!("nanoKONTROL2" in midiDevices.inp))
                console.log("disconnecting");
                if (input.isPortOpen()) input.closePort();
            else {
                console.log("connecting input to", midiDevices.inp.nanoKONTROL2);
                if (!input.isPortOpen()) input.openPort(midiDevices.inp.nanoKONTROL2);
            }

            if (!("nanoKONTROL2" in midiDevices.out))
                console.log("disconnecting");
                if (output.isPortOpen()) output.closePort();
            else {
                console.log("connecting output to", midiDevices.out.nanoKONTROL2);
                if (!output.isPortOpen()) output.openPort(midiDevices.out.nanoKONTROL2);
            }
        }
        if (output.isPortOpen()) output.sendMessage([176,71,tick * 127]);
    } catch (e) {
        console.log("such e", e);
    }
    await sleep(200);
}
fedekrum commented 8 months ago

Hi, Any progress on this issue? Any suggestion on what other lib does this?

aolsenjazz commented 8 months ago

Have you seen this fork: https://github.com/Julusian/node-midi ?

Actively maintained, contains several fixes and QOL improvements. Not sure if it solves the problems you’ve highlighted, but probably worth a check.