摘要:Web Audio API是对<audio> 标签功能上的补充,我们可以用它完成混音、音效、平移等各种复杂的音频处理,本文简单的使用其完成音波图的绘制。
PS:本例子使用ES6编程,最好在新版chrome中运行。
一、前端文件index.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0"> <meta http-equiv="X-UA-Compatible" content="ie=edge" /> <title>audiogram</title> </head> <body> <canvas id="canvas" width="400" height="100"></canvas> <button class="play" style=" 50px;height: 50px;">开始</button> <button class="stop" style=" 50px;height: 50px;">停止</button> </body> <script type="module" src="Main.js"></script> </html>
PS:浏览器加载ES6模板使用<script>标签时,要加入type = “module”属性,浏览器会默认异步加载,并知道Main.js是一个ES6模块。
二、创建Audio类
//audit类,用于加载,播放音乐 export class Audio { //单例 static getInstance() { if (!Audio.instance) { Audio.instance = new Audio(); } return Audio.instance; } //构造函数 constructor() { this.ctx = new (window.AudioContext || window.webkitAudioContext)(); } //加载资源 getData() { this.analyser = this.ctx.createAnalyser(); //从元素创建媒体节点 可以直接将audio元素传入后创建,就不用request来请求资源 //this.source = this.ctx.createMediaElementSource(audio); this.source = this.ctx.createBufferSource(); this.source.loop = true; //创建AnalyserNode,用来显示音频时间和频率的数据 this.source.connect(this.analyser); //最后连接到音频渲染设备,发出声音 this.analyser.connect(this.ctx.destination); //获取频率 this.freqs = new Uint8Array(this.analyser.frequencyBinCount); //请求资源 let request = new XMLHttpRequest(); request.open('get', 'res/bgm.mp3', true); //responseType属性须设置为arraybuffer request.responseType = 'arraybuffer'; //decodeAudioData方法用于解码音频文件 request.onload = () => { var audioData = request.response; this.ctx.decodeAudioData(audioData, (buffer) => { //将解码后的音频文件作为声音的来源 this.source.buffer = buffer; //立即开始播放声音 this.source.start(0); }, (e) => { "Error with decoding audio data" + e.error }); }; request.send(); } }
三、创建Main类
import {Audio} from "./js/Audio.js";
//用于控制整个页面的流程
class Main {
constructor() {
//获取audio实例
this.audio = Audio.getInstance();
this.init();
}
//初始化
init() {
//初始化按钮
this.play = document.querySelector('.play');
this.stop = document.querySelector('.stop');
//确保加载完资源后开始输出
let promise = new Promise((resolve) => {
this.audio.getData();
resolve();
});
promise.then(() => {
this.initCanvas();
this.outPut()
});
//播放按钮
this.play.onclick = () => {
this.audio.ctx.resume();
this.outPut();
this.play.setAttribute('disabled', 'disabled');
}
//停止按钮
this.stop.onclick = () => {
this.audio.ctx.suspend();
//this.audio.source.stop(0);使用stop停止时无法恢复,需要重载资源
cancelAnimationFrame(this.timer);
this.play.removeAttribute('disabled');
}
}
//初始化canvas
initCanvas() {
let cv = document.querySelector('#canvas');
this.canvasWidth = cv.width;
this.canvasHeight = cv.height;
this.canvas = cv.getContext("2d");
this.canvas.translate(0.5, 0.5);
this.outPutData = this.audio.freqs;
}
//输出图像
outPut() {
var height = this.canvasHeight;
var width = this.canvasWidth;
var outPutData = this.outPutData;
var length = outPutData.length;
this.audio.analyser.getByteFrequencyData(outPutData);
//将缓冲区的数据绘制到Canvas上
this.canvas.clearRect(-0.5, -0.5, width, height);
this.canvas.beginPath(), this.canvas.moveTo(0, height);
for (var i = 0; i < width; i++)
this.canvas.lineTo(i, height - height * outPutData[Math.round(length * i / width)] / 255);
this.canvas.lineTo(i, height), this.canvas.fill();
//请求下一帧
this.timer = requestAnimationFrame(() => {
this.outPut()
});
}
}
new Main();
PS:在ES6中使用箭头函数时,箭头函数中的this指向Main,真的超级好用。
注意:使用chrome打开时会出现 Origin 'null' is therefore not allowed access.这是由于在本地html页面ajax请求本地或者局域网server的资源时,被浏览器禁止了,跨域请求会带来安全隐患,因此谷歌浏览器对此做出了限制。
在服务端我们可以设置response.setHeader("Access-Control-Allow-Origin: *")允许这么做,如果是客户端的话我们可以右键chrome快捷方式—>属性->目标-> 将"--allow-file-access-from-files"添加至最后重启浏览器即可。
工程目录结构:
└── audiogram ├── js │ └── Audio.js ├── res │ └── bgm.mp3
├ ─ ─ index.html
└ ─ ─ Main.js
效果图: