OSDN Git Service

Version 0.6.149, fix X.Audio & X.UI.
[pettanr/clientJs.git] / 0.6.x / js / 07_audio / 01_XWebAudio.js
index 958b441..00cf9c3 100644 (file)
@@ -2,6 +2,7 @@
 var X_Audio_WebAudio_context = !X_UA[ 'iPhone_4s' ]  && !X_UA[ 'iPad_2Mini1' ]  && !X_UA[ 'iPod_4' ]  &&
                                                                !( X_UA[ 'Gecko' ] && X_UA[ 'Android' ] ) &&
                                                                ( window.AudioContext || window.webkitAudioContext ),
+       X_Audio_BUFFER_LIST      = [],
        X_Audio_WebAudioWrapper;
 
 /*
@@ -11,80 +12,34 @@ if( X_Audio_WebAudio_context ){
        
        X_Audio_WebAudio_context = new X_Audio_WebAudio_context;
        
-       function X_Audio_WebAudio_getBuffer( url ){
-               var i = 0, l = X_Audio_WRAPPER_LIST.length;
-               for( i = 0; i < l; ++i ){
-                       if( X_Audio_WRAPPER_LIST[ i ].url === url ) return X_Audio_WRAPPER_LIST[ i ];
-               };
-       };
-       
-       X_Audio_WebAudioWrapper = X_EventDispatcher[ 'inherits' ](
-               'X.AV.WebAudioWrapper',
+       X_Audio_BufferLoader = X_EventDispatcher[ 'inherits' ](
+               'X.AV.WebAudioBufferLoader',
                X_Class.POOL_OBJECT,
                {
-                       
                        url             : '',
-                       proxy           : null,
-                       
-                       startTime       : 0,
-                       endTime         : -1,
-                       loopStartTime   : -1,
-                       loopEndTime     : -1,
-                       seekTime        : -1,
-                       duration        : 0,
-
-                       playing         : false,
-                       error           : 0,                    
-                       loop            : false,
-                       looped          : false,
-                       autoplay        : false,
-                       volume          : 0.5,
-                                               
-                       _startPos       : 0,
-                       _endPosition    : 0,
-                       _startTime      : 0,
-            _timerID        : 0,
-            _interval       : 0,
-               buffer          : null,
-               source          : null,
-            gainNode        : null,
-            _onended        : null,
-            
             xhr             : null,
             onDecodeSuccess : null,
             onDecodeError   : null,
             
-                       Constructor : function( proxy, url, option ){
-                               var audio = X_Audio_WebAudio_getBuffer( url );
-                               
-                               this.proxy  = proxy;
-                               this.url    = url;
-                               
-                               X_AudioWrapper_updateStates( this, option );
-                               
-                               if( audio && audio.buffer ){
-                                       this._onDecodeSuccess( audio.buffer );
-                               } else
-                               if( audio ){
-                                       // TODO 当てにしていたaudioがclose 等した場合
-                                       audio.proxy[ 'listenOnce' ]( 'canplaythrough', this, this._onBufferReady );
-                               } else {
-                                       this.xhr = X.Net( { 'xhr' : url, 'type' : 'arraybuffer' } )
+            buffer          : null,
+            error           : 0,
+            webAudioList    : null,
+            
+                       Constructor : function( webAudio, url ){
+                               this.webAudioList = [ webAudio ];
+                               this.url = url;
+                               this.xhr = X.Net( { 'xhr' : url, 'dataType' : 'arraybuffer' } )
                                                                        [ 'listen' ]( X_EVENT_PROGRESS, this )
-                                                                       [ 'listenOnce' ]( [ X_EVENT_SUCCESS, X_EVENT_COMPLETE, X_EVENT_CANCELED ], this );                                      
-                               };
+                                                                       [ 'listenOnce' ]( [ X_EVENT_SUCCESS, X_EVENT_COMPLETE ], this );
                        },
                        
                        handleEvent : function( e ){
                                switch( e.type ){
                                        case X_EVENT_PROGRESS :
-                                               e[ 'percent' ] ?
-                                                       this.proxy[ 'dispatch' ]( { type : 'progress', 'percent' : e[ 'percent' ] } ) :
-                                                       this.proxy[ 'dispatch' ]( 'loadstart' );
+                                               this[ 'dispatch' ]( { type : 'progress', 'percent' : e[ 'percent' ] } );
                                                return;
                                        
                                        case X_EVENT_SUCCESS :
-                                               console.log( 'WebAudio xhr success! ' + !!X_Audio_WebAudio_context.decodeAudioData + ' t:' + typeof e.data );
                                        // TODO 旧api
                                        // https://developer.mozilla.org/en-US/docs/Web/API/Web_Audio_API/Porting_webkitAudioContext_code_to_standards_based_AudioContext
                                        
@@ -104,17 +59,12 @@ if( X_Audio_WebAudio_context ){
                                                };
                                                break;
 
-                                       case X_EVENT_CANCELED :
-                                               this.error = 1;
-                                               this.proxy[ 'dispatch' ]( 'aborted' );
-                                               break;
-
                                        case X_EVENT_COMPLETE :
-                                               this.error = 2;                         
-                                               this.proxy[ 'asyncDispatch' ]( { type : X_EVENT_ERROR, message : 'xhr error' } );
+                                               this.error = 1;                         
+                                               this[ 'asyncDispatch' ]( X_EVENT_COMPLETE );
                                                break;
                                };
-                               this.xhr[ 'unlisten' ]( [ X_EVENT_PROGRESS, X_EVENT_SUCCESS, X_EVENT_COMPLETE, X_EVENT_CANCELED ], this );
+                               this.xhr[ 'unlisten' ]( [ X_EVENT_PROGRESS, X_EVENT_SUCCESS, X_EVENT_COMPLETE ], this );
                                delete this.xhr;
                        },
                        
@@ -124,30 +74,23 @@ if( X_Audio_WebAudio_context ){
                                        this.onDecodeSuccess && this._onDecodeComplete();
                                        
                        if ( !buffer ) {
-                           this.proxy[ 'asyncDispatch' ]( { type : X_EVENT_ERROR, message : 'buffer is ' + buffer } );
+                               this.error = 2;
+                           this[ 'asyncDispatch' ]( X_EVENT_COMPLETE );
                            return;
                        };
        
                        this.buffer   = buffer;
-                       this.duration = buffer.duration * 1000;
-                       /*
-                       this.proxy[ 'asyncDispatch' ]( 'loadedmetadata' );
-                       this.proxy[ 'asyncDispatch' ]( 'loadeddata' );
-                       this.proxy[ 'asyncDispatch' ]( 'canplay' );
-                       this.proxy[ 'asyncDispatch' ]( 'canplaythrough' );
-                       */
-                                       this.proxy[ 'asyncDispatch' ]( X_EVENT_READY );
-                       
-                       this.autoplay && X_Timer_once( 16, this, this.play );
-                       
+
+                                       this[ 'asyncDispatch' ]( X_EVENT_COMPLETE );
+
                        console.log( 'WebAudio decoded!' );
                                },
                                
                                _onDecodeError : function(){
                                        console.log( 'WebAudio decode error!' );
                                        this._onDecodeComplete();
-                                       this.error = 3;
-                                       this.proxy[ 'asyncDispatch' ]( { type : X_EVENT_ERROR, message : 'decode error' } );
+                                       this.error = 2;
+                                       this[ 'asyncDispatch' ]( X_EVENT_COMPLETE );
                                },
                                
                                _onDecodeComplete : function(){
@@ -156,36 +99,100 @@ if( X_Audio_WebAudio_context ){
                                        X_Callback_correct( this.onDecodeError );
                                        delete this.onDecodeError;
                                },
-                               
-                               _onBufferReady : function( e ){
-                                       var audio = X_Audio_WebAudio_getBuffer( this.url );
-                                       this._onDecodeSuccess( audio.buffer );
-                               },
                        
-                       close : function(){     
-                   delete this.buffer;
-       
-                               if( this.xhr ) this.xhr.close();
-                               
-                               if( this.onDecodeSuccess ){
-                                       // 回収はあきらめる、、、
+                       unregister : function( webAudio ){
+                               var list = this.webAudioList,
+                                       i    = list.indexOf( webAudio );
+                               if( 0 < i ){
+                                       list.splice( i, 1 );
+                                       if( list.length ){
+                                               this.xhr && this.xhr[ 'kill' ]();
+                                               this[ 'kill' ]();
+                                       };
                                };
+                       }
+                       
+               }
+       );
        
-                               this.playing  && this.pause();
-                   this.source   && this._sourceDispose();
        
-                   this._onended && X_Callback_correct( this._onended );       
-       
-                   this.gainNode && this.gainNode.disconnect();
-                       },
+       X_Audio_WebAudioWrapper = X_Audio_AbstractAudioBackend[ 'inherits' ](
+               'X.AV.WebAudioWrapper',
+               X_Class.POOL_OBJECT,
+               {
                        
-                               _sourceDispose : function(){
-                           this.source.disconnect();
-                           delete this.source.onended;
-                           delete this.source;
-                       },
+                       loader          : null,
+                                               
+                       _startPos       : 0,
+                       _endPosition    : 0,
+                       _startTime      : 0,
+            _timerID        : 0,
+            _interval       : 0,
+               buffer          : null,
+               source          : null,
+            gainNode        : null,
+            _onended        : null,
+            
+                       Constructor : function( target, url, option ){                          
+                               var i = 0,
+                                       l = X_Audio_BUFFER_LIST.length,
+                                       loader;
+
+                               for( ; i < l; ++i ){
+                                       loader = X_Audio_BUFFER_LIST[ i ];
+                                       if( loader.url === url ){
+                                               this.loader = loader;
+                                               loader.webAudioList.push( this );
+                                               break;
+                                       };
+                               };
+                               
+                               if( !this.loader ){
+                                       this.loader = loader = new X_Audio_BufferLoader( this, url );
+                               };
+                               
+                               this.target  = target || this;
+                               
+                               this.setState( option );
+                               
+                               this[ 'listenOnce' ]( X_EVENT_KILL_INSTANCE, X_WebAudio_handleEvent );
+                               
+                               if( loader.buffer || loader.error ){
+                                       this._onLoadBufferComplete();
+                               } else {
+                                       loader[ 'listenOnce' ]( X_EVENT_COMPLETE, this, this._onLoadBufferComplete );
+                               };
+                       },
+                               _onLoadBufferComplete : function( e ){
+                                       var loader = this.loader,
+                                               buffer = loader.buffer;
+                                       
+                                       e && loader[ 'unlisten' ]( X_EVENT_COMPLETE, this, this._onLoadBufferComplete );
+                                       
+                       if ( !buffer ) {
+                               this.error = loader.error;
+                               
+                           this.target[ 'dispatch' ]({
+                                                               type    : X_EVENT_ERROR,
+                                                               error   : loader.error,
+                                                               message : loader.error === 1 ?
+                                                                                       'load buffer network error' :
+                                                                                       'buffer decode error'
+                                                       });
+                                               this[ 'kill' ]();
+                           return;
+                       };
+       
+                       this.buffer   = buffer;
+                       this.duration = buffer.duration * 1000;
+
+                                       this.target[ 'asyncDispatch' ]( X_EVENT_READY );
+                       
+                       this.autoplay && X_Timer_once( 16, this, this.play );
+                                       
+                               },
                        
-                       play : function(){
+                       actualPlay : function(){
                                var begin, end;
                                
                    if( !this.buffer ){
@@ -207,7 +214,7 @@ if( X_Audio_WebAudio_context ){
                    this.source.buffer = this.buffer;
                    this.source.connect( this.gainNode );
                    
-                   this.gainNode.gain.value = this.volume;
+                   this.gainNode.gain.value = this.gain;
                    
                    // おかしい、stop 前に外していても呼ばれる、、、@Firefox33.1
                    // 破棄された X.Callback が呼ばれて、obj.proxy() でエラーになる。Firefox では、onended は使わない
@@ -231,13 +238,19 @@ if( X_Audio_WebAudio_context ){
                    this._startTime   = X_Audio_WebAudio_context.currentTime * 1000;
                    this._interval    = this._interval || X_Timer_add( 1000, 0, this, this._onInterval );
                        },
+                       
+                               _sourceDispose : function(){
+                           this.source.disconnect();
+                           delete this.source.onended;
+                           delete this.source;
+                       },
 
                                _onInterval : function(){
                                        if( !this.playing ){
                                                delete this._interval;
                                                return X_Callback_UN_LISTEN;
                                        };
-                                       this.proxy[ 'dispatch' ]( X_EVENT_MEDIA_PLAYING );
+                                       this.target[ 'dispatch' ]( X_EVENT_MEDIA_PLAYING );
                                },
                                                
                                _onEnded : function(){
@@ -259,25 +272,25 @@ if( X_Audio_WebAudio_context ){
                                        };
                                };
                                
-                               if( this.loop ){
-                                       if( !( this.proxy[ 'dispatch' ]( X_EVENT_MEDIA_BEFORE_LOOP ) & X_Callback_PREVENT_DEFAULT ) ){
+                               if( this.autoLoop ){
+                                       if( !( this.target[ 'dispatch' ]( X_EVENT_MEDIA_BEFORE_LOOP ) & X_Callback_PREVENT_DEFAULT ) ){
                                                this.looped = true;
-                                               this.proxy[ 'dispatch' ]( X_EVENT_MEDIA_LOOPED );
-                                               this.play();
+                                               this.target[ 'dispatch' ]( X_EVENT_MEDIA_LOOPED );
+                                               this.actualPlay();
                                        };
                                } else {
-                                       this.pause();
-                                       this.proxy[ 'dispatch' ]( X_EVENT_MEDIA_ENDED );
+                                       this.actualPause();
+                                       this.target[ 'dispatch' ]( X_EVENT_MEDIA_ENDED );
                                };
                            };
                                },
                        
-                       pause : function(){
+                       actualPause : function(){
                                if( !this.playing ) return this;
                                
                                console.log( '[WebAudio] pause' );
                                
-                               this.seekTime = this.state().currentTime;
+                               this.seekTime = this.getActualCurrentTime();
                                
                    this._timerID && X_Timer_remove( this._timerID );
                                delete this._timerID;
@@ -290,40 +303,42 @@ if( X_Audio_WebAudio_context ){
                                this.source.stop( 0 ) : this.source.noteOff( 0 );
                    };
                        },
-       
-                       state : function( obj ){
-                               var result;
-                               
-                               if( obj === undefined ){
-                                   return {
-                                       startTime     : this.startTime,
-                                       endTime       : this.endTime < 0 ? this.duration : this.endTime,
-                                       loopStartTime : this.loopStartTime < 0 ? this.startTime : this.loopStartTime,
-                                       loopEndTime   : this.loopEndTime < 0 ? ( this.endTime || this.duration ) : this.loopEndTime,
-                                       loop          : this.loop,
-                                       looped        : this.looped,
-                                       volume        : this.volume,
-                                       playing       : this.playing,                           
-                                       duration      : this.duration,
-                                       
-                                       currentTime   : this.playing ? ( X_Audio_WebAudio_context.currentTime * 1000 - this._startTime + this._startPos | 0 ) : this.seekTime,
-                                       error         : this.error
-                                   };
-                               };
                        
-                               result = X_AudioWrapper_updateStates( this, obj );
-                               
+                       getActualCurrentTime : function(){
+                               return X_Audio_WebAudio_context.currentTime * 1000 - this._startTime + this._startPos | 0;
+                       },
+                       
+                       afterUpdateState : function( result ){
                                if( result & 2 || result & 1 ){ // seek
-                       this.play();
+                       this.actualPlay();
                                } else
                                if( result & 4 ){
-                      this.gainNode.gain.value = this.volume;
+                      this.gainNode.gain.value = this.gain;
                                };
                        }
 
                }
        );
 
+       function X_WebAudio_handleEvent( e ){
+               switch( e.type ){
+
+                       case X_EVENT_KILL_INSTANCE :
+                               this.loader[ 'unlisten' ]( X_EVENT_COMPLETE, this, this._onLoadBufferComplete )
+                                       .unregister( this );
+
+                               delete this.buffer;
+                               
+                               this.playing  && this.actualPause();
+                   this.source   && this._sourceDispose();
+       
+                   this._onended && X_Callback_correct( this._onended );       
+       
+                   this.gainNode && this.gainNode.disconnect();
+                               break;
+               };
+       };
+
        /*
         * http://qiita.com/sou/items/5688d4e7d3a37b4e2ff1
         * L-01F 等の一部端末で Web Audio API の再生結果に特定条件下でノイズが混ざることがある。