javascriptweb-audio-apiaudiocontext

WebAudio API change volume for one of sources


I'm reading this article

My goal is to play two sounds at the same time. One sound is in a different volume. Having regular "audio" tags is not a solution because it's not working well on mobile devices. So I started to dive into Web Audio API.

I wrote the code below, that works well across all devices. The single issue - I can't figure out how to control the volume. Code from example is not working(

Please help 🙏

function init() {
// Fix up prefixing
window.AudioContext = window.AudioContext || window.webkitAudioContext;
context = new AudioContext();
bufferLoader = new BufferLoader(
    context,
    [
    'mp3/speech.mp3',
    'mp3/bg-melody.mp3',
    ],
    finishedLoading
    );

bufferLoader.load();
}

function finishedLoading(bufferList) {
   document.querySelector('.message').innerHTML = "Ready to play"
  // Create two sources and play them both together.
  source1 = context.createBufferSource();
  source2 = context.createBufferSource();
  source1.buffer = bufferList[0];
  source2.buffer = bufferList[1];
  source1.connect(context.destination);

  // This code is not working
  var gainNode = context.createGain();
  gainNode.gain.value = 0.1;
  source2.connect(gainNode);
  source2.connect(context.destination);
 source2.loop = true;
}

Update:

This change fixed the issue

source2.connect(gainNode).connect(context.destination);

Solution

  • The connect() method returns an AudioNode, which must then have connect() called on it so the Nodes are chained together:

    const gainNode = context.createGain()
    source2
      .connect(gainNode)
      .connect(context.destination)
    

    const $ = document.querySelector.bind(document)
    const $$ = document.querySelectorAll.bind(document)
    const audioCtx = new (AudioContext || webkitAudioContext)()
    const gainNodes = [
      audioCtx.createGain(),
      audioCtx.createGain(),
    ]
    
    const url = 'https://opus-bitrates.anthum.com/audio/music-96.opus'
    loadAudio(url, gainNodes[0])
    loadAudio(url, gainNodes[1],
      15.132 / (32 * 2),  // 32 beats in 15.132s
    )
    
    $$('input').forEach((el, i) => {
      gainNodes[i].gain.value = el.value / 100
      el.oninput = () => gainNodes[i].gain.value = el.value / 100
    })
    $('#play').onclick = play
    $('#pause').onclick = pause
    
    async function loadAudio(url, gainNode, delayS = 0) {
      const buffer = await (await fetch(url)).arrayBuffer()
      const source = audioCtx.createBufferSource()
      source.buffer = await audioCtx.decodeAudioData(buffer)
      source.loop = true
      source
        .connect(gainNode)
        .connect(audioCtx.destination)
      source.start(delayS)
    }
    
    function play() { audioCtx.resume() }
    function pause() { audioCtx.suspend() }
    <button id="play">Play</button><button id="pause">Pause</button><br />
    <label><input type="range" value="50" max="100" /> Track 1</label><br />
    <label><input type="range" value="50" max="100" /> Track 2</label>