Web Audio学习与音频播放

2019年04月08日Web前端

随着浏览器的越发强大,用原生js操作音频已经不是难事了。我们来使用web audio api简单地处理下音频资源。

简介

在学习web audio api之前,先了解三个概念:

音频源,也就是音频输入,可以是直接从设备输入的音频,也可以是远程获取的音频文件。

处理节点,分析器和处理器,比如音调节点,音量节点,声音处理节点。

输出源,指音频渲染设备,一般情况下是用户设备的扬声器,即context.destination。

当然,实际使用中,可能会有n个处理节点,都可以使用connect依次关联起来。

音频文件播放

假如现在要用web audio api播放本地的一个音乐文件,按照前面的流程,我们来试下。

文件上传

既然音频文件来自本地,那么得支持文件上传:

<input type="file" id="file" accept="audio/x-wav,audio/mpeg" />

我这儿限制先显示wav和mp3两种格式。

文件读取

给input增加change事件,处理选中的文件:

var context = new (window.AudioContext || window.webkitAudioContext)();

document.getElementById('file').addEventListener('change', function(e) {
    var read = new FileReader();

    read.onload = function() {
        // 将arrayBuffer转成audioBuffer
        context.decodeAudioData(this.result, function(buffer) {
            playSound(buffer);
        }, function() {
            console.log('error');
        });
    };
    // 利用filereader将file转成arraybuffer格式
    read.readAsArrayBuffer(this.files[0]);
});

丢了一段代码,我们来看下,new AudioContext(),创建audio的上下文环境,至于webkitAudioContext是兼容较低版本的chrome的。

fileReader大伙应该见到的比较多了吧,这儿用他读取对应file对象中的文件数据,readAsArrayBuffer表示读取结果用arrayBuffer对象显示,由于此方法是异步读取的,所以只能放在onload回调中处理。

而我们拿到的arrayBuffer不能直接给web audio播放,需要使用decodeAudioData()方法将arrayBuffer转成audioBuffer,那么此时转化后的audioBuffer就是音频源啦。decodeAudioData更多介绍可以查看MDN:https://developer.mozilla.org/zh-CN/docs/Web/API/AudioContext/decodeAudioData

注:w3c文档上说明decodeAudioData支持audio标签支持的所有音频格式。MDN提示ArrayBuffer类型的对象旨在保存小型音频片段,通常小于45秒,对于较长的声音,实现它的对象MediaElementAudioSourceNode(audio元件)更合适。

音频播放

音频数据到手,接下来就是播放啦。

文件上传后,我们拿到了音频audioBuffer形式的数据,接下来使用createBufferSource()方法播放音频数据,再connect到destination就可以播放了。

// 播放音频
function playSound(buffer) {
    var source = context.createBufferSource();

    // 设置数据
    source.buffer = buffer;
    // connect到扬声器
    source.connect(context.destination);
    source.start();
}

一个简单的音频文件播放器就完成了,如果是从服务器上获取文件也是类似的,只不过是多了个ajax处理。

代码地址:webAudio播放本地音乐

音频的话,可以去一些音乐网站下载,如果懒得下的话,我这直接提供音频下载:[download id="1075"]

小bug

当连续选择多个文件时,你会发现,多个音频文件一起播放了,因此,多音频输入时,都一起connect到context.destination上就可以实现一起播放了。对于此处,这应该算是个bug,查看AudioBufferSourceNode文档,可以利用stop()方法去处理,大伙自个想下,处理下咯。

前面的例子里只出现了音频源和音频输出,并未出现处理节点。接下来,我们尝试自己创建音频,并使用音量处理节点。

自制音频并播放

创建音频源

此处我们不使用外部的音频文件,而是使用createOscillator()方法创建音频源。

该方法返回OscillatorNode,可以通过frequency属性设置他的振荡频率,type属性则可以用来指定要播放的波形。更多属性和方法参考OscillatorNode文档

var context = new (window.AudioContext || window.webkitAudioContext)();
var oscillator = context.createOscillator();
// oscillator.type = 'sine';
// oscillator.frequency.value = 800;  // 频率800Hz,默认440

创建音量处理节点

使用createGain()方法,修改返回值中的value,既可以改变音量大小。

var gainNode = context.createGain();
gainNode.gain.value = 0.8; // 音量 0 ~ 1

节点关联与播放

将这些"节点"connect起来就可以播放了。

oscillator.connect(gainNode); // 音频源关联到音量
gainNode.connect(context.destination); // 音量关联到扬声器

// chrome 73 需要用户点击才可播放
document.getElementById('start').addEventListener('click', function() {
    oscillator.start();
});

虽然播放后是一片噪音,不过简单的web audio api我们已经会使用啦。

代码地址:webAudio制造噪音并播放

其他

自己尝试过程中,在控制台下遇到这个警告: The AudioContext was not allowed to start. It must be resumed (or created) after a user gesture on the page.

大概意思就说AudioContext需要用户手动触发,所以只需要将new AudioContext()移动到事件内部就行了。

总结

我们已经能够使用web audio播放本地音乐和制造噪音了,接下来,可以尝试下其他的api了。

想要看js录音的可以看这篇文章:纯js实现录音与播放