OSDN Git Service

827bed869e719a2c36b13e60349361946c6216ce
[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                         \r
141                         _closed         : true,\r
142                         _lastUserAction : '',\r
143                         \r
144                         _loop           : false,\r
145                         _startTime      : 0,\r
146                         _volume         : 0.5,\r
147                         _autoplay       : false,\r
148                         \r
149                         Constructor : function( proxy, source, option ){\r
150                                 this.proxy   = proxy;\r
151                                 this._closed = false;\r
152                                 \r
153                                 if( option.loop )      this._loop      = true;\r
154                                 if( option.startTime ) this._startTime = option.startTime;\r
155                                 if( option.volume )    this._volume    = option.volume;\r
156                                 \r
157                                 this._rawObject = X_Audio_rawAudio || new Audio( source );//X.Dom.Node.create( 'audio', { src : source } ).appendToRoot();//( X.Dom.Node._systemNode );\r
158                                 \r
159                                 this.listen( [\r
160                                                 'loadstart', 'load', 'progress', 'suspend', 'abort', 'error', 'emptied', 'stalled', 'play', 'pause', 'loadedmetadata',\r
161                                                 'loadeddata', 'waiting', 'playing', 'canplay', 'canplaythrough', 'seeking', 'seeked', 'timeupdate', 'ended',\r
162                                                 'ratechange', 'durationchange', 'volumechange' ], this.handleEventProxy );                              \r
163                                 \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._rawObject );\r
171 \r
172                                 this._rawObject.volume   = this._volume;\r
173                                 this._rawObject.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                                                 break;\r
184                                 };\r
185                         },\r
186                         /*\r
187                          * http://uguisu.skr.jp/html/table3.html\r
188                          */\r
189                         handleEventProxy : function( e ){\r
190                                 switch( e.type ){\r
191                                         case 'loadstart' :      //      ブラウザがコンテンツの検索を開始した場合に発生\r
192                                                 break;\r
193                                         case 'progress' :       //      ブラウザがコンテンツの取得を実行した場合に発生\r
194                                                 console.log( e.loaded / e.total );\r
195                                                 break;\r
196                                         case 'suspend' :        //      ブラウザが意図的にコンテンツの取得を現在行っていない場合に発生(ダウンロードは未完了)\r
197                                         case 'abort' :          //      ダウンロードの完了前にコンテンツの取得を停止した場合に発生(この停止はエラーによるものではない)\r
198                                         case 'error' :          //      コンテンツの取得実行中にエラーが発生した場合に発生\r
199                                         case 'emptied' :        //      読み込み中に致命的なエラーが発生したか、実行状態ででload()メソッドが実行された場合に発生\r
200                                         case 'stalled' :        //      ブラウザがコンテンツの取得を試みたが、データがまだ用意されていない場合に発生\r
201                                         case 'play' :           //      再生が開始された。play()メソッドからの復帰後に発生する場合に発生\r
202                                         case 'pause' :          //      再生が一時停止された。pauseメソッドからの復帰後に発生する場合に発生\r
203                                         case 'loadedmetadata' : //      ブラウザがメディアリソースの長さと寸法を判定した場合に発生\r
204                                         case 'loadeddata' :     //      コンテンツの表示を現在の再生位置で初めて行えるようになった場合に発生\r
205                                         case 'waiting' :        //      次のフレームが利用不可のため再生を停止したが、そのフレームがやがて利用可能になると想定している場合に発生\r
206                                         case 'playing' :        //      再生が開始された場合に発生\r
207                                         case 'canplay' :        //      今すぐに再生を再開できるが、バッファリングが不十分でコンテンツを最後まで表示できないと予測している場合に発生\r
208                                         case 'canplaythrough' : //      今すぐに再生を開始してもバッファリングで停止することなく最後まで表示できると予測している場合に発生\r
209                                         case 'seeking' :        //      シークがtrueに変化し、イベントを発生させるのに十分な時間がシーク操作にかかっている場合に発生\r
210                                         case 'seeked' :         //      シークがfalseに変化した場合に発生\r
211                                         case 'timeupdate' :     //      通常の再生が行われ現在の再生位置の変化が起こった場合に発生\r
212                                                 break;\r
213                                         \r
214                                         case 'ended' :\r
215                                                 //!this._closed && this._lastUserAction !== 'stop' && this._loop && this.play();\r
216                                                 break;\r
217         \r
218                                         case 'ratechange' :     // defaultPlaybackRate属性とplaybackRate属性のどちらかが更新された場合に発生\r
219                                         case 'durationchange' : // duration属性が更新された場合に発生\r
220                                         case 'volumechange' :   // volume属性とmuted属性のどちらかが変化した場合に発生\r
221                                 };\r
222                                 \r
223                                 //console.log( 'html5 ' + e.type + ' ' + ( this.proxy._listeners && this.proxy._listeners[ e.type ] && this.proxy._listeners[ e.type ].length ) );\r
224                                 //e.type === 'canplaythrough' && console.dir( e );\r
225                                 this.proxy.dispatch( e );\r
226                         },\r
227                         \r
228                         close : function(){\r
229                                 // pool, proxy を外す\r
230                                 //this.kill();\r
231                         },\r
232                         \r
233                         play : function( position ){\r
234                                 // もし kill 後に autoplayTimer で呼ばれても、_closed==true なので平気\r
235                             if( this._closed ) return;\r
236                             this._lastUserAction = 'play';\r
237                         \r
238                             if( X.UA.Chrome ){ // [CHROME][FIX] volume TODO どの version で 修正される?\r
239                                 // [!] delay\r
240                                 X.Timer.once( 0, this, this._fixForChrome, [ this._rawObject.volume ] );\r
241                                 this._rawObject.volume = 0;\r
242                             };\r
243                             \r
244                             if( !this._rawObject.paused ){\r
245                                 this.currentTime( this._startTime );\r
246                             };\r
247                             this._rawObject.play();\r
248                         },\r
249                         \r
250                         // [CHROME][FIX] volume\r
251                         _fixForChrome : X.UA.Chrome && function( volume ){\r
252                                 !this._closed && ( this._rawObject.volume = volume );\r
253                         },\r
254                         \r
255                         pause : function(){\r
256                             if( !this._closed && !this._rawObject.error ){\r
257                                 this._lastUserAction = 'pause';\r
258                                 this._rawObject.pause();        \r
259                             };\r
260                         },\r
261                         \r
262                         stop : function(){\r
263                             if( !this._closed && !this._rawObject.error ){\r
264                                 this._lastUserAction = 'stop';\r
265                                 this._rawObject.pause();\r
266                                 this.currentTime( this._startTime );\r
267                             };\r
268                         },\r
269                         \r
270                         loop : function( v ){\r
271                             if( v === undefined ) return this._loop;\r
272                             this._rawObject.loop = this._loop = v;\r
273                         },\r
274         \r
275                         state : function(){\r
276                             var paused = !!this._rawObject.paused,\r
277                                 ended  = !!this._rawObject.ended;\r
278                         \r
279                             if( this._lastUserAction === 'stop' ){\r
280                                 if( paused ){\r
281                                     paused = false;\r
282                                     ended  = true;\r
283                                 };\r
284                             };\r
285                         \r
286                             return {\r
287                                 loop     : this._rawObject.loop,\r
288                                 error    : this._rawObject.error    || 0,   // 0, 1 ~ 4\r
289                                 paused   : paused,\r
290                                 ended    : ended,\r
291                                 source   : this._rawObject.src      || '',\r
292                                 duration : this._rawObject.duration || 0\r
293                             };\r
294                         },\r
295         \r
296                         volume : function( v ){\r
297                             if( v === undefined ) return this.audio.volume;\r
298                             this._rawObject.volume = v;\r
299                         },\r
300         \r
301                         startTime : function( time ){\r
302                             if( time === undefined ) return this._startTime;\r
303                             this._startTime = time;\r
304                         },\r
305         \r
306                         currentTime : function( time ){\r
307                             if( time === undefined ) return this._rawObject.currentTime;\r
308                             this._rawObject.currentTime = time;\r
309                         },\r
310         \r
311                         isPlaying : function(){\r
312                             return !this._rawObject.error && !this._rawObject.paused && !this._rawObject.ended;\r
313                         }\r
314         \r
315                 }\r
316         );\r
317         \r
318 };\r
319 \r
320 \r
321 \r
322 \r
323 \r
324 \r
325 \r
326 \r