OSDN Git Service

Version 0.6.96. add lock function @X.EventDispatcher.
[pettanr/clientJs.git] / 0.6.x / js / 06_audio / 01_XHTML5Audio.js
1 \r
2 /*\r
3  * original : uupaa-js HTML5Audio.js\r
4  * https://code.google.com/p/uupaa-js/source/browse/trunk/0.8/src/Audio/HTML5Audio.js?r=568\r
5  */\r
6 \r
7 var X_Audio_HTML5Audio, X_Audio_HTML5AudioWrapper, X_Audio_rawAudio,\r
8         X_Audio_HTML5Audio_LIVE_LIST = [],\r
9         X_Audio_HTML5Audio_POOL_LIST = [];\r
10         \r
11 \r
12 if( window.HTMLAudioElement ){\r
13         function getHTML5AudioWrapper( proxy ){\r
14                 var i = X_Audio_HTML5Audio_LIVE_LIST.length;\r
15                 for( ; i; ){\r
16                         if( X_Audio_HTML5Audio_LIVE_LIST[ --i ].proxy === proxy ) return X_Audio_HTML5Audio_LIVE_LIST[ i ];\r
17                 };\r
18         };\r
19         \r
20         X_Audio_HTML5Audio = X.Class._override(\r
21                 new X.EventDispatcher(),\r
22                 {\r
23                         backendName : 'HTML5 Audio',\r
24                 /*\r
25                  * HTML5 の audio 要素と video 要素でサポートされているメディアフォーマット\r
26                  * https://developer.mozilla.org/ja/docs/Web/HTML/Supported_media_formats\r
27                  * \r
28                  * 主要ブラウザのHTML5 audioタグで使えるファイル形式の再生対応状況を調べてみた\r
29                  * http://sothis.blog.so-net.ne.jp/2010-10-27\r
30                  * ダメ元で仕様に含まれていない SHOUTcast もテストしてみました。\r
31                  * \r
32                  * IE9 の HTML5 Audio について\r
33                  * http://kentablog.cluscore.com/2011/05/ie9-html5-audio.html\r
34                  * 1.Audioオブジェクトを作ることができないので、Audioタグを使う\r
35                  * 2.クロスドメインアクセスには、「clientaccesspolicy.xml」か「crossdomain.xml」が必要\r
36                  * 3.wav が不可\r
37                  * \r
38                  * IE9でHTML5 autio タグが無効になる\r
39                  * http://bbs.wankuma.com/index.cgi?mode=al2&namber=64886&KLOG=109\r
40                  *  IEのバージョン9.0.8112.16421では、Audioオブジェクトのnewも対応してました。\r
41                  *  createElement等で動的生成すると、よろしくない\r
42                  * \r
43                  * media-can-play-wav-audio.html\r
44                  * https://github.com/adobe/webkit/blob/master/LayoutTests/media/media-can-play-wav-audio.html\r
45                  * testExpected("audio.canPlayType('audio/wav; codecs=1')", "probably");\r
46                  * \r
47                  * HTML5 audioタグ ブラウザ間の違い\r
48                  * http://wiki.bit-hive.com/tomizoo/pg/HTML5%20audio%A5%BF%A5%B0%20%A5%D6%A5%E9%A5%A6%A5%B6%B4%D6%A4%CE%B0%E3%A4%A4\r
49                  *  - volume, muted iPhone(iOS4-6)、Android(2.3.6)では動作せず。\r
50                  *  - FireFox3.6, Android 2.3.6については、src変更後、load()を呼び出さないと切り替わらなかった。iPhoneはload()が不要。\r
51                  */     \r
52                         detect : function( source, ext ){\r
53                                 var ok, mineType = 'audio/' + ext;\r
54                                 switch( ext ){\r
55                                         case 'mp3' :\r
56                                                 ok = X.UA.IE || X.UA.Chrome || X.UA.Safari; //( X.UA.OS === 'windows' && X.UA.Safari );\r
57                                                 mineType = 'audio/mpeg';\r
58                                                 break;\r
59                                         case 'ogg' :\r
60                                                 ok = 15 <= X.UA.Gecko || X.UA.Chrome || X.UA.Opera;\r
61                                                 break;\r
62                                         case 'm4a' :\r
63                                                 ok = X.UA.IE || X.UA.WebKit;\r
64                                                 mineType = 'audio/mp4';\r
65                                                 break;\r
66                                         case 'webm' :\r
67                                                 ok = 2 <= X.UA.Gecko || 10.6 <= X.UA.Opera; // firefox4+(Gecko2+)\r
68                                                 break;\r
69                                         case 'wav' :\r
70                                                 ok = X.UA.Gecko || X.UA.Opera || X.UA.Safari; //( X.UA.OS === 'windows' && X.UA.Safari );\r
71                                                 //mineType = 'audio/wav'; // audio/x-wav ?\r
72                                                 break;\r
73                                         default :\r
74                                                 mineType = '';\r
75                                 };\r
76                                 \r
77                                 if( !ok && mineType ){\r
78                                         if( !X_Audio_rawAudio ) X_Audio_rawAudio = new Audio;\r
79                                         ok = X_Audio_rawAudio.canPlayType( mineType );\r
80                                 };\r
81                                 \r
82                                 this.asyncDispatch( ok ? 'support' : 'nosupport' );\r
83                                 \r
84                                 return this;\r
85                         },\r
86                         \r
87                         register : function( proxy, source, option ){\r
88                                 X_Audio_HTML5Audio_LIVE_LIST.push( new X_Audio_HTML5AudioWrapper( proxy, source, option ) );\r
89                         },\r
90                         \r
91                         close : function(){\r
92                                 return getHTML5AudioWrapper( this ).close();\r
93                         },\r
94                         \r
95                         play : function( position ){\r
96                                 return getHTML5AudioWrapper( this ).play( position );\r
97                         },\r
98                         \r
99                         pause : function(){\r
100                                 return getHTML5AudioWrapper( this ).pause();\r
101                         },\r
102                         \r
103                         stop : function(){\r
104                                 return getHTML5AudioWrapper( this ).stop();\r
105                         },\r
106                         \r
107                         loop : function( v ){\r
108                                 return getHTML5AudioWrapper( this ).loop( v );\r
109                         },\r
110         \r
111                         state : function(){\r
112                                 return getHTML5AudioWrapper( this ).state();\r
113                         },\r
114         \r
115                         volume : function( v ){\r
116                                 return getHTML5AudioWrapper( this ).volume( v );\r
117                         },\r
118         \r
119                         startTime : function( time ){\r
120                                 return getHTML5AudioWrapper( this ).startTime( time );\r
121                         },\r
122         \r
123                         currentTime : function( time ){\r
124                                 return getHTML5AudioWrapper( this ).currentTime( time );\r
125                         },\r
126         \r
127                         isPlaying : function(){\r
128                                 return getHTML5AudioWrapper( this ).isPlaying();\r
129                         }\r
130                 }\r
131         );\r
132         \r
133         X_Audio_BACKENDS.push( X_Audio_HTML5Audio );\r
134         \r
135         X_Audio_HTML5AudioWrapper = X.EventDispatcher.inherits(\r
136                 'X.AV.HTML5AudioWrapper',\r
137                 X.Class.POOL_OBJECT,\r
138                 {\r
139                         \r
140                         proxy           : null,\r
141                         \r
142                         _closed         : true,\r
143                         _lastUserAction : '',\r
144                         \r
145                         _loop           : false,\r
146                         _startTime      : 0,\r
147                         _volume         : 0.5,\r
148                         _autoplay       : false,\r
149                         \r
150                         Constructor : function( proxy, source, option ){\r
151                                 this.proxy   = proxy;\r
152                                 this._closed = false;\r
153                                 \r
154                                 if( option.loop )      this._loop      = true;\r
155                                 if( option.startTime ) this._startTime = option.startTime;\r
156                                 if( option.volume )    this._volume    = option.volume;\r
157                                 \r
158                                 this._rawObject = X_Audio_rawAudio || new Audio( source );//X.Dom.Node.create( 'audio', { src : source } ).appendToRoot();//( X.Dom.Node._systemNode );\r
159                                 \r
160                                 this.listen( [\r
161                                                 'loadstart', 'load', 'progress', 'suspend', 'abort', 'error', 'emptied', 'stalled', 'play', 'pause', 'loadedmetadata',\r
162                                                 'loadeddata', 'waiting', 'playing', 'canplay', 'canplaythrough', 'seeking', 'seeked', 'timeupdate', 'ended',\r
163                                                 'ratechange', 'durationchange', 'volumechange' ], this.handleEventProxy );                              \r
164                                 \r
165                                 if( X_Audio_rawAudio ){\r
166                                         X_Audio_rawAudio.src = source;\r
167                                         X_Audio_rawAudio.load(); // 要る?\r
168                                         X_Audio_rawAudio = null;\r
169                                 };\r
170 \r
171                                 //document.body.appendChild( this._rawObject );\r
172 \r
173                                 this._rawObject.volume   = this._volume;\r
174                                 this._rawObject.autoplay = false;\r
175                                 option.autoplay && X.Timer.once( 100, this, this.play );\r
176                                 \r
177                                 this.listenOnce( X.Event.KILL_INSTANCE );\r
178                         },\r
179                         \r
180                         handleEvent : function( e ){\r
181                                 switch( e.type ){\r
182 \r
183                                         case X.Event.KILL_INSTANCE :\r
184                                                 break;\r
185                                 };\r
186                         },\r
187                         /*\r
188                          * http://uguisu.skr.jp/html/table3.html\r
189                          */\r
190                         handleEventProxy : function( e ){\r
191                                 switch( e.type ){\r
192                                         case 'loadstart' :      //      ブラウザがコンテンツの検索を開始した場合に発生\r
193                                                 break;\r
194                                         case 'progress' :       //      ブラウザがコンテンツの取得を実行した場合に発生\r
195                                                 console.log( e.loaded / e.total );\r
196                                                 break;\r
197                                         case 'suspend' :        //      ブラウザが意図的にコンテンツの取得を現在行っていない場合に発生(ダウンロードは未完了)\r
198                                         case 'abort' :          //      ダウンロードの完了前にコンテンツの取得を停止した場合に発生(この停止はエラーによるものではない)\r
199                                         case 'error' :          //      コンテンツの取得実行中にエラーが発生した場合に発生\r
200                                         case 'emptied' :        //      読み込み中に致命的なエラーが発生したか、実行状態ででload()メソッドが実行された場合に発生\r
201                                         case 'stalled' :        //      ブラウザがコンテンツの取得を試みたが、データがまだ用意されていない場合に発生\r
202                                         case 'play' :           //      再生が開始された。play()メソッドからの復帰後に発生する場合に発生\r
203                                         case 'pause' :          //      再生が一時停止された。pauseメソッドからの復帰後に発生する場合に発生\r
204                                         case 'loadedmetadata' : //      ブラウザがメディアリソースの長さと寸法を判定した場合に発生\r
205                                         case 'loadeddata' :     //      コンテンツの表示を現在の再生位置で初めて行えるようになった場合に発生\r
206                                         case 'waiting' :        //      次のフレームが利用不可のため再生を停止したが、そのフレームがやがて利用可能になると想定している場合に発生\r
207                                         case 'playing' :        //      再生が開始された場合に発生\r
208                                         case 'canplay' :        //      今すぐに再生を再開できるが、バッファリングが不十分でコンテンツを最後まで表示できないと予測している場合に発生\r
209                                         case 'canplaythrough' : //      今すぐに再生を開始してもバッファリングで停止することなく最後まで表示できると予測している場合に発生\r
210                                         case 'seeking' :        //      シークがtrueに変化し、イベントを発生させるのに十分な時間がシーク操作にかかっている場合に発生\r
211                                         case 'seeked' :         //      シークがfalseに変化した場合に発生\r
212                                         case 'timeupdate' :     //      通常の再生が行われ現在の再生位置の変化が起こった場合に発生\r
213                                                 break;\r
214                                         \r
215                                         case 'ended' :\r
216                                                 //!this._closed && this._lastUserAction !== 'stop' && this._loop && this.play();\r
217                                                 break;\r
218         \r
219                                         case 'ratechange' :     // defaultPlaybackRate属性とplaybackRate属性のどちらかが更新された場合に発生\r
220                                         case 'durationchange' : // duration属性が更新された場合に発生\r
221                                         case 'volumechange' :   // volume属性とmuted属性のどちらかが変化した場合に発生\r
222                                 };\r
223                                 \r
224                                 //console.log( 'html5 ' + e.type + ' ' + ( this.proxy._listeners && this.proxy._listeners[ e.type ] && this.proxy._listeners[ e.type ].length ) );\r
225                                 //e.type === 'canplaythrough' && console.dir( e );\r
226                                 this.proxy.dispatch( e );\r
227                         },\r
228                         \r
229                         close : function(){\r
230                                 // pool, proxy を外す\r
231                                 //this.kill();\r
232                         },\r
233                         \r
234                         play : function( position ){\r
235                                 // もし kill 後に autoplayTimer で呼ばれても、_closed==true なので平気\r
236                             if( this._closed ) return;\r
237                             this._lastUserAction = 'play';\r
238                         \r
239                             if( X.UA.Chrome ){ // [CHROME][FIX] volume TODO どの version で 修正される?\r
240                                 // [!] delay\r
241                                 X.Timer.once( 0, this, this._fixForChrome, [ this._rawObject.volume ] );\r
242                                 this._rawObject.volume = 0;\r
243                             };\r
244                             \r
245                             if( !this._rawObject.paused ){\r
246                                 this.currentTime( this._startTime );\r
247                             };\r
248                             this._rawObject.play();\r
249                         },\r
250                         \r
251                         // [CHROME][FIX] volume\r
252                         _fixForChrome : X.UA.Chrome && function( volume ){\r
253                                 !this._closed && ( this._rawObject.volume = volume );\r
254                         },\r
255                         \r
256                         pause : function(){\r
257                             if( !this._closed && !this._rawObject.error ){\r
258                                 this._lastUserAction = 'pause';\r
259                                 this._rawObject.pause();        \r
260                             };\r
261                         },\r
262                         \r
263                         stop : function(){\r
264                             if( !this._closed && !this._rawObject.error ){\r
265                                 this._lastUserAction = 'stop';\r
266                                 this._rawObject.pause();\r
267                                 this.currentTime( this._startTime );\r
268                             };\r
269                         },\r
270                         \r
271                         loop : function( v ){\r
272                             if( v === undefined ) return this._loop;\r
273                             this._rawObject.loop = this._loop = v;\r
274                         },\r
275         \r
276                         state : function(){\r
277                             var paused = !!this._rawObject.paused,\r
278                                 ended  = !!this._rawObject.ended;\r
279                         \r
280                             if( this._lastUserAction === 'stop' ){\r
281                                 if( paused ){\r
282                                     paused = false;\r
283                                     ended  = true;\r
284                                 };\r
285                             };\r
286                         \r
287                             return {\r
288                                 loop     : this._rawObject.loop,\r
289                                 error    : this._rawObject.error    || 0,   // 0, 1 ~ 4\r
290                                 paused   : paused,\r
291                                 ended    : ended,\r
292                                 source   : this._rawObject.src      || '',\r
293                                 duration : this._rawObject.duration || 0\r
294                             };\r
295                         },\r
296         \r
297                         volume : function( v ){\r
298                             if( v === undefined ) return this.audio.volume;\r
299                             this._rawObject.volume = v;\r
300                         },\r
301         \r
302                         startTime : function( time ){\r
303                             if( time === undefined ) return this._startTime;\r
304                             this._startTime = time;\r
305                         },\r
306         \r
307                         currentTime : function( time ){\r
308                             if( time === undefined ) return this._rawObject.currentTime;\r
309                             this._rawObject.currentTime = time;\r
310                         },\r
311         \r
312                         isPlaying : function(){\r
313                             return !this._rawObject.error && !this._rawObject.paused && !this._rawObject.ended;\r
314                         }\r
315         \r
316                 }\r
317         );\r
318         \r
319 };\r
320 \r
321 \r
322 \r
323 \r
324 \r