3 * original : uupaa-js SilverlightAudio.js
\r
4 * https://code.google.com/p/uupaa-js/source/browse/trunk/0.8/src/Audio/SilverlightAudio.js?r=568
\r
6 * Silverlight 4 → 5における不具合の状況
\r
7 * http://www.slideshare.net/wakabayashiy/silverlight-4-5
\r
9 * IE10以降でSilverlightでF5押したらフリーズする不具合と対処
\r
10 * http://katsuyuzu.hatenablog.jp/entry/2014/01/11/003550
\r
12 * SilverlLight5 ie6&7(ietester,winxp), ie8(winxp) で動作確認。firefox32 では動作しない。(4以下の方がよい?)
\r
15 var X_Audio_SLAudioWrapper,
\r
16 X_Audio_SLAudio_uid = 0;
\r
18 if( X[ 'Pulgin' ][ 'SilverlightEnabled' ] ){
\r
20 X_TEMP.slaudioInit = function(){
\r
22 // http://blog.yuhiisk.com/archive/2014/12/20/dynamic-loading-and-complete-processing-of-script.html
\r
23 var s = document.createElement( '<script id="silverlightaudio" type="text/xaml"></script>' );
\r
25 document.getElementsByTagName( 'head' )[ 0 ].appendChild( s );
\r
26 s.text = '<Canvas xmlns="http://schemas.microsoft.com/client/2007" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"></Canvas>';
\r
28 delete X_TEMP.slaudioInit;
\r
31 // X.Node.inherits はできない。_rawObject は <object> でなく silverlight
\r
32 X_Audio_SLAudioWrapper = X_Audio_AbstractAudioBackend[ 'inherits' ](
\r
33 'X.AV.SilverlightAudio',
\r
34 X_Class.POOL_OBJECT,
\r
36 '_rawType' : X_EventDispatcher_EVENT_TARGET_SILVER_LIGHT,
\r
44 _lastUserAction : '',
\r
46 _interval : 0, // setInterval timer id
\r
48 'Constructor' : function( target, source, option ){
\r
49 !X_Audio_SLAudio_uid && X_TEMP.slaudioInit();
\r
52 * [Silverlight 2]JavaScriptコードからSilverlightのオブジェクトを利用するには?[C#、VB]
\r
53 * http://www.atmarkit.co.jp/fdotnet/dotnettips/902slobjcallfromjs/slobjcallfromjs.html
\r
54 * このページのサンプルは sl5+firefox32 環境で動いている。xaml を js から利用する形ではなく、.xap を sl4 以下で作るのがよさそう.
\r
58 this.target = target || this;
\r
59 this._source = source;
\r
60 // X.Audio._slOnload_ は不可
\r
61 this._onload = 'XAudioSilverlightOnLoad' + ( ++X_Audio_SLAudio_uid );
\r
62 this._callback = window[ this._onload ] = X_Closure_create( this, this.onSLReady );
\r
63 this.xnodeObject = X_Node_body
\r
64 [ 'create' ]( 'object', {
\r
65 type : 'application/x-silverlight-2',
\r
66 data : 'data:application/x-silverlight-2,',
\r
71 '<param name="background" value="#00000000">' + // transparent
\r
72 '<param name="windowless" value="true">' +
\r
73 '<param name="source" value="#silverlightaudio">' + // XAML ID
\r
74 '<param name="onload" value="' + this._onload + '">' // + // bond to global
\r
75 //'<param value="2.0.31005.0" name="minRuntimeVersion">' +
\r
76 //'<param value="true" name="autoUpgrade">' +
\r
77 //'<param name="onerror" value="slerror">' // bond to global
\r
79 this.setState( option );
\r
81 this[ 'listenOnce' ]( X_EVENT_KILL_INSTANCE );
\r
84 onSLReady : function( sender ){
\r
85 if( !this._onload ) return;
\r
87 window[ this._onload ] = null;
\r
88 delete this._onload;
\r
89 X_Closure_correct( this._callback );
\r
90 delete this._callback;
\r
92 sender[ 'children' ][ 'add' ](
\r
93 sender[ 'GetHost' ]()[ 'content' ][ 'CreateFromXaml' ](
\r
94 '<Canvas xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">' +
\r
95 '<MediaElement x:Name="media" Source="' + this._source + '" Volume="' + this.gain + '" AutoPlay="false" />' +
\r
98 this[ '_rawObject' ] = sender[ 'findName' ]( 'media' ); // x:Name='media'
\r
100 this[ 'listen' ]( [ 'MediaFailed', 'MediaOpened', 'MediaEnded', 'CurrentStateChanged' ] );
\r
103 handleEvent : function( e ){
\r
104 var lastState, currentState;
\r
106 console.log( e.type );
\r
109 case 'MediaFailed' :
\r
111 this.playing = false;
\r
112 this._ended = true;
\r
113 this._paused = false;
\r
114 if( this.playing ){
\r
115 //X_Timer_once( 16, this, this.actualPlay );
\r
117 this.target[ 'dispatch' ]( X_EVENT_ERROR ); // open failed
\r
122 case 'MediaOpened' :
\r
123 // http://msdn.microsoft.com/ja-jp/library/bb979710(VS.95).aspx
\r
124 this.duration = this[ '_rawObject' ][ 'NaturalDuration' ][ 'Seconds' ] * 1000;
\r
125 this.target[ 'asyncDispatch' ]( X_EVENT_READY );
\r
127 this.autoplay && X_Timer_once( 16, this, this.actualPlay );
\r
130 case 'MediaEnded' :
\r
131 //console.log( ' > ' + this.autoLoop + ' error:' + this.error );
\r
132 //this.autoLoop && /* this.playing && */ this.actualPlay();
\r
133 this._ended = true;
\r
136 case 'CurrentStateChanged' :
\r
137 lastState = this._lastState,
\r
138 currentState = this[ '_rawObject' ][ 'CurrentState' ];
\r
140 // ignore consecutive events or 'Closed' == 'Error'
\r
141 if( lastState === currentState
\r
142 || ( (lastState === 'Closed' || lastState === 'Error') && ( currentState === 'Closed' || currentState === 'Error') ) ){
\r
145 this._lastState = currentState; // update last state
\r
147 console.log( ' > ' + currentState + ' - ' + this._lastUserAction );
\r
149 switch( currentState ){
\r
152 switch( this._lastUserAction ){
\r
154 this.target[ 'dispatch' ]( X_EVENT_MEDIA_WAITING );
\r
157 this.target[ 'dispatch' ]( X_EVENT_MEDIA_SEEKING );
\r
164 // media.play(none supported file) -> 'Error'
\r
165 // media.play(file not found) -> 'Closed'
\r
166 // media.load -> 'Error'
\r
170 this.error = this.error || 2;
\r
171 this.playing = false;
\r
172 this._ended = true;
\r
173 this._paused = false;
\r
174 this.target[ 'dispatch' ]( X_EVENT_ERROR );
\r
178 // userAction.pause() -> MediaState('Paused') -> x
\r
179 // userAction.stop() -> MediaState('Paused') -> x
\r
180 // userAction.play() + file end -> MediaState('Paused') -> uueventfire('ended')
\r
183 this.playing && X_Timer_once( 16, this, this.actualPlay );
\r
184 //this.playing = false;
\r
186 switch( this._lastUserAction ){
\r
187 case 'play': // play() -> file end -> event('ended')
\r
189 //this.seekTime = 0;
\r
190 this._ended = true;
\r
191 this._paused = false;
\r
192 //this.target[ 'dispatch' ]( X_EVENT_MEDIA_ENDED );
\r
193 //this.setCurrentTime( this.startTime );
\r
196 this._ended = false;
\r
197 this._paused = true;
\r
200 this._ended = true;
\r
201 this._paused = false;
\r
205 // media.play -> 'Playing'
\r
208 //this.playing = true;
\r
209 this._ended = false;
\r
210 this._paused = false;
\r
211 this.target[ 'dispatch' ]( X_EVENT_MEDIA_PLAYING );
\r
216 this.playing && X_Timer_once( 16, this, this.actualPlay );
\r
219 //this.playing = false;
\r
220 this._ended = true;
\r
221 this._paused = false;
\r
222 //this.setCurrentTime( this.startTime );
\r
227 case X_EVENT_KILL_INSTANCE :
\r
228 this.playing && this.target[ 'dispatch' ]( X_EVENT_MEDIA_ENDED );
\r
229 this.playing && this.actualPause();
\r
231 if( this._onload ){
\r
232 // window への delete に ie5 は対応しないが、そもそも ie5 は Silverlight に非対応
\r
233 window[ this._onload ] = null;
\r
234 delete this._onload;
\r
235 X_Closure_correct( this._callback );
\r
237 this.xnodeObject[ 'kill' ]();
\r
242 // SilverlightAudio.play
\r
243 actualPlay : function(){
\r
244 var begin, offset, end;
\r
246 // もし kill 後に autoplayTimer で呼ばれても、_closed==true なので平気
\r
247 if( this.error ) return;
\r
248 if( !this.duration ){
\r
249 this.autoplay = true;
\r
253 this._lastUserAction = 0 <= this.seekTime ? 'seek' : 'play';
\r
255 end = X_AudioWrapper_getEndTime( this );
\r
256 begin = X_AudioWrapper_getStartTime( this, end, true ) | 0;
\r
258 // 1 秒以下は指定できないため四捨五入
\r
259 begin = ( begin / 1000 | 0 ) * 1000 + ( 500 < begin % 1000 ? 1000 : 0 );
\r
261 this[ '_rawObject' ][ 'Volume' ] = this.gain;
\r
263 this.setCurrentTime( this._beginTime = begin );
\r
265 console.log( '[play] ' + begin + ' -> ' + end );
\r
268 if( offset = begin - this.getActualCurrentTime() ){
\r
269 this.setCurrentTime( begin + offset );
\r
270 console.log( ' [差補正] ' + offset + ' ct:' + this.getActualCurrentTime() + ' begin:' + begin );
\r
271 this._beginTime = begin = this.getActualCurrentTime();
\r
274 if( !this.playing || this._ended ){
\r
275 console.log( '[play] play()' + begin + ' -> ' + end );
\r
276 this[ '_rawObject' ].play();
\r
277 this.playing = true;
\r
278 this._ended = false;
\r
281 this._timerID && X_Timer_remove( this._timerID );
\r
283 this._timerID = X_Timer_once( end - begin, this, this._onEnded );
\r
285 if( !this._interval ){
\r
286 this._interval = X_Timer_add( 1000, 0, this, this._onInterval );
\r
290 _onInterval : function(){
\r
291 if( !this.playing ){
\r
292 delete this._interval;
\r
293 return X_CALLBACK_UN_LISTEN;
\r
295 this.target[ 'dispatch' ]( X_EVENT_MEDIA_PLAYING );
\r
298 _onEnded : function(){
\r
300 delete this._timerID;
\r
302 if( this.playing ){
\r
303 //console.log( '> end ' + X_AudioWrapper_getEndTime( this ) + ' current:' + ( this.getActualCurrentTime() ) );
\r
304 time = this.getActualCurrentTime();
\r
306 if( time < this._beginTime ){
\r
307 console.log( '== waiting ' + time + ' < begin:' + this._beginTime );
\r
308 this.setCurrentTime( this._beginTime );
\r
309 time = this.getActualCurrentTime();
\r
310 console.log( ' > ' + time );
\r
311 this._ended && this[ '_rawObject' ].play();
\r
312 this._ended = false;
\r
313 this.target[ 'dispatch' ]( X_EVENT_MEDIA_WAITING );
\r
314 this._timerID = X_Timer_once( X_AudioWrapper_getEndTime( this ) - time, this, this._onEnded );
\r
318 time -= X_AudioWrapper_getEndTime( this );
\r
320 console.log( ' > まだ終わらない ' + time );
\r
321 this._ended && this[ '_rawObject' ].play();
\r
322 this._ended = false;
\r
323 this._timerID = X_Timer_once( -time, this, this._onEnded );
\r
327 if( this.autoLoop ){
\r
328 console.log( '========= loop?' );
\r
329 if( !( this.target[ 'dispatch' ]( X_EVENT_MEDIA_BEFORE_LOOP ) & X_CALLBACK_PREVENT_DEFAULT ) ){
\r
330 console.log( '========== loopした' );
\r
331 this.looped = true;
\r
332 this.target[ 'dispatch' ]( X_EVENT_MEDIA_LOOPED );
\r
336 console.log( '========= pause' );
\r
337 this.actualPause();
\r
338 this.target[ 'dispatch' ]( X_EVENT_MEDIA_ENDED );
\r
343 // SilverlightAudio.pause
\r
344 actualPause : function(){
\r
345 if( this.error || !this.playing ) return;
\r
347 this._lastUserAction = 'pause';
\r
348 this.seekTime = this.getActualCurrentTime();
\r
349 this.playing = false;
\r
350 this._paused = true;
\r
351 this._ended = false;
\r
353 this[ '_rawObject' ].pause();
\r
354 //this.target[ 'dispatch' ]( 'pause' );
\r
357 getActualCurrentTime : function(){
\r
358 return this[ '_rawObject' ][ 'Position' ][ 'Seconds' ] * 1000 | 0;
\r
361 afterUpdateState : function( result ){
\r
362 if( result & 3 ){ // seek
\r
366 end = X_AudioWrapper_getEndTime( this );
\r
367 halfway = end < this.duration;
\r
368 this._timerID && X_Timer_remove( this._timerID );
\r
371 this._timerID = X_Timer_once( end - this.getActualCurrentTime(), this, this._onEnded );
\r
373 delete this._timerID;
\r
377 this[ '_rawObject' ][ 'Volume' ] = this.gain;
\r
381 // SilverlightAudio.currentTime
\r
382 setCurrentTime : function( time ){ // @param Number: time
\r
383 var position = this[ '_rawObject' ][ 'Position' ]; // [!] create instance
\r
385 position[ 'Seconds' ] = time / 1000 | 0; // set current time
\r
387 this[ '_rawObject' ][ 'Position' ] = position; // [!] reattach instance
\r
393 function slerror(){
\r
394 alert( 'slerror' );
\r
397 X_Audio_BACKENDS.push( {
\r
398 backendName : 'Silverlight Audio',
\r
406 detect : function( proxy, source, ext ){
\r
407 var ok = ext === 'mp3' || ext === 'wma' || ext === 'wav';
\r
408 proxy[ 'asyncDispatch' ]( { type : X_EVENT_COMPLETE, canPlay : ok } );
\r
411 klass : X_Audio_SLAudioWrapper
\r