改变audio采样数据改变音频音量实例页面

回到相关文章 »

效果:

原始音频

//zxx: 原始音频音量较大,播放前请注意周围环境

生成新的音频

代码:

HTML代码:
<h4>原始音频</h4>
<audio src="./bgmusic.mp3" controls></audio>

<h4>生成新的音频</h4>
<button id="button">生成20%音量音频</button>
<div id="output" class="output"></div>
JS代码:
// 音量
const volume = 0.2;
// 定义一个AudioContext对象
// 因为 Web Audio API都是源自此对象
const audioContext = new AudioContext();
// 降低音量后的audioBuffer数据
let newAudioBuffer = null;
// 获取音频资源
fetch('./bgmusic.mp3')
  .then(res => res.arrayBuffer())
  .then(buffer => audioContext.decodeAudioData(buffer))
  .then(audioBuffer => {
    newAudioBuffer = new AudioBuffer({
        length: audioBuffer.length,
        numberOfChannels: audioBuffer.numberOfChannels,
        sampleRate: audioBuffer.sampleRate
    });
    
    for (let channel = 0; channel < audioBuffer.numberOfChannels; channel += 1) {
        const channelData = audioBuffer.getChannelData(channel);
        const copiedChannelData = newAudioBuffer.getChannelData(channel);
    
        for (let sample = 0; sample < channelData.length; sample += 1) {
            copiedChannelData[sample] = channelData[sample] * volume;
        }
    }
});

// Convert AudioBuffer to a Blob using WAVE representation
function bufferToWave(abuffer, len) {
    var numOfChan = abuffer.numberOfChannels,
    length = len * numOfChan * 2 + 44,
    buffer = new ArrayBuffer(length),
    view = new DataView(buffer),
    channels = [], i, sample,
    offset = 0,
    pos = 0;

    // write WAVE header
    // "RIFF"
    setUint32(0x46464952);
    // file length - 8                      
    setUint32(length - 8);
    // "WAVE"                     
    setUint32(0x45564157);
    // "fmt " chunk
    setUint32(0x20746d66);  
    // length = 16                       
    setUint32(16);  
    // PCM (uncompressed)                               
    setUint16(1); 
    setUint16(numOfChan);
    setUint32(abuffer.sampleRate);
    // avg. bytes/sec
    setUint32(abuffer.sampleRate * 2 * numOfChan);
    // block-align
    setUint16(numOfChan * 2);
    // 16-bit (hardcoded in this demo)
    setUint16(16);                           
    // "data" - chunk
    setUint32(0x61746164); 
    // chunk length                   
    setUint32(length - pos - 4);                   

    // write interleaved data
    for(i = 0; i < abuffer.numberOfChannels; i++)
        channels.push(abuffer.getChannelData(i));

    while(pos < length) {
         // interleave channels
        for(i = 0; i < numOfChan; i++) {
            // clamp
            sample = Math.max(-1, Math.min(1, channels[i][offset])); 
            // scale to 16-bit signed int
            sample = (0.5 + sample < 0 ? sample * 32768 : sample * 32767)|0; 
            // write 16-bit sample
            view.setInt16(pos, sample, true);          
            pos += 2;
        }
        // next source sample
        offset++                                     
    }

    // create Blob
    return new Blob([buffer], {type: "audio/wav"});

    function setUint16(data) {
        view.setUint16(pos, data, true);
        pos += 2;
    }

    function setUint32(data) {
        view.setUint32(pos, data, true);
        pos += 4;
    }
}

// 点击按钮播放
button.onclick = function () {
    const blob = bufferToWave(newAudioBuffer, newAudioBuffer.length);
    const blobUrl = URL.createObjectURL(blob);
    
    output.innerHTML = `<audio src="${blobUrl}" controls></audio>
    <p><a href="${blobUrl}" download="bgmusic-volume-down.wav">下载该音频</a></p>
    `;
};