'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 1:[1]

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>

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1