Search code examples
javascriptweb-audio-apip5.js

setTimeout delay of ~100ms when set to 0


As a personnal project I'm trying to reproduce a piano roll (like the ones in DAWs) using p5.js, Web Midi Api and Web Audio Font. When implementing the play function, I first did it with a while loop, but I realised that I couldn't stop the track from playing since javascript is single thread. So I tried to play the notes recursively with this code :

function playTrackRecursively(id) {
  var delay = 0;
  isTrackPlaying = true;
  playNote(mainTrack[id][0], 0);
  if (id < mainTrack.length - 1 && isTrackPlaying){
    delay = mainTrack[id + 1][1] - mainTrack[id][1];
    recursiveID = setTimeout(function() {
      playTrackRecursively(id + 1);
    }, delay * 1000);
  }
  isTrackPlaying = false;
}

It's working fine except that when I try to play multiple notes simultaneously, they play each one after the other with a delay of roughly 100ms.

What could I do ? Thank you.


Solution

  • I found the solution with the help of @user120242 in the comments.

    So it was just a mix between the two solutions that I had. I go recursively through every timestamp in the track, and each time I get all the notes that start here in an array, then I play them one by one (it's fast enough to give the illusion of simultaneity). So despite what I read, it was indeed possible.

    Here is the code :

    function playTrackRecursively(id) {
      var delay = 0;
      isTrackPlaying = true;
      var notesBatch = getNotesOfSameStartTime(mainTrack[id][1]);
      playNotesInBatch(notesBatch);
      if (id + notesBatch.length < mainTrack.length && isTrackPlaying){
        delay = mainTrack[id + notesBatch.length][1] - mainTrack[id][1];
        recursiveID = setTimeout(function() {
          playTrackRecursively(id + notesBatch.length);
        }, delay * 1000);
      }
    }
    
    function playNotesInBatch(notes) {
      for (var i = 0; i < notes.length; i++) {
        playNote(notes[i][0], notes[i][1]);
      }
    }
    
    function getNotesOfSameStartTime(startTime) {
      var indexArray = []
      for (var i = 0; i < mainTrack.length; i++) {
        if (mainTrack[i][1] == startTime) {
          indexArray.push(i);
        }
      }
      var batch = mainTrack.slice(indexArray[0], indexArray[indexArray.length - 1] + 1);
      return batch;
    }