OSDN Git Service

forgot to commit...
[pettanr/clientJs.git] / 0.6.x / js / 07_audio / 01_XWebAudio.js
1
2 var X_Audio_WebAudio_context   = window.webkitAudioContext || window.AudioContext,
3         X_Audio_WebAudio_LIVE_LIST = [],
4         X_Audio_WebAudio_POOL_LIST = [],
5         X_Audio_WebAudio, X_Audio_WebAudioWrapper, X_Audio_rawAudio;
6
7 if( X_Audio_WebAudio_context ){
8         
9         X_Audio_WebAudio_context = new X_Audio_WebAudio_context;
10         
11         function getWebAudioWrapper( proxy ){
12                 var i = X_Audio_WebAudio_LIVE_LIST.length;
13                 for( ; i; ){
14                         if( X_Audio_WebAudio_LIVE_LIST[ --i ].proxy === proxy ) return X_Audio_WebAudio_LIVE_LIST[ i ];
15                 };
16         };
17         
18         X_Audio_WebAudio = 
19                 {
20                         backendName : 'Web Audio',
21
22                         detect : function( proxy, source, ext ){
23                                 var ok = ext === 'mp3' || ext === 'ogg';
24                                 
25                                 proxy.asyncDispatch( ok ? 'support' : 'nosupport' );
26                         },
27                         
28                         register : function( proxy, source, option ){
29                                 X_Audio_WebAudio_LIVE_LIST.push( new X_Audio_WebAudioWrapper( proxy, source, option ) );
30                         },
31                         
32                         close : function( proxy ){
33                                 return getWebAudioWrapper( proxy ).close();
34                         },
35                         
36                         play : function( proxy, startTime, endTime, loop, loopStartTime ){
37                                 return getWebAudioWrapper( proxy ).play( startTime, endTime, loop, loopStartTime );
38                         },
39                         
40                         pause : function( proxy ){
41                                 return getWebAudioWrapper( proxy ).pause();
42                         },
43         
44                         state : function( proxy, obj ){
45                                 return getWebAudioWrapper( proxy ).state( obj );
46                         }
47                 };
48         
49         X_Audio_BACKENDS.push( X_Audio_WebAudio );
50         
51         X_Audio_WebAudioWrapper = X.EventDispatcher.inherits(
52                 'X.AV.WebAudioWrapper',
53                 X.Class.POOL_OBJECT,
54                 {
55                         
56                         proxy           : null,
57                         
58                         startTime       : 0,
59                         endTime         : 0,
60                         loopStartTime   : 0,
61                         seekTime        : 0,
62                         duration        : 0,
63                         
64                         playing         : false,
65                         error           : 0,                    
66                         loop            : false,
67                         volume          : 0.5,
68                                                 
69                         _startTime      : 0,
70                         _playTime       : 0,
71             _timerID        : 0,
72             _interval       : 0,
73                 buffer          : null,
74             gainNode        : null,
75             _onended        : null,
76             
77             xhr             : null,
78             onDecodeSuccess : null,
79             onDecodeError   : null,
80             
81                         Constructor : function( proxy, source, option ){
82                                 this.closed = false;
83                                 this.proxy  = proxy;
84                                 this.xhr    = X.Net.xhrGet( source, 'arraybuffer' )
85                                                                 .listen( X.Event.PROGRESS, this )
86                                                                 .listenOnce( [ X.Event.SUCCESS, X.Event.COMPLETE, X.Event.CANCELED ], this );
87                                 X_AudioWrapper_updateStates( this, option );
88                         },
89                         
90                         handleEvent : function( e ){
91                                 switch( e.type ){
92                                         case X.Event.PROGRESS :
93                                                 e.percent ?
94                                                         this.proxy.dispatch( { type : 'progress', percent : e.percent } ) :
95                                                         this.proxy.dispatch( 'loadstart' );
96                                                 return;
97                                         
98                                         case X.Event.SUCCESS :
99                                                 X_Audio_WebAudio_context.decodeAudioData( e.data,
100                                                         this.callbackDecodeSuccess = X_Callback_create( this, this._onDecodeSuccess ),
101                                                         this.callbackDecodeError   = X_Callback_create( this, this._onDecodeError ) );
102                                                 break;
103
104                                         case X.Event.CANCELED :
105                                                 this.error = 1;
106                                                 this.proxy.dispatch( 'aborted' );
107                                                 break;
108
109                                         case X.Event.COMPLETE :
110                                                 this.error = 2;                         
111                                                 this.proxy.asyncDispatch( { type : 'error', message : 'xhr error' } );
112                                                 break;
113                                 };
114                                 this.xhr.unlisten( [ X.Event.PROGRESS, X.Event.SUCCESS, X.Event.COMPLETE, X.Event.CANCELED ], this );
115                                 delete this.xhr;
116                         },
117                         
118                                 _onDecodeSuccess : function( buffer ){
119                                         this._onDecodeComplete();
120                                         
121                         if ( !buffer ) {
122                             this.proxy.asyncDispatch( { type : 'error', message : 'buffer is ' + buffer } );
123                             return;
124                         };
125         
126                         this.buffer   = buffer;
127                         this.duration = buffer.duration * 1000;
128                         this.endTime  = this.endTime || this.duration;
129                         
130                         this.proxy.asyncDispatch( 'loadedmetadata' );
131                         this.proxy.asyncDispatch( 'loadeddata' );
132                         this.proxy.asyncDispatch( 'canplay' );
133                         this.proxy.asyncDispatch( 'canplaythrough' );
134                                 },
135                                 
136                                 _onDecodeError : function(){
137                                         this._onDecodeComplete();
138                                         this.error = 3;
139                                         this.proxy.asyncDispatch( { type : 'error', message : 'decode error' } );
140                                 },
141                                 
142                                 _onDecodeComplete : function(){
143                                         X_Callback_correct( this.callbackDecodeSuccess );
144                                         delete this.callbackDecodeSuccess;
145                                         X_Callback_correct( this.callbackDecodeError );
146                                         delete this.callbackDecodeError;
147                                 },
148                         
149                         close : function(){     
150                     delete this.buffer;
151         
152                                 this.playing  && this.pause();
153                     this.source   && this._sourceDispose();
154         
155                     this._onended && X_Callback_correct( this._onended );       
156         
157                     this.gainNode && this.gainNode.disconnect();
158                         },
159                         
160                                 _sourceDispose : function(){
161                             this.source && this.source.disconnect();
162                             delete this.source.onended;
163                             delete this.source;
164                         },
165                         
166                         play : function( seekTime ){
167                                 var begin;
168                                 
169                     if( !this.buffer ) return this;
170                                 
171                                 begin = ( seekTime || seekTime === 0 ) ? seekTime : this.playing ? this.loopStartTime : this.startTime;
172                                 
173                                 console.log( '[WebAudio] play' );
174                                 
175                                 if( this.source ) this._sourceDispose();
176                                 if( !this.gainNode ) this.gainNode = X_Audio_WebAudio_context.createGain();
177                                 
178                     this.source        = X_Audio_WebAudio_context.createBufferSource();
179                     this.source.buffer = this.buffer;
180                     this.source.connect( this.gainNode );
181                     
182                     this.gainNode.connect( X_Audio_WebAudio_context.destination );
183                     this.gainNode.gain.value = this.volume;
184                     
185                     this._timerID && X.Timer.remove( this._timerID );
186                     
187                     // おかしい、stop 前に外していても呼ばれる、、、@Firefox
188                 if( this.source.onended !== undefined ){
189                         //console.log( '> use onended' );
190                         this.source.onended = this._onended || ( this._onended = X_Callback_create( this, this._onEnded ) );
191                 } else {
192                                         this._timerID = X.Timer.once( this.endTime - begin, this, this._onEnded );
193                 };
194         
195                     if( this.source.start ){
196                         this.source.start( 0, begin / 1000, this.endTime / 1000 );
197                     } else {
198                         this.source.noteGrainOn( 0, begin / 1000, this.endTime / 1000 );
199                     };
200                     
201                     this.playing    = true;
202                     this._startTime = begin;
203                     this._playTime  = X_Audio_WebAudio_context.currentTime * 1000;
204                     this._interval  = this._interval || X.Timer.add( 1000, 0, this, this._onInterval );
205                         },
206
207                                 _onInterval : function(){
208                                         if( !this.playing ){
209                                                 delete this._interval;
210                                                 return X_Callback_UN_LISTEN;
211                                         };
212                                         this.proxy.dispatch( 'timeupdate' );
213                                 },
214                                                 
215                                 _onEnded : function(){
216                                         delete this._timerID;
217                             if( this.playing ){
218                                 console.log( '> onEnd ' + ( this.playing && ( X_Audio_WebAudio_context.currentTime * 1000 - this._playTime ) ) + ' < ' + ( this.endTime - this._startTime ) );
219                                 // Firefox 用の対策,,,
220                                 if( X_Audio_WebAudio_context.currentTime * 1000 - this._playTime < this.endTime - this._startTime ) return;
221                                 
222                                 if( this.loop ){
223                                         this.play();
224                                 } else {
225                                         this.pause();
226                                         this.proxy.dispatch( 'ended' );
227                                 };
228                             };
229                                 },
230                         
231                         pause : function(){
232                                 if( !this.playing ) return this;
233                                 
234                                 console.log( '[WebAudio] pause' );
235                                 
236                     this._timerID && X.Timer.remove( this._timerID );
237                                 delete this._timerID;
238                                 delete this.playing;
239
240                     if( this.source ){
241                         if( this.source.onended ) delete this.source.onended;
242                         
243                         this.source.stop ? 
244                                 this.source.stop( 0 ) : this.source.noteOff( 0 );
245                     };
246                         },
247         
248                         state : function( obj ){
249                                 var time = this.playing ? ( X_Audio_WebAudio_context.currentTime * 1000 - this._playTime ) : 0,
250                                         result, halfway;
251                                 
252                                 if( obj === undefined ){
253                                     return {
254                                         startTime     : this.startTime,
255                                         endTime       : this.endTime,
256                                         loopStartTime : this.loopStartTime,
257                                         currentTime   : time + this._startTime,
258                                         loop          : this.loop,
259                                         volume        : this.volume,
260                                         error         : this.error,
261                                         playing       : this.playing,                           
262                                         duration      : this.duration
263                                     };                                  
264                                 };
265                         
266                                 result = X_AudioWrapper_updateStates( this, obj );
267                                 
268                                 if( result & 2 ){ // seek
269                         this.play( this.seekTime );
270                         delete this.seekTime;
271                                 } else
272                                 if( result & 1 ){
273                                         this.play();
274                                 } else
275                                 if( result & 4 ){
276                        this.gainNode.gain.value = this.volume;
277                                 };
278                         }
279
280                 }
281         );
282
283 };