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.Node.inherits はできない。_rawObject は <object> でなく silverlight
\r
21 X_Audio_SLAudioWrapper = X.EventDispatcher.inherits(
\r
22 'X.AV.SilverlightAudioWrapper',
\r
23 X.Class.POOL_OBJECT,
\r
25 '_rawType' : X_EventDispatcher_EVENT_TARGET_TYPE.SILVER_LIGHT,
\r
48 _lastUserAction : '',
\r
50 _interval : 0, // setInterval timer id
\r
52 Constructor : function( proxy, source, option ){
\r
54 if( !X_Audio_SLAudio_uid ){
\r
56 // X_Node_systemNode.create( 'script', { type : 'text/xaml', id : 'silverlightaudio' } )
\r
57 // .text( '<Canvas xmlns="http://schemas.microsoft.com/client/2007" ' +
\r
58 // 'xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"></Canvas>');
\r
61 // <script id="silverlightaudio" type="text/xaml"><Canvas xmlns="http://schemas.microsoft.com/client/2007" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"></Canvas></script>
\r
64 * [Silverlight 2]JavaScriptコードからSilverlightのオブジェクトを利用するには?[C#、VB]
\r
65 * http://www.atmarkit.co.jp/fdotnet/dotnettips/902slobjcallfromjs/slobjcallfromjs.html
\r
66 * このページのサンプルは sl5+firefox32 環境で動いている。xaml を js から利用する形ではなく、.xap を sl4 以下で作るのがよさそう.
\r
71 this._source = source;
\r
72 // X.Audio._slOnload_ は不可
\r
73 this._onload = 'XAudioSilverlightOnLoad' + ( ++X_Audio_SLAudio_uid );
\r
74 this._callback = window[ this._onload ] = X_Callback_create( this, this.onSLReady );
\r
75 this.xnodeObject = X_Node_body
\r
76 .create( 'object', {
\r
77 type : 'application/x-silverlight-2',
\r
78 data : 'data:application/x-silverlight-2,',
\r
83 '<param name="background" value="#00000000">' + // transparent
\r
84 '<param name="windowless" value="true">' +
\r
85 '<param name="source" value="#silverlightaudio">' + // XAML ID
\r
86 '<param name="onload" value="' + this._onload + '">' // + // bond to global
\r
87 //'<param value="2.0.31005.0" name="minRuntimeVersion">' +
\r
88 //'<param value="true" name="autoUpgrade">' +
\r
89 //'<param name="onerror" value="slerror">' // bond to global
\r
91 X_AudioWrapper_updateStates( this, option );
\r
93 this.listenOnce( X.Event.KILL_INSTANCE );
\r
96 onSLReady : function( sender ){
\r
97 if( !this._onload ) return;
\r
99 window[ this._onload ] = null;
\r
100 delete this._onload;
\r
101 X_Callback_correct( this._callback );
\r
102 delete this._callback;
\r
104 sender.children.add(
\r
108 '<Canvas xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">' +
\r
109 '<MediaElement x:Name="media" Source="' + this._source + '" Volume="' + this.volume + '" AutoPlay="false" />' +
\r
112 this._rawObject = sender.findName('media'); // x:Name='media'
\r
114 this.listen( [ 'MediaFailed', 'MediaOpened', 'MediaEnded', 'CurrentStateChanged' ] );
\r
117 handleEvent : function( e ){
\r
118 var lastState, currentState;
\r
120 console.log( e.type );
\r
123 case 'MediaFailed' :
\r
125 this.playing = false;
\r
126 this._ended = true;
\r
127 this._paused = false;
\r
128 this.proxy.dispatch( X.Event.ERROR ); // open failed
\r
131 case 'MediaOpened' :
\r
132 // http://msdn.microsoft.com/ja-jp/library/bb979710(VS.95).aspx
\r
133 this.duration = this._rawObject.NaturalDuration.Seconds * 1000;
\r
134 // TODO 'canplaythrough'
\r
135 //this.proxy.asyncDispatch( 'loadstart' );
\r
136 //this.proxy.asyncDispatch( 'loadedmetadata' );
\r
137 //this.proxy.asyncDispatch( 'loadeddata' );
\r
138 //this.proxy.asyncDispatch( 'canplay' );
\r
139 //this.proxy.asyncDispatch( 'canplaythrough' );
\r
140 this.proxy.asyncDispatch( X.Event.READY );
\r
142 this.autoplay && X.Timer.once( 16, this, this.play );
\r
145 case 'MediaEnded' :
\r
146 this.loop && this.playing && this.play();
\r
149 case 'CurrentStateChanged' :
\r
150 lastState = this._lastState,
\r
151 currentState = this._rawObject.CurrentState;
\r
153 // ignore consecutive events or 'Closed' == 'Error'
\r
154 if( lastState === currentState
\r
155 || ( (lastState === 'Closed' || lastState === 'Error') && ( currentState === 'Closed' || currentState === 'Error') ) ){
\r
158 this._lastState = currentState; // update last state
\r
160 switch( currentState ){
\r
163 switch( this._lastUserAction ){
\r
165 this.proxy.dispatch( X.Event.MEDIA_WAITING );
\r
168 this.proxy.dispatch( X.Event.MEDIA_SEEKING );
\r
175 // media.play(none supported file) -> 'Error'
\r
176 // media.play(file not found) -> 'Closed'
\r
177 // media.load -> 'Error'
\r
181 this.error = this.error || 2;
\r
182 this.playing = false;
\r
183 this._ended = true;
\r
184 this._paused = false;
\r
185 this.proxy.dispatch( X.Event.ERROR );
\r
188 // userAction.pause() -> MediaState('Paused') -> x
\r
189 // userAction.stop() -> MediaState('Paused') -> x
\r
190 // userAction.play() + file end -> MediaState('Paused') -> uueventfire('ended')
\r
192 this.playing = false;
\r
194 switch( this._lastUserAction ){
\r
195 case 'play': // play() -> file end -> event('ended')
\r
198 this._ended = true;
\r
199 this._paused = false;
\r
200 this.proxy.dispatch( X.Event.MEDIA_ENDED );
\r
201 this._currentTime( this.startTime );
\r
204 this._ended = false;
\r
205 this._paused = true;
\r
208 this._ended = true;
\r
209 this._paused = false;
\r
213 // media.play -> 'Playing'
\r
216 this.playing = true;
\r
217 this._ended = false;
\r
218 this._paused = false;
\r
219 this.proxy.dispatch( X.Event.MEDIA_PLAYING );
\r
224 this.playing = false;
\r
225 this._ended = true;
\r
226 this._paused = false;
\r
227 this._currentTime( this.startTime );
\r
232 case X.Event.KILL_INSTANCE :
\r
233 if( this._onload ){
\r
234 // window への delete に ie5 は対応しないが、そもそも ie5 は Silverlight に非対応
\r
235 window[ this._onload ] = null;
\r
236 delete this._onload;
\r
237 X_Callback_correct( this._callback );
\r
239 this.xnodeObject.destroy();
\r
244 close : function(){
\r
245 this.playing && this.pause();
\r
246 this.proxy.dispatch( X.Event.MEDIA_ENDED );
\r
250 // SilverlightAudio.play
\r
254 // もし kill 後に autoplayTimer で呼ばれても、_closed==true なので平気
\r
255 if( this.error ) return;
\r
256 if( !this.duration ){
\r
257 this.autoplay = true;
\r
261 this._lastUserAction = 0 <= this.seekTime ? 'seek' : 'play';
\r
263 end = X_AudioWrapper_getEndTime( this );
\r
264 begin = X_AudioWrapper_getStartTime( this, end, true ) | 0;
\r
266 console.log( '[SLAudio] play ' + begin + ' -> ' + end );
\r
268 this._rawObject.Volume = this.volume;
\r
269 this._beginTime = begin;
\r
270 this._currentTime( begin );
\r
272 if( !this.playing ){
\r
273 this._rawObject.play();
\r
274 //this.proxy.dispatch( 'play' );
\r
276 this.playing = true;
\r
279 this._timerID && X.Timer.remove( this._timerID );
\r
281 if( end < this.duration ){
\r
282 this._timerID = X.Timer.once( end - begin, this, this._onEnded );
\r
284 delete this._timerID;
\r
287 if( !this._interval ){
\r
288 this._interval = X.Timer.add( 1000, 0, this, this._onInterval );
\r
292 _onInterval : function(){
\r
293 if( !this.playing ){
\r
294 delete this._interval;
\r
295 return X_Callback_UN_LISTEN;
\r
297 this.proxy.dispatch( X.Event.MEDIA_PLAYING );
\r
300 _onEnded : function(){
\r
302 delete this._timerID;
\r
304 if( this.playing ){
\r
305 console.log( '> end ' + X_AudioWrapper_getEndTime( this ) + ' current:' + ( this._rawObject.Position.Seconds * 1000 | 0 ) );
\r
306 time = this._rawObject.Position.Seconds * 1000 | 0;
\r
308 if( time <= this._beginTime ){
\r
309 console.log( '== waiting' );
\r
310 this.proxy.dispatch( X.Event.MEDIA_WAITING );
\r
311 this._timerID = X.Timer.once( X_AudioWrapper_getEndTime( this ) - this._beginTime, this, this._onEnded );
\r
315 time -= X_AudioWrapper_getEndTime( this );
\r
317 console.log( '> onEnd ' + time );
\r
318 this._timerID = X.Timer.once( -time, this, this._onEnded );
\r
323 if( !( this.proxy.dispatch( X.Event.MEDIA_BEFORE_LOOP ) & X.Callback.PREVENT_DEFAULT ) ){
\r
324 this.looped = true;
\r
325 this.proxy.dispatch( X.Event.MEDIA_LOOPED );
\r
330 this.proxy.dispatch( X.Event.MEDIA_ENDED );
\r
335 // SilverlightAudio.pause
\r
336 pause : function(){
\r
337 if( this.error || !this.playing ) return;
\r
339 this._lastUserAction = 'pause';
\r
340 this.seekTime = this.state().currentTime;
\r
341 this.playing = false;
\r
342 this._paused = true;
\r
343 this._ended = false;
\r
345 this._rawObject.pause();
\r
346 //this.proxy.dispatch( 'pause' );
\r
349 // SilverlightAudio.state
\r
350 state : function( obj ){ // @return Hash: { loop, error, paused, ended, source, duration }
\r
353 if( obj === undefined ){
\r
355 startTime : this.startTime,
\r
356 endTime : this.endTime < 0 ? this.duration : this.endTime,
\r
357 loopStartTime : this.loopStartTime < 0 ? this.startTime : this.loopStartTime,
\r
358 loopEndTime : this.loopEndTime < 0 ? ( this.endTime || this.duration ) : this.loopEndTime,
\r
360 // 整数化 しておかないと seek 時に不具合がある。
\r
361 currentTime : this.playing ? this._rawObject.Position.Seconds * 1000 | 0 : this.seekTime,
\r
364 looped : this.looped,
\r
365 volume : this.volume,
\r
366 error : this.error,
\r
367 playing : this.playing,
\r
368 duration : this.duration // this._rawObject.NaturalDuration.Seconds;
\r
372 result = X_AudioWrapper_updateStates( this, obj );
\r
374 if( result & 2 ){ // seek
\r
378 end = X_AudioWrapper_getEndTime( this );
\r
379 halfway = end < this.duration;
\r
380 this._timerID && X.Timer.remove( this._timerID );
\r
383 this._timerID = X.Timer.once( end - this._rawObject.Position.Seconds * 1000 | 0, this, this._onEnded );
\r
385 delete this._timerID;
\r
390 this._rawObject.Volume = this.volume;
\r
395 // SilverlightAudio.currentTime
\r
396 _currentTime : function( time ){ // @param Number: time
\r
397 var position = this._rawObject.Position; // [!] create instance
\r
399 position.Seconds = time / 1000 | 0; // set current time
\r
401 this._rawObject.Position = position; // [!] reattach instance
\r
407 function slerror(){
\r
408 alert( 'slerror' );
\r
411 X_Audio_BACKENDS.push( {
\r
412 backendName : 'Silverlight Audio',
\r
414 detect : function( proxy, source, ext ){
\r
415 var ok = ext === 'mp3' || ext === 'wma' || ext === 'wav';
\r
416 proxy.asyncDispatch( ok ? X_Audio_CAN_PLAY : X_Audio_NOT_PLAY );
\r
419 klass : X_Audio_SLAudioWrapper
\r