Have trouble stopping the sound

I have an odd situation that I can’t seem to figure out what’s happening.
I call for a sound effect to be played at 00:30 later. It’s something like this:

a.play(30, x, x);

But because of certain changes, I would like to stop that before it has started at 00:20… so I call

a.stop(); //instead of using dispose because I might need to use this sound again

But the sound still plays…and I couldn’t stop it from playing.

Maybe try play(0) before stop(0)?

Maybe ISOUNDOPTIONS needs a “queued” t-minus property?

In your case the easiest might be to do the timing manually:

var timeout = setTimeout(() => {
  a.play();
}, 30000);

...

// Stop it
clearTimeout(timeout);
a.stop();

Unfortunately that doesn’t work in my case. Here is the issue, it’s an automated piano-playing system. So it’s calling all the notes at once based on the database (with onsets and pitches) I have. The methods above still can’t kill the sound.

for (var i = 0; i < pitches.length; i++) {
btff[thenote].setVolume(1, 0.01);
btff[thenote].play(onsets[i] / 1000 * usertempo, 0, lengths[i] / 1000 * usertempo);}

I do not understand, cause in this case it would have not even played, could you create a repro in the playground ???

Only one sound is enough.

for (var i = 0; i < pitches.length; i++) {
musics[thenote].setVolume(1, 0.01);
musics[thenote].play(onsets[i] / 1000 * usertempo, 0, lengths[i] / 1000 * usertempo);
}

var StopMusic = function stopmus() {
for (var i = 0; i < 88; i++)
musics[i].stop();
}

Think you would better to not use BABYLON.Sound, but WebAudio directly. Once something is scheduled on the audioContext, there is no way to stop it. It can be delayed using context.suspend(), but will resume later.

You can actually just context.close(), & then create a new context. Not sure if you can do that to the BABYLON.Sound context cleanly.

Edit: try sound.dispose().

This should work:

for (var i = 0; i < pitches.length; i++) { 
  musics[thenote].setVolume(1, 0.01);
  musics[thenote].timeout = setTimeout(() => {
    musics[thenote].play(0, 0, lengths[i] / 1000 * usertempo);
  }, onsets[i] / 1000 * usertempo);
}

var StopMusic = function stopmus() {
for (var i = 0; i < 88; i++)
  clearTimeout(musics[i].timeout);
  musics[i].stop();
}

I see.

When I use the code below, I get only one pitch all the way without the other 87 pitches…LOL

Also, the stop function still doesn’t’ work

for (var i = 0; i < pitches.length; i++) {
thenote = c88(pitches[i].replace(“t”, “”));
musics[thenote].timeout = setTimeout(() => {
musics[thenote].setVolume(1, 0.01);
musics[thenote].play(0, 0, lengths[i] / 1000 * usertempo);
}, onsets[i] * usertempo);
}

I guess musics[thenote].setVolume(1, 0.01); should be before the setTimeout :slight_smile: but could you share a playground ??? it would be way easier ?

I tried that as well. Didn’t work. Sorry I’m not able to share. But here is the working product (without the stop function): www.findbach.com/beta4 and www.findbach.com/beta5

Gotta say BABYLON.Sound is geared to playing sound files, not making music with many, many short duration buffers. I see you are an actual musician. If you are ok with using audio that you did not originate, WebAudioFonts is a library which handles what you wish to do in a more direct way.

I am doing not doing 88 keys, just 72, but here is a test file I made about 6 weeks ago. https://youtu.be/O-pK260uKxw

3 Likes

Very cool. I’ll check this out! So it’s possible to combine my 88 keys on BabylonJS with sounds from WebAudioFonts, right?

No, there are “fonts” of the various instruments, hundreds, which are in this repo,https://github.com/surikov/webaudiofontdata.

That repo is implemented as a web site. You can check out each instrument, from here https://surikov.github.io/webaudiofontdata/sound/

Once you have an instrument you want (that’s why I asked if you were ok with not originating the audio), view the corresponding code from the repo to get you started.

he also has his own threeJS based collaboration tool, https://github.com/surikov/riffshare

Here is an example from Metallica.

You can of course run your code with BabylonJS too.

Right now I’m working on a hybrid. Why does it keep cycling on one pitch? Am I missing something in my JS? Timing seems right.

var AudioContextFunc = window.AudioContext || window.webkitAudioContext;
var audioContext = new AudioContextFunc();
var player = new WebAudioFontPlayer();
player.loader.decodeAfterLoading(audioContext, ‘_tone_0001_GeneralUserGS_sf2_file’);
for (var i = 0; i < pitches.length; i++) {
thenote = c88(pitches[i].replace(“t”, “”));
setTimeout(() => {
player.queueWaveTable(audioContext, audioContext.destination, _tone_0001_GeneralUserGS_sf2_file, 0, thenote + 21, lengths[i] / 1000 * usertempo, 0.5);
}, onsets[i] * usertempo);
}

I think thenote inside the timeout function is executed not like thenote outside the timeout function… Is there a way to pass thenote inside the settimeout function?

when I tried console.log(thenote) inside and outside the settimeout function…they are different. one updates, the other got stuck

I think thenote inside the timeout function is executed not like thenote outside the timeout function… Is there a way to pass thenote inside the settimeout function?

when I tried console.log(thenote) inside and outside the settimeout function…they are different. one updates, the other got stuck

ohhhhhh my bad it is due to the capture in the loooooop …

you can simply do :frowning:

(let i = 0; i < pitches.length; i++) {
thenote = c88(pitches[i].replace(“t”, “”));
setTimeout(() => {
player.queueWaveTable(audioContext, audioContext.destination, _tone_0001_GeneralUserGS_sf2_file, 0, thenote + 21, lengths[i] / 1000 * usertempo, 0.5);
}, onsets[i] * usertempo);
}

basically replace the var with a let…

sorry I don’t see the let…