11

I have following AudioContext() sound object in JavaScript.
Its volume is 100%. I want to play its volume in 10% (where volume = 0.1).
How can I reduce its volume to 10%?

const aCtx = new AudioContext();
let source = aCtx.createBufferSource();
let buf;
fetch('https://dl.dropboxusercontent.com/s/knpo4d2yooe2u4h/tank_driven.wav') // can be XHR as well
  .then(resp => resp.arrayBuffer())
  .then(buf => aCtx.decodeAudioData(buf)) // can be callback as well
  .then(decoded => {
    source.buffer = buf = decoded;
    source.loop = true;
    source.connect(aCtx.destination);
    check.disabled = false;
  });

check.onchange = e => {
  if (check.checked) {
    source.start(0); // start our bufferSource
  } else {
    source.stop(0); // this destroys the buffer source
    source = aCtx.createBufferSource(); // so we need to create a new one
    source.buffer = buf;
    source.loop = true;
    source.connect(aCtx.destination);
  }
};
<label>Start Playing</label>
<input type="checkbox" id="check" disabled><br>
<br>Its volume is 100%. Please help me to reduce it to 10%.
Rashid
  • 1,244
  • 3
  • 13
  • 29
  • So I recognize [my code](http://stackoverflow.com/a/42970609/3702797) here, and if you read the answer in its entirety, you'd see that in the second snippet I use an [gainNode](https://developer.mozilla.org/en-US/docs/Web/API/AudioContext/createGain), which will give you the control on the output volume. – Kaiido Apr 13 '17 at 07:43

2 Answers2

22

We use GainNodes to control the volume.

var gainNode = aCtx.createGain()
gainNode.gain.value = 0.1 // 10 %
gainNode.connect(aCtx.destination)

// now instead of connecting to aCtx.destination, connect to the gainNode
source.connect(gainNode)

solution

const aCtx = new AudioContext();

const gainNode = aCtx.createGain();
gainNode.gain.value = 0.1; // setting it to 10%
gainNode.connect(aCtx.destination);

let source = aCtx.createBufferSource();
let buf;
fetch('https://dl.dropboxusercontent.com/s/knpo4d2yooe2u4h/tank_driven.wav') // can be XHR as well
  .then(resp => resp.arrayBuffer())
  .then(buf => aCtx.decodeAudioData(buf)) // can be callback as well
  .then(decoded => {
    source.buffer = buf = decoded;
    source.loop = true;
    source.connect(gainNode);

    check.disabled = false;
  });

check.onchange = e => {
  if (check.checked) {
    source.start(0); // start our bufferSource
  } else {
    source.stop(0); // this destroys the buffer source
    source = aCtx.createBufferSource(); // so we need to create a new one
    source.buffer = buf;
    source.loop = true;
    source.connect(gainNode);
    
  }
};
<label>Start Playing</label>
<input type="checkbox" id="check" disabled><br>
<br>Its volume is 100%. Please help me to reduce it to 10%.
arc
  • 4,553
  • 5
  • 34
  • 43
  • 1
    @Kaiido Thank you. Should have seen that. It hurts me too... now that I see it. – arc Apr 13 '17 at 08:07
0

You can use createGain of AudioContext for that puporse.

As shown below,

For more information checkout on createGain

https://developer.mozilla.org/en-US/docs/Web/API/AudioContext/createGain

    const aCtx = new AudioContext();
    let source = aCtx.createBufferSource();
    let buf;

    var gainNode = aCtx.createGain(); // Create a gainNode reference.
    gainNode.connect(aCtx.destination); // Add context to gainNode

fetch('https://dl.dropboxusercontent.com/s/knpo4d2yooe2u4h/tank_driven.wav') // can be XHR as well
      .then(resp => resp.arrayBuffer())
      .then(buf => aCtx.decodeAudioData(buf)) // can be callback as well
      .then(decoded => {
        source.buffer = buf = decoded;
        source.loop = true;

        source.connect(gainNode);   //Connecting gain to source
        gainNode.gain.value = 1;  // 100% VOLUME RANGE OF VALUE IS 0-1

        check.disabled = false;
      });


   check.onchange = e => {
      if (check.checked) {
        source.start(0); // start our bufferSource
      } else {
        source.stop(0); // this destroys the buffer source
        source = aCtx.createBufferSource(); // so we need to create a new one
        source.buffer = buf;
        source.loop = true;

        source.connect(gainNode);   //Connecting gain to source
        gainNode.gain.value = 0.1;  // 10% VOLUME RANGE OF VALUE IS 0-1
      }
    };
Nadir Laskar
  • 4,012
  • 2
  • 16
  • 33
  • 2
    You need to connect the buffer source to the gainNode in the check.onchange too, otherwise after second change it won't work anymore – Kaiido Apr 13 '17 at 07:59
  • 1
    You don't need to re-connect the gainNode to the context, do it once, e.g on gloabl scope. You are also still connecting the buffer to the context's destination directly, this is not good at all. – Kaiido Apr 13 '17 at 08:09
  • This is just for demonstration purpose of using gainNode for manipulating volume, optimizations can be done in many ways. – Nadir Laskar Apr 13 '17 at 08:17
  • I'm not talking about optimizations. Your code **doesn't work**, in the scope of gainNode demo. Simply try to set it's gain.value to lower than 1 and you'll see that since you are connecting directly the buffersource to the audio context, this buffersource will still play at volume 1. => just remove all `source.connect(aCtx.destination);` – Kaiido Apr 13 '17 at 08:20
  • This is not the part of the code I added. Checkout the question. – Nadir Laskar Apr 13 '17 at 08:24
  • I know this, [I am the author of the code in the question](http://stackoverflow.com/a/42970609/3702797). When I wrote it, I didn't used a gainNode, so I did connect the buffer source directly to the context. But now that you are implementing this gainNode, you must remove these lines. – Kaiido Apr 13 '17 at 08:31