How can I make the 'noteon' and 'noteoff' listeners reactive to hot-swapped midi devices?

Bonjour :-)

I'm Working on a web app in Svelte and I currently have addListener methods for 'connected', 'disconnected', 'noteon' and 'noteoff' and they all work fine if a MIDI device is connected to my computer when the page loads.

What I want, however, is for the 'noteon' and 'noteoff' listeners to work reactively to devices that are hot-swapped (connected and/or disconnected) after the page has already loaded.

For example, if I load the page with a midi keyboard connected then the 'noteon' and noteoff' listeners work just fine, but if I then disconnect and re-connect the midi keyboard, then the 'noteon' and 'noteoff' listeners don't seem to work anymore, until I re-load the page, at which point they will work again. To be clear, the 'connected' and 'disconnected' listeners DO work just fine in reaction to midi devices being hot-swapped.

So, how might I go about adjusting my code so that my desired behavior will work?

Here is the code I currently have that works if a midi keyboard is already connected, but doesn't fully work when the the midi keyboard is disconnected and then reconnected...

 // The variables like MIDIConnection and MIDINoteNumber are svelte stores defined outside the Web MIDI JS code

WebMidi.enable(function (error) {

  if (error) {
    console.log('WebMidi could not be enabled.', error);
  } else {
    console.log('WebMidi enabled!');
  }

  // Octave Offset to make middle C read as C3, rather than C4
  WebMidi.octaveOffset = -1;

  // Reacting when a new device becomes available
  WebMidi.addListener('connected', function(event) {

    MIDIConnection.update( data => {
     data = `MIDI is connected :-)`;
     return data;
    });

  });

  // Reacting when a device becomes unavailable
  WebMidi.addListener('disconnected', function(event) {

    MIDIConnection.update( data => {
      data = `MIDI is disconnected :-(`;
      return data
    });

  });

  // Retrieve an input by index

  input = WebMidi.inputs[0];

  input.addListener('noteon', 'all', function(event) {

    MIDINoteNumber.update(noteNumber => {
      noteNumber.push(`${event.note.number}`)
      return noteNumber
    });
  });

  input.addListener('noteoff', 'all', function(event) {

    MIDINoteNumber.update(noteNumber => {
      noteNumber = noteNumber.filter(e => e !== `${event.note.number}`)
      return noteNumber
    });

  });

});

Thank you for any help that you can give in advance :-)

Comments

  • What you want to do is add the noteon listener inside the callback for the connected event. This way, each time the device is connected, the listener for the noteon event will be properly added. Note that connected events will be fired right after WebMidi.js is enabled for all currently connected MIDI devices.

    Hope this helps.

  • ok, I will give that a try :-)

    Merci!

  • aha!

    That kind of works!

    ...new issue, though...

    I can now plug and unplug a midi keyboard and the 'noteon' and 'noteoff' listeners are added without refreshing the page, but now whenever I play a note on the midi keyboard that note is added twice to the array of current notes...I did not change the code at all, I just copied and pasted it into the 'connected' listener...and to be clear, it was not doing that before I moved the 'noteon' and 'noteoff' listeners inside the 'connected' listener.

    ...dunno if this might be a WebMIDI JS issue or a svelte store issue...

    ...any thoughts?


    This is what it did before moving the note listeners (notice the MIDI Note Name(s) and MIDI Note Number(s):

    And this is what it is doing now:


  • edited January 15

    hmmmm...haven't changed the code at all...as far as I know, but now it's going nuts! Just playing C3 once and...


    wrong device name as well, as it's still the Alesis Q61, the other thing...

  • UPDATE: Quit Chrome and restarted it, and the issues not getting the right device and the note being written many times went away...

    Still showing a double for every note, however...

  • edited January 21

    It's hard to say without seeing the actual code. Just make sure you are not adding the listener twice. As stated in the doc, the connected event may be fired more than once at startup if there are multiple devices.

    You can check if an Input already has a listener with hasListener() .