OSDN Git Service

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