--- /dev/null
+
+var X_Audio_WebAudio_context = window.webkitAudioContext || window.AudioContext,
+ X_Audio_WebAudio_LIVE_LIST = [],
+ X_Audio_WebAudio_POOL_LIST = [],
+ X_Audio_WebAudio, X_Audio_WebAudioWrapper, X_Audio_rawAudio;
+
+if( X_Audio_WebAudio_context ){
+
+ X_Audio_WebAudio_context = new X_Audio_WebAudio_context;
+
+ function getWebAudioWrapper( proxy ){
+ var i = X_Audio_WebAudio_LIVE_LIST.length;
+ for( ; i; ){
+ if( X_Audio_WebAudio_LIVE_LIST[ --i ].proxy === proxy ) return X_Audio_WebAudio_LIVE_LIST[ i ];
+ };
+ };
+
+ X_Audio_WebAudio =
+ {
+ backendName : 'Web Audio',
+
+ detect : function( proxy, source, ext ){
+ var ok = ext === 'mp3' || ext === 'ogg';
+
+ proxy.asyncDispatch( ok ? 'support' : 'nosupport' );
+ },
+
+ register : function( proxy, source, option ){
+ X_Audio_WebAudio_LIVE_LIST.push( new X_Audio_WebAudioWrapper( proxy, source, option ) );
+ },
+
+ close : function( proxy ){
+ return getWebAudioWrapper( proxy ).close();
+ },
+
+ play : function( proxy, startTime, endTime, loop, loopStartTime ){
+ return getWebAudioWrapper( proxy ).play( startTime, endTime, loop, loopStartTime );
+ },
+
+ pause : function( proxy ){
+ return getWebAudioWrapper( proxy ).pause();
+ },
+
+ state : function( proxy, obj ){
+ return getWebAudioWrapper( proxy ).state( obj );
+ }
+ };
+
+ X_Audio_BACKENDS.push( X_Audio_WebAudio );
+
+ X_Audio_WebAudioWrapper = X.EventDispatcher.inherits(
+ 'X.AV.WebAudioWrapper',
+ X.Class.POOL_OBJECT,
+ {
+
+ proxy : null,
+
+ startTime : 0,
+ endTime : 0,
+ loopStartTime : 0,
+ seekTime : 0,
+ duration : 0,
+
+ playing : false,
+ error : 0,
+ loop : false,
+ volume : 0.5,
+
+ _startTime : 0,
+ _playTime : 0,
+ _timerID : 0,
+ _interval : 0,
+ buffer : null,
+ gainNode : null,
+ _onended : null,
+
+ xhr : null,
+ onDecodeSuccess : null,
+ onDecodeError : null,
+
+ Constructor : function( proxy, source, option ){
+ this.closed = false;
+ this.proxy = proxy;
+ this.xhr = X.Net.xhrGet( source, 'arraybuffer' )
+ .listen( X.Event.PROGRESS, this )
+ .listenOnce( [ X.Event.SUCCESS, X.Event.COMPLETE, X.Event.CANCELED ], this );
+ X_AudioWrapper_updateStates( this, option );
+ },
+
+ handleEvent : function( e ){
+ switch( e.type ){
+ case X.Event.PROGRESS :
+ e.percent ?
+ this.proxy.dispatch( { type : 'progress', percent : e.percent } ) :
+ this.proxy.dispatch( 'loadstart' );
+ return;
+
+ case X.Event.SUCCESS :
+ X_Audio_WebAudio_context.decodeAudioData( e.data,
+ this.callbackDecodeSuccess = X_Callback_create( this, this._onDecodeSuccess ),
+ this.callbackDecodeError = X_Callback_create( this, this._onDecodeError ) );
+ break;
+
+ case X.Event.CANCELED :
+ this.error = 1;
+ this.proxy.dispatch( 'aborted' );
+ break;
+
+ case X.Event.COMPLETE :
+ this.error = 2;
+ this.proxy.asyncDispatch( { type : 'error', message : 'xhr error' } );
+ break;
+ };
+ this.xhr.unlisten( [ X.Event.PROGRESS, X.Event.SUCCESS, X.Event.COMPLETE, X.Event.CANCELED ], this );
+ delete this.xhr;
+ },
+
+ _onDecodeSuccess : function( buffer ){
+ this._onDecodeComplete();
+
+ if ( !buffer ) {
+ this.proxy.asyncDispatch( { type : 'error', message : 'buffer is ' + buffer } );
+ return;
+ };
+
+ this.buffer = buffer;
+ this.duration = buffer.duration * 1000;
+ this.endTime = this.endTime || this.duration;
+
+ this.proxy.asyncDispatch( 'loadedmetadata' );
+ this.proxy.asyncDispatch( 'loadeddata' );
+ this.proxy.asyncDispatch( 'canplay' );
+ this.proxy.asyncDispatch( 'canplaythrough' );
+ },
+
+ _onDecodeError : function(){
+ this._onDecodeComplete();
+ this.error = 3;
+ this.proxy.asyncDispatch( { type : 'error', message : 'decode error' } );
+ },
+
+ _onDecodeComplete : function(){
+ X_Callback_correct( this.callbackDecodeSuccess );
+ delete this.callbackDecodeSuccess;
+ X_Callback_correct( this.callbackDecodeError );
+ delete this.callbackDecodeError;
+ },
+
+ close : function(){
+ delete this.buffer;
+
+ this.playing && this.pause();
+ this.source && this._sourceDispose();
+
+ this._onended && X_Callback_correct( this._onended );
+
+ this.gainNode && this.gainNode.disconnect();
+ },
+
+ _sourceDispose : function(){
+ this.source && this.source.disconnect();
+ delete this.source.onended;
+ delete this.source;
+ },
+
+ play : function( seekTime ){
+ var begin;
+
+ if( !this.buffer ) return this;
+
+ begin = ( seekTime || seekTime === 0 ) ? seekTime : this.playing ? this.loopStartTime : this.startTime;
+
+ console.log( '[WebAudio] play' );
+
+ if( this.source ) this._sourceDispose();
+ if( !this.gainNode ) this.gainNode = X_Audio_WebAudio_context.createGain();
+
+ this.source = X_Audio_WebAudio_context.createBufferSource();
+ this.source.buffer = this.buffer;
+ this.source.connect( this.gainNode );
+
+ this.gainNode.connect( X_Audio_WebAudio_context.destination );
+ this.gainNode.gain.value = this.volume;
+
+ this._timerID && X.Timer.remove( this._timerID );
+
+ // おかしい、stop 前に外していても呼ばれる、、、@Firefox
+ if( this.source.onended !== undefined ){
+ //console.log( '> use onended' );
+ this.source.onended = this._onended || ( this._onended = X_Callback_create( this, this._onEnded ) );
+ } else {
+ this._timerID = X.Timer.once( this.endTime - begin, this, this._onEnded );
+ };
+
+ if( this.source.start ){
+ this.source.start( 0, begin / 1000, this.endTime / 1000 );
+ } else {
+ this.source.noteGrainOn( 0, begin / 1000, this.endTime / 1000 );
+ };
+
+ this.playing = true;
+ this._startTime = begin;
+ this._playTime = X_Audio_WebAudio_context.currentTime * 1000;
+ this._interval = this._interval || X.Timer.add( 1000, 0, this, this._onInterval );
+ },
+
+ _onInterval : function(){
+ if( !this.playing ){
+ delete this._interval;
+ return X_Callback_UN_LISTEN;
+ };
+ this.proxy.dispatch( 'timeupdate' );
+ },
+
+ _onEnded : function(){
+ delete this._timerID;
+ if( this.playing ){
+ console.log( '> onEnd ' + ( this.playing && ( X_Audio_WebAudio_context.currentTime * 1000 - this._playTime ) ) + ' < ' + ( this.endTime - this._startTime ) );
+ // Firefox 用の対策,,,
+ if( X_Audio_WebAudio_context.currentTime * 1000 - this._playTime < this.endTime - this._startTime ) return;
+
+ if( this.loop ){
+ this.play();
+ } else {
+ this.pause();
+ this.proxy.dispatch( 'ended' );
+ };
+ };
+ },
+
+ pause : function(){
+ if( !this.playing ) return this;
+
+ console.log( '[WebAudio] pause' );
+
+ this._timerID && X.Timer.remove( this._timerID );
+ delete this._timerID;
+ delete this.playing;
+
+ if( this.source ){
+ if( this.source.onended ) delete this.source.onended;
+
+ this.source.stop ?
+ this.source.stop( 0 ) : this.source.noteOff( 0 );
+ };
+ },
+
+ state : function( obj ){
+ var time = this.playing ? ( X_Audio_WebAudio_context.currentTime * 1000 - this._playTime ) : 0,
+ result, halfway;
+
+ if( obj === undefined ){
+ return {
+ startTime : this.startTime,
+ endTime : this.endTime,
+ loopStartTime : this.loopStartTime,
+ currentTime : time + this._startTime,
+ loop : this.loop,
+ volume : this.volume,
+ error : this.error,
+ playing : this.playing,
+ duration : this.duration
+ };
+ };
+
+ result = X_AudioWrapper_updateStates( this, obj );
+
+ if( result & 2 ){ // seek
+ this.play( this.seekTime );
+ delete this.seekTime;
+ } else
+ if( result & 1 ){
+ this.play();
+ } else
+ if( result & 4 ){
+ this.gainNode.gain.value = this.volume;
+ };
+ }
+
+ }
+ );
+
+};