浏览器环境下音频类的实现
一套html框架肯定离不开多媒体元素的支持,本例用JavaScript实现在浏览器环境下音频的加载,解码,播放,暂停等功能,代码为了类型判断,和语法检测 都用typescript完成。
1. 音频文件的加载
我们用ajax来加载一个音频文件到浏览器内存中,只需要设置XMLHttpRequest对象的responseType(响应类型)为arraybuffer即刻,示例如下:
// 加载资源
public load(url: string, callBack?:Function) {
let self = this;
let xhr =<XMLHttpRequest>(window["XMLHttpRequest"] ? new window["XMLHttpRequest"]() : new ActiveXObject("MSXML2.XMLHTTP"));
xhr.open("GET", url, true);
xhr.responseType = "arraybuffer";
xhr.onreadystatechange= () => {
};
xhr.send();
}
传入加载的的路径和加载完成的回调函数。
2. 音频文件的解码
利用load函数加载我们可以得到一个buffer,将这个buffer解码才能正常播放这个音频,解码需要我们先有一个音频的上下文环境,如下示例
// 初始化环境
public static initContext() {
if (!Sound.audioContext) {
Sound.audioContext = new (window["AudioContext"] || window["webkitAudioContext"] || window["mozAudioContext"])();
}
}
在sound类中首先需要初始化音频的上下文环境,使用这个audioContext中的decodeAudioData函数可以进行对加载成功的buff进行解码,在ajax的redayState为4后进行解码,如下示例
xhr.onreadystatechange = () => { if (xhr.readyState == 4) { if (xhr.status >= 400 || xhr.status == 0) { alert("加载资源错误!") } else { // 加载成功 进行解码 Sound.audioContext.decodeAudioData(xhr.response, (audioBuffer) => { self.buffer = audioBuffer; self.isLoading = true; callBack && callBack(); }, () => { alert("解码失败,路径:" + url); }) } } };
解码成功后将audioBuffer赋值给类就可以了。
3. 实现音频的播放暂停 停止 恢复等功能
音频的播放音量的控制还需要用到一个叫GainNode的对象,这是一个音频处理模块,封装在es的库中,全部代码示例如下:
namespace media { export class Sound { // 音频环境 public static audioContext: AudioContext; // 初始化环境 public static initContext() { if (!Sound.audioContext) { Sound.audioContext = new (window["AudioContext"] || window["webkitAudioContext"] || window["mozAudioContext"])(); } } // 音频数据 public buffer: AudioBuffer; // 音频节点控制 public gain: GainNode; // 是否已经加载完成 public isLoading: boolean = false; // 是否正在播放 public isPlaying: boolean = false; // 开始时间 public startTime: number; // 音频资源buffer public source: AudioBufferSourceNode; // 开始播放时间 public beginTime: number; // 音量 public _volume: number = 1; // 是否暂停 public isPaused: boolean = true; // 音频类型 0为背景音乐 重复播放 1是音效 只播放一次 public type: number; public constructor() { this.gain = Sound.audioContext["createGain"] ? Sound.audioContext.createGain() : Sound.audioContext["createGainNode"](); } // 加载资源 public load(url: string, callBack?: Function) { let self = this; let xhr = <XMLHttpRequest>(window["XMLHttpRequest"] ? new window["XMLHttpRequest"]() : new ActiveXObject("MSXML2.XMLHTTP")); xhr.open("GET", url, true); xhr.responseType = "arraybuffer"; xhr.onreadystatechange = () => { if (xhr.readyState == 4) { if (xhr.status >= 400 || xhr.status == 0) { alert("加载资源错误!") } else { // 加载成功 进行解码 Sound.audioContext.decodeAudioData(xhr.response, (audioBuffer) => { self.buffer = audioBuffer; self.isLoading = true; callBack && callBack(); }, () => { alert("解码失败,路径:" + url); }) } } }; xhr.send(); } public play(type: number = 0, volume: number = 1, startTime: number = 0) { if (!this.isLoading) { alert("未加载成功,播放失败"); return } if (this.gain && !this.isPlaying) { this.startTime = startTime; this.type = type; if (this.source) { this.source.onended = null; this.source = null; } this.source = Sound.audioContext.createBufferSource(); this.source.buffer = this.buffer; this.source.connect(this.gain); // 关联音频渲染设备 this.gain.connect(Sound.audioContext.destination); this.source.onended = () => { this.onPlayEnd(); if (type == 0) { this.play(type, volume, this.startTime); } else { this.stop(); } } this.beginTime = Date.now(); this.volume = volume; this.source.start(0, this.startTime); this.isPlaying = true; this.isPaused = false; return true; } return false; } // 停止 public stop() { if (this.source) { if (this.source.stop) this.source.stop(0); else this.source["noteOff"](0); this.source.onended = null; this.source.disconnect(); this.source = null; this.isPlaying = false; return true; } return false; } // 暂停 public pause() { if (this.source) { (<any>this.gain).position = this.position; this.isPaused = true; return this.stop(); } return false; } // 播放完成调度 public onPlayEnd() { } /** * 恢复当前音频 * @returns {boolean} */ public resume() { if (this.gain) { return this.play(this.type, this.volume, (<any>this.gain).position); } return false; } /** * 音量 * @param {number} value */ public set volume(value: number) { if (!this.gain) return; this._volume = value; this.gain.gain.value = value; } /** * 获取当前播放位置 * @returns {number} */ public get position() { if (this.source) return (Date.now() - this.beginTime) / 1000 + this.startTime; else return 0; } } }
最后再来测试下
window.addEventListener("load", () => { media.Sound.initContext(); let sound = new media.Sound(); sound.load("./mp3/test.mp3", () => { sound.play(); }); }, false);