2021年7月11日 星期日

[webaudio, gain] How to change the audio gain dynamically

How to change the audio gain dynamically

井民全, Jing, mqjing@gmail.com


You can directly change the gainNode to set up the value that you want by the following snippet. However, you can use  AudioParam.linearRampToValueAtTime[1] to adjust the audio gain in a gradual linear way.



Key

gainNode.gain.value = 2;

        this.source.connect(gainNode);

        gainNode.connect(this.context.destination);

        this.source.start(0, this.startOffset % buffer.duration);


        // Adjust the volume after 5 sec

        setTimeout(function() {

            alert('set gain value');

            gainNode.gain.value = 0.2

       }, 5000)



Code

File: client.html

<!-- File: client.htm-->

<!doctype html>

<html lang="en">

<body>

<h1>Test</h1>

<script src="client.js"></script>



File: client.js

'use strict';

/**

 * Key Idea:

 * - connect gainNode

 * - change the gain value 

 * 

 * Reference:

 * - https://github.com/borismus/webaudioapi.com/blob/master/content/book/Web_Audio_API_Boris_Smus.pdf, page 28.

 */

class Test{

    constructor() {

        this.context = null;

        this.buffer = null;

        this.source = null;


        this.startOffset = 0;

        this.startTime = 0;

    }


    initUI(){

        this.addPlayButton();

        this.addPauseButton();

        this.addStopButton();

    }

   

    play(buffer) {

        this.startTime = this.context.currentTime;

        this.source = this.context.createBufferSource();

        this.source.buffer = this.buffer;


        let gainNode = this.context.createGain();

        gainNode.gain.value = 2;

        this.source.connect(gainNode);

        gainNode.connect(this.context.destination);

        this.source.start(0, this.startOffset % buffer.duration);


        setTimeout(function() {

            alert('set gain value');

            gainNode.gain.value = 0.2

       }, 5000)

    }


    stop() {

        if (this.source != null) {

            this.source.stop();

            return [1, 'ok'];

        }else{

            let strMsg = '[Error] _uiSoundStop:: this.source == null.';

            return [0, strMsg];

        }

    }


    pause() {

        const [bOk, strMsg] = this.stop();

        if (!bOk)

            return [bOk, strMsg];


        this.startOffset += this.context.currentTime - this.startTime; // Measure how much time passed since the last pause.

        return [1, 'ok'];

    }



    addPlayButton(){

        this.comm('Play', async () => {

            console.log('this.constructor.name = ', this.constructor.name); // Test

            console.log('clicked');

            this._initAudioContext();

            this.buffer = await this._loadAudioBuffer("https://archive.org/download/100ClassicalMusicMasterpieces/1685%20Purcell%20,%20Trumpet%20Tune%20and%20Air.mp3");

            this.play(this.buffer);

        });

    }


    addPauseButton(){

        this.comm('Pause', () => {

            let [bOk, strMsg] = this.pause();

            if (!bOk){

                this._showMessageError(strMsg);

            }

        });

    }


    addStopButton(){

        this.comm('Stop', () => {

            this.stop();

        });

    }


    comm(strTitle, listener) {

        let button = document.createElement('button');

        button.innerHTML = strTitle;  // set the button content

        button.addEventListener('click', listener);


        let body = document.getElementsByTagName("body")[0];

        body.appendChild(button);

    }


    _initAudioContext() {

        let contextClass = (window.AudioContext ||

            window.webkitAudioContext ||

            window.mozAudioContext ||

            window.oAudioContext ||

            window.msAudioContext);

        if (contextClass) {

                console.log('Web Audio API is available.')

                this.context = new contextClass();

        } else {

            console.log(' Web Audio API is not available. Ask the user to use a supported browser.')

        }

    }


    _loadAudioBuffer(url){

        return new Promise(resolve => {

            var request = new XMLHttpRequest();

            request.open('GET', url, true);

            request.responseType = 'arraybuffer';

            // Decode asynchronously

            request.onload = () => {

                this.context.decodeAudioData(request.response, (theBuffer) => {

                    console.log('Get the data.')

                    this.buffer = theBuffer;

                    resolve(this.buffer);

                    console.log('buffer = ', this.buffer);

                    }, function () {

                        console.log('[Error] _loadAudioBuffer:: decodeAudioData error.')

                    });

            } // end of onload

            request.send();});

        }


    


    _showMessageError(strMsg) {

        console.log(strMsg);

        alert(strMsg);

    }

} // end of class


let test = new Test();

test.initUI();



Run

gio open client.html 


Reference

  1. https://developer.mozilla.org/en-US/docs/Web/API/AudioParam/linearRampToValueAtTime