【HTML5】AudioContext 简单应用

AudioContext 是 HTML5 中新增的一个关于音频处理的对象。虽然我们平时提到 HTML5 音频都会想到 audio 标签,但是今天我们要讲的 AudioContext 对象要比 audio 标签更加的强大,它能实现 audio 标签所不能实现的功能,比如一个展示音频频谱的插件。

介绍

AudioContext 对象是一个音频处理上下文,表示由音频模块连接而成的音频处理图,每个模块对应一个 AudioNode。

AudioContext 可以控制它所包含的节点的创建,以及音频处理、解码操作的执行。

做任何事情之前都要先创建 AudioContext 对象,因为一切都发生在这个环境之中。

而通过 AudioContext 创建的处理节点(audioNode)可以理解成一个个管道函数,我们把这些节点按一定次序连接起来,音频数据就会按次序通过这些节点,并做相应的处理,如下图:

实现

实现过程直接看代码吧,主要就是要注意下面几点:

  • 音频数据来源可以是一个 audio 标签,也可以是 Blob 数据,对应的方法分别是 createBufferSource() 和 createMediaElementSource()。需要注意的是,因为浏览器同源策略的原因,非同源音频无法进行解析。
  • 获取分析器数据的方法需要传入一个类型数组(Uint8Array)
  • 分析器获取数据的方法没有返回值,它只会将数据填入传入方法的类型数组中
// Audio 分析器
function AudioAnalyser(audioEl, analyserCount, fftIndex) {
    var audioContext = new AudioContext(),
        audioBufferSource = audioContext.createMediaElementSource(audioEl),
        audioAnalyserList = [],
        audioSplitter = audioContext.createChannelSplitter(analyserCount),
        audioMerger = audioContext.createChannelMerger(analyserCount);

    // 创建分析器列表
    for (var i = 0; i < analyserCount; i++) {
        var analyser = audioContext.createAnalyser();
        analyser.fftSize = Math.pow(2, fftIndex);
        audioAnalyserList.push(analyser);
    }

    // 连接 Audio 节点
    audioBufferSource.connect(audioSplitter);
    audioAnalyserList.forEach(function (analyser, index) {
        audioSplitter.connect(analyser, index);
        analyser.connect(audioMerger, 0, index);
    });
    audioMerger.connect(audioContext.destination);

    // 获取分析器数据方法
    function getByteFrequencyDataList() {
        var arrayBuffer;
        return audioAnalyserList.map(function (analyser) {
            arrayBuffer = new Uint8Array(analyser.frequencyBinCount);
            analyser.getByteFrequencyData(arrayBuffer);
            return arrayBuffer;
        });
    }

    function getByteFrequencyDataCount() {
        var count = 0;
        audioAnalyserList.forEach(function (analyser) {
            count += analyser.frequencyBinCount;
        });
        return count;
    }

    return {
        getByteFrequencyDataList: getByteFrequencyDataList,
        getByteFrequencyDataCount: getByteFrequencyDataCount
    }
}

DEMO