OSDN Git Service

fix XNode.text & X.Audio.Sprite & parse json of X.Net.XHR.
[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_WebAudioWrapper;
4
5 if( !X_UA.iPhone_4s && !X_UA.iPod_2Mini1 && !X_UA.iPod_4 && X_Audio_WebAudio_context ){
6         
7         X_Audio_WebAudio_context = new X_Audio_WebAudio_context;
8         
9         function X_Audio_WebAudio_getBuffer( url ){
10                 var i = 0, l = X_Audio_WRAPPER_LIST.length;
11                 for( i = 0; i < l; ++i ){
12                         if( X_Audio_WRAPPER_LIST[ i ].url === url ) return X_Audio_WRAPPER_LIST[ i ];
13                 };
14         };
15         
16         X_Audio_WebAudioWrapper = X.EventDispatcher.inherits(
17                 'X.AV.WebAudioWrapper',
18                 X.Class.POOL_OBJECT,
19                 {
20                         
21                         url             : '',
22                         proxy           : null,
23                         
24                         startTime       : 0,
25                         endTime         : -1,
26                         loopStartTime   : -1,
27                         loopEndTime     : -1,
28                         seekTime        : -1,
29                         duration        : 0,
30                         
31                         playing         : false,
32                         error           : 0,                    
33                         loop            : false,
34                         looped          : false,
35                         autoplay        : false,
36                         volume          : 0.5,
37                                                 
38                         _startTime      : 0,
39                         _endTime        : 0,
40                         _playTime       : 0,
41             _timerID        : 0,
42             _interval       : 0,
43                 buffer          : null,
44                 source          : null,
45             gainNode        : null,
46             _onended        : null,
47             
48             xhr             : null,
49             onDecodeSuccess : null,
50             onDecodeError   : null,
51             
52                         Constructor : function( proxy, url, option ){
53                                 var audio = X_Audio_WebAudio_getBuffer( url );
54                                 
55                                 this.url = url;
56                                 this.closed = false;
57                                 this.proxy  = proxy;
58                                 
59                                 X_AudioWrapper_updateStates( this, option );
60                                 
61                                 if( audio && audio.buffer ){
62                                         this._onDecodeSuccess( audio.buffer );
63                                 } else
64                                 if( audio ){
65                                         // TODO 当てにしていたaudioがclose 等した場合
66                                         audio.proxy.listenOnce( 'canplaythrough', this, this._onBufferReady );
67                                 } else {
68                                         this.xhr = X.Net.xhrGet( url, 'arraybuffer' )
69                                                                         .listen( X.Event.PROGRESS, this )
70                                                                         .listenOnce( [ X.Event.SUCCESS, X.Event.COMPLETE, X.Event.CANCELED ], this );                                   
71                                 };
72                         },
73                         
74                         handleEvent : function( e ){
75                                 switch( e.type ){
76                                         case X.Event.PROGRESS :
77                                                 e.percent ?
78                                                         this.proxy.dispatch( { type : 'progress', percent : e.percent } ) :
79                                                         this.proxy.dispatch( 'loadstart' );
80                                                 return;
81                                         
82                                         case X.Event.SUCCESS :
83                                                 console.log( 'WebAudio xhr success! ' + !!X_Audio_WebAudio_context.decodeAudioData );
84                                         // TODO 旧api
85                                         // https://developer.mozilla.org/en-US/docs/Web/API/Web_Audio_API/Porting_webkitAudioContext_code_to_standards_based_AudioContext
86                                                 if( X_Audio_WebAudio_context.decodeAudioData ){
87                                                         X_Audio_WebAudio_context.decodeAudioData( e.data,
88                                                                 this.onDecodeSuccess = X_Callback_create( this, this._onDecodeSuccess ),
89                                                                 this.onDecodeError   = X_Callback_create( this, this._onDecodeError ) );
90                                                 } else {
91                                                         this._onDecodeSuccess( X_Audio_WebAudio_context.createBuffer( e.data, false ) );
92                                                 };
93                                                 break;
94
95                                         case X.Event.CANCELED :
96                                                 this.error = 1;
97                                                 this.proxy.dispatch( 'aborted' );
98                                                 break;
99
100                                         case X.Event.COMPLETE :
101                                                 this.error = 2;                         
102                                                 this.proxy.asyncDispatch( { type : 'error', message : 'xhr error' } );
103                                                 break;
104                                 };
105                                 this.xhr.unlisten( [ X.Event.PROGRESS, X.Event.SUCCESS, X.Event.COMPLETE, X.Event.CANCELED ], this );
106                                 delete this.xhr;
107                         },
108                         
109                                 _onDecodeSuccess : function( buffer ){
110                                         console.log( 'WebAudio decode success!' );
111                                         
112                                         this.onDecodeSuccess && this._onDecodeComplete();
113                                         
114                         if ( !buffer ) {
115                             this.proxy.asyncDispatch( { type : 'error', message : 'buffer is ' + buffer } );
116                             return;
117                         };
118         
119                         this.buffer   = buffer;
120                         this.duration = buffer.duration * 1000;
121                         
122                         this.proxy.asyncDispatch( 'loadedmetadata' );
123                         this.proxy.asyncDispatch( 'loadeddata' );
124                         this.proxy.asyncDispatch( 'canplay' );
125                         this.proxy.asyncDispatch( 'canplaythrough' );
126                         
127                         this.autoplay && X.Timer.once( 16, this, this.play );
128                         
129                         console.log( 'WebAudio decoded!' );
130                                 },
131                                 
132                                 _onDecodeError : function(){
133                                         console.log( 'WebAudio decode error!' );
134                                         this._onDecodeComplete();
135                                         this.error = 3;
136                                         this.proxy.asyncDispatch( { type : 'error', message : 'decode error' } );
137                                 },
138                                 
139                                 _onDecodeComplete : function(){
140                                         X_Callback_correct( this.onDecodeSuccess );
141                                         delete this.onDecodeSuccess;
142                                         X_Callback_correct( this.onDecodeError );
143                                         delete this.onDecodeError;
144                                 },
145                                 
146                                 _onBufferReady : function( e ){
147                                         var audio = X_Audio_WebAudio_getBuffer( this.url );
148                                         this._onDecodeSuccess( audio.buffer );
149                                 },
150                         
151                         close : function(){     
152                     delete this.buffer;
153         
154                                 if( this.xhr ) this.xhr.close();
155                                 
156                                 if( this.onDecodeSuccess ){
157                                         // 回収はあきらめる、、、
158                                 };
159         
160                                 this.playing  && this.pause();
161                     this.source   && this._sourceDispose();
162         
163                     this._onended && X_Callback_correct( this._onended );       
164         
165                     this.gainNode && this.gainNode.disconnect();
166                         },
167                         
168                                 _sourceDispose : function(){
169                             this.source.disconnect();
170                             delete this.source.onended;
171                             delete this.source;
172                         },
173                         
174                         play : function(){
175                                 var begin, end;
176                                 
177                     if( !this.buffer ){
178                         this.autoplay = true;
179                         return;
180                     };
181                                 
182                                 end   = X_AudioWrapper_getEndTime( this );
183                                 begin = X_AudioWrapper_getStartTime( this, end, true );
184                                 
185                                 console.log( '[WebAudio] play ' + begin + ' -> ' + end );
186                                 
187                                 if( this.source ) this._sourceDispose();
188                                 if( !this.gainNode ){
189                                         this.gainNode = X_Audio_WebAudio_context.createGain ? X_Audio_WebAudio_context.createGain() : X_Audio_WebAudio_context.createGainNode();
190                         this.gainNode.connect( X_Audio_WebAudio_context.destination );
191                                 };
192                     this.source        = X_Audio_WebAudio_context.createBufferSource();
193                     this.source.buffer = this.buffer;
194                     this.source.connect( this.gainNode );
195                     
196                     this.gainNode.gain.value = this.volume;
197                     
198                     // おかしい、stop 前に外していても呼ばれる、、、@Firefox33.1
199                     // 破棄された X.Callback が呼ばれて、obj._() でエラーになる。Firefox では、onended は使わない
200                 if( false && this.source.onended !== undefined ){
201                         //console.log( '> use onended' );
202                         this.source.onended = this._onended || ( this._onended = X_Callback_create( this, this._onEnded ) );
203                 } else {
204                         this._timerID && X.Timer.remove( this._timerID );
205                                         this._timerID = X.Timer.once( end - begin, this, this._onEnded );
206                 };
207         
208                     if( this.source.start ){
209                         this.source.start( 0, begin / 1000, end / 1000 );
210                     } else {
211                         this.source.noteGrainOn( 0, begin / 1000, end / 1000 );
212                     };
213                     
214                     this.playing    = true;
215                     this._startTime = begin;
216                     this._endTime   = end;
217                     this._playTime  = X_Audio_WebAudio_context.currentTime * 1000;
218                     this._interval  = this._interval || X.Timer.add( 1000, 0, this, this._onInterval );
219                         },
220
221                                 _onInterval : function(){
222                                         if( !this.playing ){
223                                                 delete this._interval;
224                                                 return X_Callback_UN_LISTEN;
225                                         };
226                                         this.proxy.dispatch( 'timeupdate' );
227                                 },
228                                                 
229                                 _onEnded : function(){
230                                         var time;
231                                         delete this._timerID;
232                                         
233                             if( this.playing ){
234                                 time = X_Audio_WebAudio_context.currentTime * 1000 - this._playTime - this._endTime + this._startTime | 0;
235                                 //console.log( '> onEnd ' + ( this.playing && ( X_Audio_WebAudio_context.currentTime * 1000 - this._playTime ) ) + ' < ' + ( this._endTime - this._startTime ) );
236                                 if( this._onended ){
237                                         // Firefox 用の対策,,,
238                                         if( time < 0 ) return;
239                                 } else {
240                                         if( time < 0 ){
241                                                 console.log( '> onEnd ' + ( -time ) + ' start:' + this._startTime + '-' + this._endTime );
242                                                 this._timerID = X.Timer.once( -time, this, this._onEnded );
243                                                 return;
244                                         };
245                                 };
246                                 
247                                 if( this.loop ){
248                                         this.looped = true;
249                                         ( this.proxy.dispatch( 'looped' ) | X.Callback.PREVENT_DEFAULT ) || this.play();
250                                 } else {
251                                         this.pause();
252                                         this.proxy.dispatch( 'ended' );
253                                 };
254                             };
255                                 },
256                         
257                         pause : function(){
258                                 if( !this.playing ) return this;
259                                 
260                                 console.log( '[WebAudio] pause' );
261                                 
262                                 this.seekTime = this.state().currentTime;
263                                 
264                     this._timerID && X.Timer.remove( this._timerID );
265                                 delete this._timerID;
266                                 delete this.playing;
267
268                     if( this.source ){
269                         if( this.source.onended ) delete this.source.onended;
270                         
271                         this.source.stop ? 
272                                 this.source.stop( 0 ) : this.source.noteOff( 0 );
273                     };
274                         },
275         
276                         state : function( obj ){
277                                 var result;
278                                 
279                                 if( obj === undefined ){
280                                     return {
281                                         startTime     : this.startTime,
282                                         endTime       : this.endTime < 0 ? this.duration : this.endTime,
283                                         loopStartTime : this.loopStartTime < 0 ? this.startTime : this.loopStartTime,
284                                         loopEndTime   : this.loopEndTime < 0 ? ( this.endTime || this.duration ) : this.loopEndTime,
285                                         loop          : this.loop,
286                                         looped        : this.looped,
287                                         volume        : this.volume,
288                                         playing       : this.playing,                           
289                                         duration      : this.duration,
290                                         
291                                         currentTime   : this.playing ? ( X_Audio_WebAudio_context.currentTime * 1000 - this._playTime + this._startTime | 0 ) : this.seekTime,
292                                         error         : this.error
293                                     };
294                                 };
295                         
296                                 result = X_AudioWrapper_updateStates( this, obj );
297                                 
298                                 if( result & 2 || result & 1 ){ // seek
299                         this.play();
300                                 } else
301                                 if( result & 4 ){
302                        this.gainNode.gain.value = this.volume;
303                                 };
304                         }
305
306                 }
307         );
308         
309         X_Audio_BACKENDS.push(
310                 {
311                         backendName : 'Web Audio',
312
313                         detect : function( proxy, source, ext ){
314                                 var ok = ext === 'mp3' || ext === 'ogg';
315                                 
316                                 proxy.asyncDispatch( ok ? 'support' : 'nosupport' );
317                         },
318                         
319                         klass : X_Audio_WebAudioWrapper
320                 }
321         );
322
323 };