Search code examples
javascriptasync-awaitweb-midi

Javascript: waiting for response from a MIDI device before continuing loop in asynchronous communication


I am using MIDI protocol with web MIDI API to communicate to a MIDI decive. I want to send MIDI messages to this device each time waiting for a response or a timeout to send next message. the response are received through.

I want the following:

//gotMIDImessage will be called when the message is received
midiIn.onmidimessage = gotMIDImessage;
function gotMIDImessage(messageData) {
  //Do something with the data
}
//send bunch of messages in a loop and wait for responce each time
function askDevice(){
  for (var i=0;i<n;i++){
    for (var j=0;j<m;j++){
      midiOut.send([0xF0,0x52,0x00,0x61,0x09,0x00,i,j,0xF7]);
      //wait for gotMIDImessage or timeout to continue and do something about the response hre
    }
  }
}

This is my first attempt:

midiIn.onmidimessage = gotMIDImessage;

function gotMIDImessage(messageData) {
  received.innerHTML=received.innerHTML+"<br>"+messageData.data;
  return new Promise(resolve => {
    setTimeout(() => {
      resolve(1);
    }, 100);
  });
}


async function askDevice() {
  var o = {'data': 'await'};
  for (var i=0;i<5;i++){
    for (var j=0;j<3;j++){
      var res=await gotMIDImessage(o);
      midiOut.send([0xF0,0x52,0x00,0x61,0x09,0x00,i,j,0xF7]);
    }
  }
}

It obviously doesn't work because await gotMIDImessage(); calls the function gotMIDImessage() but this function fires automatically when a message is received.

How can I manage to do this?

EDIT: I tried also this version which doesnt seem to work

received=null;
midiMessage=null;
function gotMIDImessage(messageData) {
  received=true;
  midiMessage=messageData.data;
}

function sendWait(message){
  received=false;
  midiOut.send(message);
  function waitForIt(){
    if(!received){
      setTimeout(function(){waitForIt()},1000);
    }else{
      alert("received");
    }
  } 
  return midiMessage;
}
function askDevice() {
  var o = {'data': 'await'};
  for (var i=0;i<5;i++){
    for (var j=0;j<3;j++){
      res=sendWait([0xF0,0x52,0x00,0x61,0x09,0x00,i,j,0xF7]);
    }
  }
}

res is always null and I receive all the 15 messages after the loop is finished


Solution

  • You can do it like this:

    function awaitReply(msg, timeout = 5000) {
      return new Promise((resolve, reject) => {
        const gotMidiMessage = msg => {
          midiIn.removeEventListener('midimessage', gotMidiMessage); // Removing the event listener again
          resolve(msg);
        };
        midiIn.addEventListener('midimessage', gotMidiMessage);
        midiOut.send(msg);
        setTimeout(() => {
          // Don't forget to remove the event listener
          midiIn.removeEventListener('midimessage', gotMidiMessage);
          /* Resolve the Promise anyway ... */
          resolve(null);
          /* ... OR reject it, but then you have to catch the error in the call */
          // reject('Error message');
        }, timeout);
      });
    }
    
    async function askDevice() {
      for (let i = 0; i < 5 ; i++) {
        for (let j = 0; j < 3; j++) {
          let res = await awaitReply([0xF0, 0x52, 0x00, 0x61, 0x09, 0x00, i, j, 0xF7]);
        }
      }
    }
    

    Here is a working example:

    function awaitReply(msg) {
      return new Promise(resolve => {
        const gotMidiMessage = messageData => {
          window.removeEventListener('midimessage', gotMidiMessage); // Removing the event listener again
          resolve(messageData);
        };
        window.addEventListener('midimessage', gotMidiMessage);
        midiOut.send(msg);
      });
    }
    
    async function askDevice() {
      for (let i = 0; i < 5 ; i++) {
        for (let j = 0; j < 3; j++) {
          let res = await awaitReply([0xF0, 0x52, 0x00, 0x61, 0x09, 0x00, i, j, 0xF7]);
          // Logging your iterator variable which you want to send
          console.log(res.detail[6], res.detail[7]);
        }
      }
    }
    
    const midiOut = {
      send(msg) {
        return setTimeout(() => {
          let evt = new CustomEvent('midimessage', { detail: msg });
          window.dispatchEvent(evt);
        }, 1000);
      }
    };
    
    askDevice();