javascriptweb-audio-apijavascript-oscillator

Using WebAudio to play a sequence of notes - how to stop asynchronously?


I am using WebAudio to play a sequence of notes. I have a playNote function which works well; I send it note frequency and start and stop times for each note. The generation of the sequence parameters occurs before the actual sound starts, which is a little confusing. The function just creates an oscillator for every note. (I tried other methods and this is the cleanest).

But I would like to stop the sequence asynchronously (e.g. when an external event occurs). I tried setting up a master Gain node that could be used to gate the output, but it seems it needs to be "inside" the function, so it can't be controlled later on. If I try and turn off my gain object inside the function then it is too late - because the start & stop times have already been passed to the function.

Here is my function:

function playNote(audioContext,frequency, startTime, endTime, last) {
  gainNode = audioContext.createGain(); //to get smooth rise/fall
  oscillator = audioContext.createOscillator();
  oscillator.frequency.value=frequency;
  oscillator.connect(gainNode);
  gainNode.connect(analyserScale); //analyser is global
  analyserScale.connect(audioContext.destination);
  gainNode.gain.exponentialRampToValueAtTime(toneOn,  startTime + trf);
  gainNode.gain.exponentialRampToValueAtTime(toneOff, endTime+trf);
  oscillator.start(startTime);
  oscillator.stop(endTime);
}

Any help appreciated!


Solution

  • This does it: Web Audio API: Stop all scheduled sounds from playing. The solution is to keep track of the scheduled oscillators with an array.

    The function now becomes: var oscs = []; //list of oscillators

    function playNote(audioContext,frequency, startTime, endTime, last, index) {
      gainNode = audioContext.createGain(); //to get smooth rise/fall
    
      oscillator = audioContext.createOscillator();
      oscillator.frequency.value=frequency;
      oscillator.connect(gainNode);
      //keep track of alll the oscs so that they can be switched off if scale is stopped by user
        oscs[index] = oscillator;
    
      gainNode.connect(analyserScale); //analyser is global
      analyserScale.connect(audioContext.destination);
      gainNode.gain.exponentialRampToValueAtTime(toneOn,  startTime + trf);
      gainNode.gain.exponentialRampToValueAtTime(toneOff, endTime+trf);
      oscillator.start(startTime);
      oscillator.stop(endTime);
    }
    

    Then code to stop the oscillators:

    for(let i=0; i<oscs.length; i++) {
        if(oscs[i]){
          oscs[i].stop(0);
        }
      }