Can't toggle start()/stop() for ToneJS oscillators

765 Views Asked by At

I have some JS cobbled together with the ToneJS library to toggle some sounds on/off.

function togglePlay() {
  const status = Tone.Transport.state; // Is either 'started', 'stopped' or 'paused'
  const element = document.getElementById("play-stop-toggle");
  if (status == "started") {
    element.innerText = "PLAY";
    Tone.Transport.stop()
  } else {
    element.innerText = "STOP";
    Tone.Transport.start()
  }

  document.querySelector('#status').textContent = status;
}


var filter = new Tone.Filter({
  type: 'lowpass',
  Q: 12
}).toMaster()



var fmOsc   = new Tone.AMOscillator("Ab3", "sine", "square").toMaster()
var fmOsc2  = new Tone.AMOscillator("Cb3", "sine", "square").toMaster()
var fmOsc3  = new Tone.AMOscillator("Eb3", "sine", "square").toMaster()

var synth   = new Tone.MembraneSynth().toMaster()

//create a loop
var loop = new Tone.Loop(function(time){
//    synth.triggerAttackRelease("A1", "8n", time)
//    fmOsc.start()
    fmOsc2.start()
    fmOsc3.start();
}, "4n")


//play the loop between 0-2m on the transport
loop.start(0)
// loop.start(0).stop('2m')

Inside the loop I have a drum beat that I have commented out currently. When it was commented in, the togglePlay() function would start and stop it as expected.

However, the fmOsc2 and fmOsc3 functions will start when I toggle start, but do not terminate when I toggle stop.

For reference, here is what the HTML side looks like: <div class="trackPlay" id="play-stop-toggle" onclick="togglePlay()">PLAY</div>

How can I get the fmOsc2 and fmOsc3 functions to toggle with the button state?

1

There are 1 best solutions below

0
On BEST ANSWER

The Tone Transport is not meant to be used in this way, either for starting or stopping oscillators (it can start/stop oscillators, of course, but I don't think that it's doing what you want it to do) :-)

If you add a console.log within the function that you're passing to Tone.Loop, you will see that it's being called repeatedly i.e., you're calling fmOsc.start() over and over again. (It does make sense to do the repeating drum beat in that Loop though)

Calling Tone.Transport.stop() does not stop the oscillators -- it only stops the Loop / Transport function (i.e., the "time keeping"). The drum beat makes it obvious -- pressing Stop in your UI kills the drums, but the oscillators keep oscillating.

The easiest / most direct (and only?) way of stopping the oscillators is to call .stop() on them:

  if (status == "started") {
    element.innerText = "PLAY";
    Tone.Transport.stop()
    fmOsc2.stop()
    fmOsc3.stop()
  } else {
    // ... [snip] ...