12 create : function( sourceList, opt_option ){
\r
13 return new X_AudioProxy( X.Type.isArray( sourceList ) ? X_Object_cloneArray( sourceList ) : [ sourceList ], opt_option || {} );
\r
17 var X_Audio_BACKENDS = [],
\r
18 X_Audio_WRAPPER_LIST = [];
\r
21 * TODO preplayerror play してみたら error が出た、backend の変更。
\r
24 function X_Audio_startDetectionBackend( backend, proxy, sourceList, option ){
\r
25 var source = sourceList[ 0 ] || '',
\r
26 ext = X_URL_cleanup( source ).split( '.' ).pop(),
\r
29 if( source && backend ){
\r
30 sup = [ proxy, sourceList, option, source, ext ];
\r
33 proxy.listenOnce( [ 'support', 'nosupport' ], backend, X_Audio_onEndDetection, sup );
\r
34 backend.detect( proxy, source, ext );
\r
36 proxy.asyncDispatch( 'nobackend' );
\r
40 function X_Audio_onEndDetection( e, proxy, sourceList, option, source, ext, sup ){
\r
41 var i = X_Audio_BACKENDS.indexOf( this ), backend;
\r
43 proxy.unlisten( [ 'support', 'nosupport' ], this, X_Audio_onEndDetection, sup );
\r
48 proxy.asyncDispatch( {
\r
49 type : 'backendfound',
\r
52 backendName : this.backendName
\r
56 console.log( 'No ' + source + ' ' + this.backendName );
\r
57 if( sup[ 3 ] = source = sourceList[ sourceList.indexOf( source ) + 1 ] ){
\r
58 sup[ 4 ] = ext = X_URL_cleanup( source ).split( '.' ).pop();
\r
59 proxy.listenOnce( [ 'support', 'nosupport' ], this, X_Audio_onEndDetection, sup );
\r
60 this.detect( proxy, source, ext );
\r
62 if( backend = X_Audio_BACKENDS[ i + 1 ] ){
\r
63 X_Audio_startDetectionBackend( backend, proxy, sourceList, option );
\r
65 proxy.asyncDispatch( 'nobackend' );
\r
71 function X_AudioProxy_getAudioWrapper( proxy ){
\r
72 var i = X_Audio_WRAPPER_LIST.length;
\r
74 if( X_Audio_WRAPPER_LIST[ --i ].proxy === proxy ) return X_Audio_WRAPPER_LIST[ i ];
\r
78 var X_AudioProxy = X.EventDispatcher.inherits(
\r
80 X.Class.POOL_OBJECT,
\r
86 Constructor : function( sourceList, option ){
\r
87 X_Audio_startDetectionBackend( X_Audio_BACKENDS[ 0 ], this, sourceList, option );
\r
88 this.listenOnce( [ 'backendfound', 'nobackend', X.Event.KILL_INSTANCE ], X_AudioProxy_handleEvent );
\r
92 return this._backend !== -1 && X_AudioProxy_getAudioWrapper( this ).close();
\r
95 play : function( startTime, endTime, loop, loopStartTime, loopEndTime ){
\r
96 var state, duration;
\r
97 if( 0 <= startTime ){
\r
99 currentTime : startTime,
\r
100 startTime : startTime,
\r
103 loopStartTime : loopStartTime,
\r
104 loopEndTime : loopEndTime
\r
107 this._backend !== -1 && X_AudioProxy_getAudioWrapper( this ).play();
\r
111 seek : function( seekTime ){
\r
112 var state = this.state(),
\r
113 end = X_AudioWrapper_getEndTime( X_AudioProxy_getAudioWrapper( this ) );
\r
114 if( seekTime < end ){
\r
115 this.state( { currentTime : seekTime } );
\r
120 pause : function(){
\r
121 this.state().playing && X_AudioProxy_getAudioWrapper( this ).pause();
\r
125 state : function( obj ){
\r
126 var backend = this._backend !== -1 && X_AudioProxy_getAudioWrapper( this );
\r
128 if( obj === undefined ){
\r
134 loopStartTime : -1,
\r
142 source : this.source || '',
\r
146 backend && backend.state( obj );
\r
150 loop : function( v ){
\r
151 var backend = this._backend !== -1 && X_AudioProxy_getAudioWrapper( this );
\r
152 if( v === undefined ){
\r
153 return backend && backend.state().loop;
\r
155 backend && backend.state( { loop : v } );
\r
159 volume : function( v ){
\r
160 var backend = this._backend !== -1 && X_AudioProxy_getAudioWrapper( this );
\r
161 if( v === undefined ){
\r
162 return backend && backend.state().volume;
\r
164 backend && backend.state( { volume : v } );
\r
168 currentTime : function( v ){
\r
169 var backend = this._backend !== -1 && X_AudioProxy_getAudioWrapper( this );
\r
170 if( v === undefined ){
\r
171 return backend && backend.state().currentTime;
\r
173 backend && backend.state( { currentTime : v } );
\r
177 isPlaying : function(){
\r
178 return this._backend !== -1 && X_AudioProxy_getAudioWrapper( this ).state().playing;
\r
184 function X_AudioProxy_handleEvent( e ){
\r
186 case 'backendfound' :
\r
187 this.unlisten( 'nobackend', X_AudioProxy_handleEvent );
\r
188 this.source = e.source;
\r
189 this.backendName = X_Audio_BACKENDS[ this._backend ].backendName;
\r
190 X_Audio_WRAPPER_LIST.push( new X_Audio_BACKENDS[ this._backend ].klass( this, e.source, e.option ) );
\r
197 case X.Event.KILL_INSTANCE :
\r
203 function X_AudioWrapper_updateStates( audioWrapper, obj ){
\r
204 var playing = audioWrapper.playing,
\r
206 end = 0, seek = 0, volume = 0;
\r
211 case 'currentTime' :
\r
212 v = X_AudioWrapper_timeStringToNumber( v );
\r
213 if( X.Type.isNumber( v ) ){
\r
215 if( audioWrapper.state().currentTime !== v ){
\r
216 audioWrapper.seekTime = v;
\r
220 audioWrapper.seekTime = v;
\r
229 case 'loopStartTime' :
\r
230 case 'loopEndTime' :
\r
231 v = X_AudioWrapper_timeStringToNumber( v );
\r
232 console.log( k + ' ' + v );
\r
233 if( v || v === 0 ){
\r
234 if( audioWrapper[ k ] !== v ){
\r
235 audioWrapper[ k ] = v;
\r
237 // 再生中の endTime の変更
\r
238 if( playing && ( k === 'endTime' || k === 'loopEndTime' ) ) end = 1;
\r
241 delete audioWrapper[ k ];
\r
242 if( playing && ( k === 'endTime' || k === 'loopEndTime' ) ) end = 1;
\r
247 if( playing ) seek = 2;
\r
250 if( X.Type.isBoolean( v ) && audioWrapper[ k ] !== v ){
\r
251 audioWrapper[ k ] = v;
\r
256 if( X.Type.isNumber( v ) ){
\r
257 v = v < 0 ? 0 : 1 < v ? 1 : v;
\r
258 if( audioWrapper[ k ] !== v ){
\r
259 audioWrapper[ k ] = v;
\r
260 // if playing -> update
\r
261 if( playing ) volume = 4;
\r
268 if( audioWrapper.endTime < audioWrapper.startTime ||
\r
269 ( audioWrapper.loopEndTime < 0 ? audioWrapper.endTime : audioWrapper.loopEndTime ) < ( audioWrapper.loopStartTime < 0 ? audioWrapper.startTime : audioWrapper.loopStartTime ) ||
\r
270 X_AudioWrapper_getEndTime( audioWrapper ) < audioWrapper.seekTime// ||
\r
271 //audioWrapper.duration < audioWrapper.endTime
\r
273 console.log( 'error @updateStateObject() begin:' + audioWrapper.startTime + ' end:' + audioWrapper.endTime + ' d:' + audioWrapper.duration + ' ls:' + audioWrapper.loopStartTime );
\r
277 return end + seek + volume;
\r
280 function X_AudioWrapper_timeStringToNumber( time ){
\r
281 var ary, ms, s = 0, m = 0, h = 0;
\r
282 if( X.Type.isNumber( time ) ) return time;
\r
283 if( !X.Type.isString( time ) || !time.length ) return;
\r
285 ary = time.split( '.' );
\r
286 ms = parseInt( ( ary[ 1 ] + '000' ).substr( 0, 3 ) ) || 0;
\r
288 ary = ary[ 0 ].split( ':' );
\r
289 if( 3 < ary.length ) return;
\r
291 switch( ary.length ){
\r
295 s = parseInt( ary[ 0 ] ) || 0;
\r
298 m = parseInt( ary[ 0 ] ) || 0;
\r
299 s = parseInt( ary[ 1 ] ) || 0;
\r
300 if( 60 <= s ) alert( 'invalid time string ' + time );
\r
303 h = parseInt( ary[ 0 ] ) || 0;
\r
304 m = parseInt( ary[ 1 ] ) || 0;
\r
305 s = parseInt( ary[ 2 ] ) || 0;
\r
306 if( 60 <= s ) alert( 'invalid time string ' + time );
\r
307 if( 60 <= m ) alert( 'invalid time string ' + time );
\r
310 alert( 'invalid time string ' + time );
\r
312 ms = ( h * 3600 + m * 60 + s ) * 1000 + ms;
\r
313 return ms < 0 ? 0 : ms;
\r
316 function X_AudioWrapper_getStartTime( audioWrapper, endTime, delSeekTime ){
\r
317 var seek = audioWrapper.seekTime;
\r
318 if( delSeekTime ) delete audioWrapper.seekTime;
\r
321 if( audioWrapper.duration <= seek || endTime < seek ) return 0;
\r
325 if( audioWrapper.looped && 0 <= audioWrapper.loopStartTime ){
\r
326 if( audioWrapper.duration <= audioWrapper.loopStartTime || endTime < audioWrapper.loopStartTime ) return 0;
\r
327 return audioWrapper.loopStartTime;
\r
330 if( audioWrapper.startTime < 0 || audioWrapper.duration <= audioWrapper.startTime ) return 0;
\r
331 return audioWrapper.startTime;
\r
334 function X_AudioWrapper_getEndTime( audioWrapper ){
\r
335 var duration = audioWrapper.duration;
\r
337 if( audioWrapper.looped && 0 <= audioWrapper.loopEndTime ){
\r
338 if( duration <= audioWrapper.loopEndTime ) return duration;
\r
339 return audioWrapper.loopEndTime;
\r
342 if( audioWrapper.endTime < 0 || duration <= audioWrapper.endTime ) return duration;
\r
343 return audioWrapper.endTime;
\r