OSDN Git Service

Version 0.6.133, fix for closure compiler - ADVANCED_OPTIMIZATIONS
[pettanr/clientJs.git] / 0.6.x / js / 07_audio / 03_XSilverlightAudio.js
1 \r
2 /*\r
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
5  *\r
6  * Silverlight 4 → 5における不具合の状況\r
7  * http://www.slideshare.net/wakabayashiy/silverlight-4-5 \r
8  * \r
9  * IE10以降でSilverlightでF5押したらフリーズする不具合と対処\r
10  * http://katsuyuzu.hatenablog.jp/entry/2014/01/11/003550\r
11  * \r
12  * SilverlLight5 ie6&7(ietester,winxp), ie8(winxp) で動作確認。firefox32 では動作しない。(4以下の方がよい?)\r
13  */\r
14 \r
15 var X_Audio_SLAudioWrapper,\r
16         X_Audio_SLAudio_uid = 0;\r
17 \r
18 if( X.Pulgin.SilverlightEnabled ){\r
19         \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
24                 {\r
25                         '_rawType'      : X_EventDispatcher_EVENT_TARGET_TYPE.SILVER_LIGHT,\r
26                 proxy           : null,\r
27                 \r
28                         startTime       : 0,\r
29                         endTime         : -1,\r
30                         loopStartTime   : -1,\r
31                         loopEndTime     : -1,\r
32                         seekTime        : -1,\r
33                         duration        : 0,\r
34                         \r
35                         playing         : false,\r
36                         error           : 0,                    \r
37                         loop            : false,\r
38                         looped          : false,\r
39                         autoplay        : false,\r
40                         volume          : 0.5,\r
41                 \r
42                 _onload         : '',\r
43                 _callback       : null,                 \r
44                 xnodeObject     : null,\r
45                         _source         : '',\r
46                         _ended          : true,\r
47                         _paused         : false,\r
48                         _lastUserAction : '',\r
49                         _lastState      : '',\r
50                         _interval       : 0, // setInterval timer id\r
51                         \r
52                         Constructor : function( proxy, source, option ){\r
53                                 \r
54                                 if( !X_Audio_SLAudio_uid ){\r
55                                         // source\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
59                                         \r
60                                         // html に以下を書いた                     \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
62                                 };\r
63                                 /*\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
67                                  */\r
68                                 \r
69                                 // TODO embed\r
70                                 this.proxy       = proxy;\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
79                                                 width  : 1,\r
80                                                 height : 1\r
81                                         })\r
82                                         .html(\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
90                                         );\r
91                                 X_AudioWrapper_updateStates( this, option );\r
92         \r
93                                 this.listenOnce( X_Event.KILL_INSTANCE );\r
94                         },\r
95                         \r
96                         onSLReady : function( sender ){\r
97                                 if( !this._onload ) return;\r
98                                 \r
99                                 window[ this._onload ] = null;\r
100                                 delete this._onload;\r
101                                 X_Callback_correct( this._callback );\r
102                                 delete this._callback;\r
103 \r
104                                 sender.children.add(\r
105                                         sender.GetHost().\r
106                                         content.\r
107                                         CreateFromXaml(\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
110                                         '</Canvas>'));\r
111                 \r
112                                 this._rawObject = sender.findName('media'); // x:Name='media'\r
113 \r
114                                 this.listen( [ 'MediaFailed', 'MediaOpened', 'MediaEnded', 'CurrentStateChanged' ] );\r
115                         },\r
116                         \r
117                         handleEvent : function( e ){\r
118                                 var lastState, currentState;\r
119                                 \r
120                                 console.log( e.type );\r
121                                 switch( e.type ){\r
122                                         \r
123                                         case 'MediaFailed' :\r
124                                                 this.error   = 4;\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
129                                                 break;\r
130 \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
141                                 \r
142                                 this.autoplay && X.Timer.once( 16, this, this.play );\r
143                                                 break;\r
144 \r
145                                         case 'MediaEnded' :                             \r
146                                                 this.loop && this.playing && this.play();\r
147                                                 break;\r
148 \r
149                                         case 'CurrentStateChanged' :\r
150                                                 lastState        = this._lastState,\r
151                                                 currentState = this._rawObject.CurrentState;\r
152                                 \r
153                                                 // ignore consecutive events or 'Closed' == 'Error'\r
154                                                 if( lastState === currentState\r
155                                                         || ( (lastState === 'Closed' || lastState === 'Error') && ( currentState === 'Closed' || currentState === 'Error') ) ){\r
156                                                         return;\r
157                                                 };\r
158                                                 this._lastState = currentState; // update last state\r
159                                 \r
160                                                 switch( currentState ){\r
161                                                         case 'Buffering' :\r
162                                                         case 'Opening' :\r
163                                                                 switch( this._lastUserAction ){\r
164                                                                         case 'play' :\r
165                                                                                 this.proxy.dispatch( X_Event.MEDIA_WAITING );\r
166                                                                                 break;\r
167                                                                         case 'seek' :\r
168                                                                                 this.proxy.dispatch( X_Event.MEDIA_SEEKING );\r
169                                                                                 break;\r
170                                                                         case 'pause' :\r
171                                                                                 break;\r
172                                                                 };\r
173                                                                 break;\r
174 \r
175                                                         // media.play(none supported file) -> 'Error'\r
176                                                         // media.play(file not found)     -> 'Closed'\r
177                                                         // media.load -> 'Error'\r
178                                                         case 'Error':\r
179                                                                 this.error   = 4;\r
180                                                         case 'Closed':\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
186                                                                 break;\r
187 \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
191                                                         case 'Paused':\r
192                                                                 this.playing = false;\r
193                                                                 \r
194                                                                 switch( this._lastUserAction ){\r
195                                                                         case 'play': // play() -> file end -> event('ended')\r
196                                                                         case 'seek':\r
197                                                                                 this.seekTime = 0;\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
202                                                                                 break;\r
203                                                                         case 'pause':\r
204                                                                                 this._ended  = false;\r
205                                                                                 this._paused = true;\r
206                                                                                 break;\r
207                                                                         case 'stop':\r
208                                                                                 this._ended  = true;\r
209                                                                                 this._paused = false;\r
210                                                                 };\r
211                                                                 break;\r
212 \r
213                                                         // media.play -> 'Playing'\r
214                                                         case 'Playing':\r
215                                                                 this.error   = 0;\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
220                                                                 break;\r
221 \r
222                                                         // stop()\r
223                                                         case 'Stopped':\r
224                                                                 this.playing = false;\r
225                                                                 this._ended  = true;\r
226                                                                 this._paused = false;\r
227                                                                 this._currentTime( this.startTime );\r
228                                                                 break;\r
229                                                 };\r
230                                                 break;\r
231 \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
238                                                 };\r
239                                                 this.xnodeObject.destroy();\r
240                                                 break;\r
241                                 };\r
242                         },\r
243                         \r
244                         close : function(){\r
245                                 this.playing && this.pause();\r
246                                 this.proxy.dispatch( X_Event.MEDIA_ENDED );\r
247                                 this.kill();\r
248                         },\r
249                         \r
250                         // SilverlightAudio.play\r
251                         play : function(){\r
252                                 var begin, end;\r
253                                 \r
254                                 // もし kill 後に autoplayTimer で呼ばれても、_closed==true なので平気\r
255                                 if( this.error ) return;\r
256                                 if( !this.duration ){\r
257                                         this.autoplay = true;\r
258                                         return;\r
259                                 };\r
260                                 \r
261                                 this._lastUserAction = 0 <= this.seekTime ? 'seek' : 'play';\r
262                                 \r
263                                 end   = X_AudioWrapper_getEndTime( this );\r
264                                 begin = X_AudioWrapper_getStartTime( this, end, true ) | 0;\r
265                                 \r
266                                 console.log( '[SLAudio] play ' + begin + ' -> ' + end );\r
267                                 \r
268                             this._rawObject.Volume = this.volume;\r
269                             this._beginTime = begin;\r
270                             this._currentTime( begin );\r
271                             \r
272                             if( !this.playing ){\r
273                                     this._rawObject.play();\r
274                             //this.proxy.dispatch( 'play' );\r
275                             \r
276                             this.playing = true;\r
277                             };\r
278                     \r
279                     this._timerID && X.Timer.remove( this._timerID );\r
280                     \r
281                 if( end < this.duration ){\r
282                                         this._timerID = X.Timer.once( end - begin, this, this._onEnded );\r
283                 } else {\r
284                         delete this._timerID;\r
285                 };\r
286                 \r
287                                 if( !this._interval ){\r
288                                         this._interval = X.Timer.add( 1000, 0, this, this._onInterval );\r
289                                 };\r
290                         },\r
291                                 \r
292                                 _onInterval : function(){\r
293                                         if( !this.playing ){\r
294                                                 delete this._interval;\r
295                                                 return X_Callback_UN_LISTEN;\r
296                                         };\r
297                                         this.proxy.dispatch( X_Event.MEDIA_PLAYING );\r
298                                 },\r
299                                 \r
300                                 _onEnded : function(){\r
301                                         var time, end;\r
302                                         delete this._timerID;\r
303                                         \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
307                                 \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
312                                         return;\r
313                                 };\r
314                                 \r
315                                 time -= X_AudioWrapper_getEndTime( this );\r
316                                 if( time < 0 ){\r
317                                         console.log( '> onEnd ' + time );\r
318                                         this._timerID = X.Timer.once( -time, this, this._onEnded );\r
319                                         return;\r
320                                 };\r
321                                 \r
322                                 if( this.loop ){\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
326                                                 this.play();\r
327                                         };\r
328                                 } else {\r
329                                         this.pause();\r
330                                         this.proxy.dispatch( X_Event.MEDIA_ENDED );\r
331                                 };\r
332                             };\r
333                                 },\r
334                         \r
335                         // SilverlightAudio.pause\r
336                         pause : function(){\r
337                                 if( this.error || !this.playing ) return;\r
338                                 \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
344                                                         \r
345                                 this._rawObject.pause();\r
346                                 //this.proxy.dispatch( 'pause' );\r
347                         },\r
348                         \r
349                         // SilverlightAudio.state\r
350                         state : function( obj ){ // @return Hash: { loop, error, paused, ended, source, duration }\r
351                                 var result, end;\r
352                                 \r
353                                 if( obj === undefined ){\r
354                                     return {\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
359                                         \r
360                                         // 整数化 しておかないと seek 時に不具合がある。\r
361                                         currentTime   : this.playing ? this._rawObject.Position.Seconds * 1000 | 0 : this.seekTime,\r
362 \r
363                                         loop          : this.loop,\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
369                                     };\r
370                                 };\r
371                         \r
372                                 result = X_AudioWrapper_updateStates( this, obj );\r
373                             \r
374                                 if( result & 2 ){ // seek\r
375                         this.play();\r
376                                 } else {\r
377                                         if( result & 1 ){\r
378                                                 end     = X_AudioWrapper_getEndTime( this );\r
379                                                 halfway = end < this.duration;\r
380                                                 this._timerID && X.Timer.remove( this._timerID );\r
381                                                 \r
382                                                 if( halfway ){\r
383                                                         this._timerID = X.Timer.once( end - this._rawObject.Position.Seconds * 1000 | 0, this, this._onEnded );\r
384                                                 } else {\r
385                                                         delete this._timerID;\r
386                                                 };\r
387 \r
388                                         };\r
389                                         if( result & 4 ){\r
390                                this._rawObject.Volume = this.volume;\r
391                                         };\r
392                                 };\r
393                         },\r
394                         \r
395                                 // SilverlightAudio.currentTime\r
396                                 _currentTime : function( time ){ // @param Number: time\r
397                                         var position = this._rawObject.Position; // [!] create instance\r
398         \r
399                                         position.Seconds = time / 1000 | 0; // set current time\r
400                                 \r
401                                         this._rawObject.Position = position; // [!] reattach instance\r
402                                 }\r
403 \r
404                 }\r
405         );\r
406 \r
407         function slerror(){\r
408                 alert( 'slerror' );\r
409         };\r
410 \r
411         X_Audio_BACKENDS.push( {\r
412                 backendName : 'Silverlight Audio',\r
413 \r
414                 detect : function( proxy, source, ext ){\r
415                         var ok = ext === 'mp3' || ext === 'wma' || ext === 'wav';\r
416                         proxy.asyncDispatch( { type : X_Event.COMPLETE, canPlay : ok } );                               \r
417                 },\r
418                 \r
419                 klass : X_Audio_SLAudioWrapper\r
420                 \r
421         } );\r
422 \r
423 };