Oscillator not stoppingor ramping down in Web Audio API

46 Views Asked by At

I am trying to make a metronome as part of a bigger app by using an oscillator that switches on and off at set intervals in Web Audio Api, I have tried both the Start()/Stop() method and the LinearRampToValue(), in the code below. The oscillator plays at the correct bpm/interval but it won't leave a silent gap between clicks (I hope it makes sense)...what am I doing wrong? it's more of a pulse than a click with gaps as of now. Thanks!

function startMetronome() {
    document.getElementById("startStopButton").innerText = "Stop";
    document.getElementById("startStopButton").style.background = "black";
    playingMetronome = true;
    const bpm = parseInt(bpmInput.value);
    const interval = (60 / bpm) * 1000;

    oscillator = audioContext.createOscillator();
    oscillator.frequency.value = 440;
    oscillator.type = "triangle";

    let gainNode = audioContext.createGain();
    oscillator.connect(gainNode);
    gainNode.connect(audioContext.destination);

    const now = audioContext.currentTime;
    let nextStartTime = now;
    oscillator.start(nextStartTime);
    gainNode.gain.setValueAtTime(1, nextStartTime);

    intervalId = setInterval(function () {
        nextStartTime += interval / 1000;
        gainNode.gain.linearRampToValueAtTime(0, nextStartTime + 0.5);
        oscillator = audioContext.createOscillator();
        oscillator.frequency.value = 440;
        oscillator.type = "triangle";
        const newGainNode = audioContext.createGain();
        oscillator.connect(newGainNode);
        newGainNode.connect(audioContext.destination);
        oscillator.start(nextStartTime);
        newGainNode.gain.setValueAtTime(1, nextStartTime);
        gainNode = newGainNode;
    }, interval);
} 

the version with .start()/.stop()

function startMetronome() {
    document.getElementById("startStopButton").innerText = "Stop";
    document.getElementById("startStopButton").style.background = "black";
    playingMetronome = true;
    const bpm = parseInt(bpmInput.value);
    const interval = (60 / bpm) * 1000;

    oscillator = audioContext.createOscillator();
    oscillator.frequency.value = 440;
    oscillator.type = "triangle";
    oscillator.connect(audioContext.destination);

    const now = audioContext.currentTime;
    let nextStartTime = now;
    oscillator.start(nextStartTime);

    intervalId = setInterval(function () {
        nextStartTime += interval / 1000;
        oscillator.stop(nextStartTime + 0.5);
        oscillator = audioContext.createOscillator();
        oscillator.frequency.value = 440;
        oscillator.type = "triangle";
        oscillator.connect(audioContext.destination);
        oscillator.start(nextStartTime);
    }, interval);
}
1

There are 1 best solutions below

0
On

I managed to fix that moving a few things around. I mixed both version so that using gainNode I can fade the oscillator out each time.

function startMetronome() {
    document.getElementById("startStopButton").innerText = "Stop";
    document.getElementById("startStopButton").style.background = "black";
    playingMetronome = true;
    const bpm = parseInt(bpmInput.value);
    const interval = (60 / bpm) * 1000;

    const gainNode = audioContext.createGain();
    gainNode.connect(audioContext.destination);
    gainNode.gain.setValueAtTime(1, audioContext.currentTime);

    function colorChange() {
        setTimeout(function () {
            document.getElementById("startStopButton").style.background = "black";
        }, 7);
        document.getElementById("startStopButton").style.background = "gray";
    }

    oscillator = audioContext.createOscillator();
    oscillator.frequency.value = 440;
    oscillator.type = "triangle";
    oscillator.connect(gainNode);

    const now = audioContext.currentTime;
    let nextStartTime = now;
    oscillator.start(nextStartTime);
    gainNode.gain.setValueAtTime(1, nextStartTime);
    gainNode.gain.linearRampToValueAtTime(0, nextStartTime + 0.1);
    oscillator.stop(nextStartTime + 0.2);
    colorChange();

    intervalId = setInterval(function () {
        nextStartTime += interval / 1000;
        oscillator = audioContext.createOscillator();
        oscillator.frequency.value = 440;
        oscillator.type = "triangle";
        oscillator.connect(gainNode);
        oscillator.start(nextStartTime);
        gainNode.gain.setValueAtTime(1, nextStartTime);
        gainNode.gain.linearRampToValueAtTime(0, nextStartTime + 0.1);
        oscillator.stop(nextStartTime + 0.2);
        colorChange();
    }, interval);
}