mirror of
https://github.com/hiloteam/Hilo.git
synced 2025-12-08 20:35:59 +00:00
304 lines
8.3 KiB
JavaScript
304 lines
8.3 KiB
JavaScript
/**
|
||
* Hilo
|
||
* Copyright 2015 alibaba.com
|
||
* Licensed under the MIT License
|
||
*/
|
||
|
||
/**
|
||
* @class WebAudio声音播放模块。它具有更好的声音播放和控制能力,适合在iOS6+平台使用。
|
||
* 兼容情况:iOS6+、Chrome33+、Firefox28+支持,但Android浏览器均不支持。
|
||
* @param {Object} properties 创建对象的属性参数。可包含此类所有可写属性。
|
||
* @module hilo/media/WebAudio
|
||
* @requires hilo/core/Class
|
||
* @requires hilo/util/util
|
||
* @requires hilo/event/EventMixin
|
||
* @property {String} src 播放的音频的资源地址。
|
||
* @property {Boolean} loop 是否循环播放。默认为false。
|
||
* @property {Boolean} autoPlay 是否自动播放。默认为false。
|
||
* @property {Boolean} loaded 音频资源是否已加载完成。只读属性。
|
||
* @property {Boolean} playing 是否正在播放音频。只读属性。
|
||
* @property {Number} duration 音频的时长。只读属性。
|
||
* @property {Number} volume 音量的大小。取值范围:0-1。
|
||
* @property {Boolean} muted 是否静音。默认为false。
|
||
*/
|
||
var WebAudio = (function(){
|
||
|
||
var context = null;
|
||
try {
|
||
var AudioContext = window.AudioContext || window.webkitAudioContext;
|
||
if (AudioContext) {
|
||
context = new AudioContext();
|
||
}
|
||
} catch(e) {
|
||
context = null;
|
||
}
|
||
|
||
return Class.create(/** @lends WebAudio.prototype */{
|
||
Mixes: EventMixin,
|
||
constructor: function(properties){
|
||
util.copy(this, properties, true);
|
||
|
||
this._init();
|
||
},
|
||
|
||
src: null,
|
||
loop: false,
|
||
autoPlay: false,
|
||
loaded: false,
|
||
playing: false,
|
||
duration: 0,
|
||
volume: 1,
|
||
muted: false,
|
||
|
||
_context: null, //WebAudio上下文 the WebAudio Context
|
||
_gainNode: null, //音量控制器 the volume controller
|
||
_buffer: null, //音频缓冲文件 the audio file buffer
|
||
_audioNode: null, //音频播放器 the audio playing node
|
||
_startTime: 0, //开始播放时间戳 the start time to play the audio
|
||
_offset: 0, //播放偏移量 the offset of current playing audio
|
||
|
||
/**
|
||
* @private 初始化
|
||
*/
|
||
_init:function(){
|
||
this._context = context;
|
||
this._gainNode = context.createGain ? context.createGain() : context.createGainNode();
|
||
this._gainNode.connect(context.destination);
|
||
|
||
this._onAudioEvent = this._onAudioEvent.bind(this);
|
||
this._onDecodeComplete = this._onDecodeComplete.bind(this);
|
||
this._onDecodeError = this._onDecodeError.bind(this);
|
||
},
|
||
/**
|
||
* 加载音频文件。注意:我们使用XMLHttpRequest进行加载,因此需要注意跨域问题。
|
||
*/
|
||
load: function(){
|
||
if(!this._buffer){
|
||
var buffer = WebAudio._bufferCache[this.src];
|
||
if(buffer){
|
||
this._onDecodeComplete(buffer);
|
||
}
|
||
else{
|
||
var request = new XMLHttpRequest();
|
||
request.src = this.src;
|
||
request.open('GET', this.src, true);
|
||
request.responseType = 'arraybuffer';
|
||
request.onload = this._onAudioEvent;
|
||
request.onprogress = this._onAudioEvent;
|
||
request.onerror = this._onAudioEvent;
|
||
request.send();
|
||
}
|
||
this._buffer = true;
|
||
}
|
||
return this;
|
||
},
|
||
|
||
/**
|
||
* @private
|
||
*/
|
||
_onAudioEvent: function(e){
|
||
// console.log('onAudioEvent:', e.type);
|
||
var type = e.type;
|
||
|
||
switch(type){
|
||
case 'load':
|
||
var request = e.target;
|
||
request.onload = request.onprogress = request.onerror = null;
|
||
this._context.decodeAudioData(request.response, this._onDecodeComplete, this._onDecodeError);
|
||
request = null;
|
||
break;
|
||
case 'ended':
|
||
this.playing = false;
|
||
this.fire('end');
|
||
if(this.loop) this._doPlay();
|
||
break;
|
||
case 'progress':
|
||
this.fire(e);
|
||
break;
|
||
case 'error':
|
||
this.fire(e);
|
||
break;
|
||
}
|
||
},
|
||
|
||
/**
|
||
* @private
|
||
*/
|
||
_onDecodeComplete: function(audioBuffer){
|
||
if(!WebAudio._bufferCache[this.src]){
|
||
WebAudio._bufferCache[this.src] = audioBuffer;
|
||
}
|
||
|
||
this._buffer = audioBuffer;
|
||
this.loaded = true;
|
||
this.duration = audioBuffer.duration;
|
||
|
||
this.fire('load');
|
||
if(this.autoPlay) this._doPlay();
|
||
},
|
||
|
||
/**
|
||
* @private
|
||
*/
|
||
_onDecodeError: function(){
|
||
this.fire('error');
|
||
},
|
||
|
||
/**
|
||
* @private
|
||
*/
|
||
_doPlay: function(){
|
||
this._clearAudioNode();
|
||
|
||
var audioNode = this._context.createBufferSource();
|
||
|
||
//some old browser are noteOn/noteOff -> start/stop
|
||
if(!audioNode.start){
|
||
audioNode.start = audioNode.noteOn;
|
||
audioNode.stop = audioNode.noteOff;
|
||
}
|
||
|
||
audioNode.buffer = this._buffer;
|
||
audioNode.onended = this._onAudioEvent;
|
||
this._gainNode.gain.value = this.muted ? 0 : this.volume;
|
||
audioNode.connect(this._gainNode);
|
||
audioNode.start(0, this._offset);
|
||
|
||
this._audioNode = audioNode;
|
||
this._startTime = this._context.currentTime;
|
||
this.playing = true;
|
||
},
|
||
|
||
/**
|
||
* @private
|
||
*/
|
||
_clearAudioNode: function(){
|
||
var audioNode = this._audioNode;
|
||
if(audioNode){
|
||
audioNode.onended = null;
|
||
// audioNode.disconnect(this._gainNode);
|
||
audioNode.disconnect(0);
|
||
this._audioNode = null;
|
||
}
|
||
},
|
||
|
||
/**
|
||
* 播放音频。如果正在播放,则会重新开始。
|
||
*/
|
||
play: function(){
|
||
if(this.playing) this.stop();
|
||
|
||
if(this.loaded){
|
||
this._doPlay();
|
||
}else if(!this._buffer){
|
||
this.autoPlay = true;
|
||
this.load();
|
||
}
|
||
|
||
return this;
|
||
},
|
||
|
||
/**
|
||
* 暂停音频。
|
||
*/
|
||
pause: function(){
|
||
if(this.playing){
|
||
this._audioNode.stop(0);
|
||
this._offset += this._context.currentTime - this._startTime;
|
||
this.playing = false;
|
||
}
|
||
return this;
|
||
},
|
||
|
||
/**
|
||
* 恢复音频播放。
|
||
*/
|
||
resume: function(){
|
||
if(!this.playing){
|
||
this._doPlay();
|
||
}
|
||
return this;
|
||
},
|
||
|
||
/**
|
||
* 停止音频播放。
|
||
*/
|
||
stop: function(){
|
||
if(this.playing){
|
||
this._audioNode.stop(0);
|
||
this._audioNode.disconnect();
|
||
this._offset = 0;
|
||
this.playing = false;
|
||
}
|
||
return this;
|
||
},
|
||
|
||
/**
|
||
* 设置音量。
|
||
*/
|
||
setVolume: function(volume){
|
||
if(this.volume != volume){
|
||
this.volume = volume;
|
||
this._gainNode.gain.value = volume;
|
||
}
|
||
return this;
|
||
},
|
||
|
||
/**
|
||
* 设置是否静音。
|
||
*/
|
||
setMute: function(muted){
|
||
if(this.muted != muted){
|
||
this.muted = muted;
|
||
this._gainNode.gain.value = muted ? 0 : this.volume;
|
||
}
|
||
return this;
|
||
},
|
||
|
||
Statics: /** @lends WebAudio */ {
|
||
/**
|
||
* 浏览器是否支持WebAudio。
|
||
*/
|
||
isSupported: context !== null,
|
||
|
||
/**
|
||
* 浏览器是否已激活WebAudio。
|
||
*/
|
||
enabled: false,
|
||
|
||
/**
|
||
* 激活WebAudio。注意:需用户事件触发此方法才有效。激活后,无需用户事件也可播放音频。
|
||
*/
|
||
enable: function(){
|
||
if(!this.enabled && context){
|
||
var source = context.createBufferSource();
|
||
source.buffer = context.createBuffer(1, 1, 22050);
|
||
source.connect(context.destination);
|
||
source.start ? source.start(0, 0, 0) : source.noteOn(0, 0, 0);
|
||
this.enabled = true;
|
||
return true;
|
||
}
|
||
return this.enabled;
|
||
},
|
||
/**
|
||
* The audio buffer caches.
|
||
* @private
|
||
* @type {Object}
|
||
*/
|
||
_bufferCache:{},
|
||
/**
|
||
* 清除audio buffer 缓存。
|
||
* @param {String} url audio的网址,默认清除所有的缓存
|
||
*/
|
||
clearBufferCache:function(url){
|
||
if(url){
|
||
this._bufferCache[url] = null;
|
||
}
|
||
else{
|
||
this._bufferCache = {};
|
||
}
|
||
}
|
||
}
|
||
});
|
||
|
||
})(); |