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