Monique Dumont
Monique Dumont

Reputation: 51

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.

Upvotes: 2

Views: 481

Answers (1)

Monique Dumont
Monique Dumont

Reputation: 51

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;
}

Upvotes: 3

Related Questions