Search code examples
javascriptweb-audio-api

AudioContext produces no sound when defined outside of a function


According to the documentation, you should invoke AudioContext only once per page.

When I try to use it correctly and invoke it outside of the function, no sound is produced. The context variable is defined and populated, no errors are thrown in the console, it just doesn't produce sound.

When I invoke it inside a function that is called every 'onClick' event it initially works, but I unsurprisingly get an error the 6th time I invoke it, as I have reached the limit of times I can invoke it.

var context = new AudioContext; //when defined here, no sound is produced

function playChord() {
    var context = new AudioContext; //when defined here, sound is produced (only 6 times)

    var time = 0.05;
    var frequencies = [...];
    frequencies.forEach(function(frequency) {
        var oscillator = context.createOscillator();
        var gain = context.createGain();

        gain.gain.setValueAtTime(0, audioContext.currentTime);
        gain.gain.linearRampToValueAtTime(1, time);
        gain.gain.linearRampToValueAtTime(0, time + 60*0.25);

        oscillator.frequency.value = frequency;
        oscillator.connect(gain);
        gain.connect(context.destination);

        oscillator.start(...);
        oscillator.stop(...);
    });
};

Why does merely moving the instantiation of the context variable stop my browser (chrome) from producing sound?


Solution

  • Found my own issue. Unsurprisingly it was a silly mistake. The issue was with this line

    gain.gain.linearRampToValueAtTime(1, time);
    

    I wasn't setting the gain to 1 at a time relative to the context.currentTime.

    The reason it was working correctly when AudioContext was defined inside the function was because the 'currentTime' starts counting at 0, and was close enough to still affect the oscillator in question.

    When it was moved out of the function, the time was well past the range in which the linearRamptoValueAtTime() would affect anything. Just add context.currentTime, and everything will be ok :)

    gain.gain.linearRampToValueAtTime(1, context.currentTime + time);
    gain.gain.linearRampToValueAtTime(0, context.currentTime + time + 0.25);