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
6 * Windows 版 Safari は QuickTime のインストールが必要
\r
8 * 1. iOS4(iPod 2G) で ended に達すると音が鳴らなくなる fix で解決
\r
9 * 2. iOS6(iPod 4G) で ended に達すると音が鳴らなくなる fix で頻度が改善 emded イベントは発しないので、timeupdate 時に currentTime で判断する
\r
10 * 3. WP7(IS12T) で最後の方にある音が鳴らない? mp3 cbr を使えばいい? 裏に回っても音が鳴り続ける
\r
11 * 4. AOSP 2.x で ended に達すると音が鳴らなくなる -> リロード(audio.src='';audio.src=src;audio.load())でで解決
\r
12 * 5. AOSP 3.x で ended に達すると音が鳴らなくなる -> リロード(audio.src='';audio.sr=src)で解決、但し 2.x 4.x より遅延が大きく 1 秒弱程度ある
\r
13 * 6. AOSP 4.4.2- は ended に達した際に currentTime が変更できなくなり、リロードが必要になる, 4.0, 4.1, 4.2, 4.3 で確認, play() で頻度低下
\r
14 * 7. Android 4.4.4 Chrome WebView は ended に達した際に play() が必要
\r
15 * 8. BlinkOpera32 Win8 は HTMLAudio が壊れている、WebAudio は mp3 がデコードに失敗、ogg が動作
\r
18 * 1. AOSP4.1 iframe 内の Audio は親に focus が移っても再生を継続する
\r
19 * 2. AOSP oggはシークが乱れる m4a, mp3 は優秀
\r
21 * http://unolabo.boo.jp/archives/2011/06/13-iphone%E3%81%AEaudio%E5%91%A8%E3%82%8A%E3%81%AE%E3%83%A1%E3%83%A2.html
\r
22 * 【JS】iPhoneのAudio周りのメモ iOS4.0 と 4.2 の違い
\r
26 * duration が取得できるタイミングが遅くそれまでは infinity(PC Opera12), NaN(WP9), 0(Android 標準ブラウザ ChromeWebView) が入っている
\r
28 * 1. touch が不要の場合、自動で再生を開始して duration を取得するまで再生する
\r
29 * -> 取得後に pause or 通常再生
\r
30 * 2. touch が必要な場合、タッチイベント内の audio.play() で duration 取得
\r
33 * 1. loadeddata 等では duration が infinity で、再生後の durationchange 時に duration が判明する
\r
34 * 2. duration 判明後には currentTime によるシークと、現在時間の取得が可能になる。
\r
35 * 3. Opera12.17 Win32(XP) portable apps は勝手に再生が始まる、、、Win8+Opera では発生しない
\r
36 * -> その際には timeupdate が発行されない、、、 iframe+image+audio で使わないときは破棄する、とか。
\r
37 * -> opera11、10.54 WinXP はまとも、、、 portable が怪しい??
\r
43 // ended が発生しない timeupdate 内で play() を呼ぶ (未検証) 不具合確認は iOS4,6
\r
44 X_HTMLAudio_endedFixIOS = X_UA[ 'iOS' ] < 7,
\r
45 // Android 2.3.5 で ended 時に audio.src='';audio.src=src;audio.load() を実施。 2.3.4 でも問題なし。
\r
46 X_HTMLAudio_endedFixAOSP2 = X_UA[ 'AOSP' ] < 3,
\r
47 // Android 3.1 で ended 時に src='';src=src を実施。
\r
48 X_HTMLAudio_endedFixAOSP3 = !X_HTMLAudio_endedFixAOSP2 && X_UA[ 'AOSP' ] < 4,
\r
49 // ended 時に play() を実施, currentTime が duration に張り付き更新されなければ src='';src=src を実施。
\r
50 X_HTMLAudio_endedFixAOSP4 = 4 <= X_UA[ 'AOSP' ],
\r
51 // ended 時に play() を実施
\r
52 X_HTMLAudio_endedFixCWV = X_UA[ 'ChromeWV' ] || ( X_UA[ 'Blink' ] && X_UA[ 'Android' ] ),
\r
54 // Opera Mobile 12 は 2回目以降の currentTime へのセットで currentTime が更新されなくなるため、タイマーを使用する
\r
55 X_HTMLAudio_currentTimeFix = !!X_UA[ 'OperaMobile' ] || !!X_UA[ 'OperaTablet' ],
\r
57 X_HTMLAudio_volumeFix = X_UA[ 'Chrome' ],
\r
59 * win opera12 volume, mute の変更が2度目以降できない
\r
61 X_HTMLAudio_volumeEnabled = !( X_UA[ 'WinPhone' ] && X_UA[ 'IE9' ] ) && !X_UA[ 'Opera' ],
\r
62 // Gecko PC + Android でseek時に再生がしばしば止まる問題の修正
\r
63 X_HTMLAudio_needPlayForSeek = X_UA[ 'Gecko' ],
\r
65 X_HTMLAudio_pauseFix = 12 <= X_UA[ 'Opera' ] && 0 < ' XP XPSP2 2003|XP64'.indexOf( X_UA[ 'Windows' ] ), // XP + Opera12 のみ?
\r
67 X_HTMLAudio_need1stTouch = X_UA[ 'iOS' ] || 4.2 <= X_UA[ 'AOSP' ] || X_UA[ 'ChromeWV' ] || X_UA[ 'WinPhone' ] || ( X_UA[ 'Blink' ] && X_UA[ 'Android' ] ),
\r
69 X_HTMLAudio_playTrigger = ( X_UA[ 'WinPhone' ] && X_UA[ 'IE9' ] ) ? 'canplay' : X_UA[ 'iOS' ] ? 'suspend' : X_UA[ 'Blink' ] ? 'stalled' : 'canplaythrough',
\r
71 X_HTMLAudio_durationFix = X_UA[ 'iOS' ] || X_UA[ 'ChromeWV' ] || ( X_UA[ 'WinPhone' ] && X_UA[ 'IE9' ] ) || ( X_UA[ 'Windows' ] && 12 <= X_UA[ 'Opera' ] ),
\r
73 X_HTMLAudio_shortPlayFix = X_UA[ 'AOSP' ]; // Android 4.1.1 でも遭遇
\r
75 if( X_Audio_constructor ){
\r
77 X_HTMLAudio = X_AudioBase[ 'inherits' ](
\r
79 X_Class.POOL_OBJECT,
\r
83 // 1: canplaythrought|timeupdateに達している、またはdurationFixが終了している
\r
90 // 2:touch による play 済
\r
91 _touchState : X_HTMLAudio_need1stTouch ? 1 : 0,
\r
93 _currentFixStart : 0,
\r
94 _currentFixBegin : 0,
\r
96 // 0:durationFix不要 または 完了
\r
97 // 1:durationFix未着手(touchState=1なら play() に入れる)
\r
98 // 2:canplay イベント発生 -> play()
\r
101 // :timeupdate イベントで durationFixは完了
\r
102 _durationFixPhase : X_HTMLAudio_durationFix ? 1 : 0,
\r
103 _lastCurrentTime : 0,
\r
105 _shortPlayFixON : false,
\r
106 _shortPlayFixTime : 0,
\r
108 _endedFixON : false,
\r
110 'Constructor' : function( disatcher, source, option ){
\r
113 this.disatcher = disatcher || this;
\r
114 this._closed = false;
\r
115 this._src = source;
\r
117 if( X_HTMLAudio_shortPlayFix ){
\r
118 this._shortPlayFixON = X_URL_getEXT( source ) === 'm4a';
\r
121 this.setState( option );
\r
123 if( option[ 'useVideo' ] ){
\r
124 raw = document.createElement( 'video' );
\r
125 raw.preload = 'none'; // auto, metadata, none
\r
126 raw.autoplay = false, // no-auto
\r
129 raw.crossorigin = option[ 'crossorigin' ] || ''; //crossorigin: "anonymous", X.URL.isSameDomain() で切り替え
\r
130 raw.style.cssText = 'position:absolute;bottom:0;left:-50px;width:100px;height:100px;opacity:0;';
\r
131 raw.controls = false;
\r
132 raw.WebKitPlaysInline = true;
\r
133 X_elmBody.appendChild( raw );
\r
135 raw = X_TEMP.rawAudio || new X_Audio_constructor( '' );
\r
136 // raw.loop = false; // loop を使えば ended で止まること回避できるかも 但し ended イベントが起きなくなる
\r
138 if( X_TEMP.rawAudio ) delete X_TEMP.rawAudio;
\r
141 this[ '_rawObject' ] = raw;
\r
143 this[ 'listen' ]( [
\r
144 X_EVENT_KILL_INSTANCE,
\r
145 X_HTMLAudio_playTrigger,
\r
146 //'loadstart', 'load',
\r
147 'progress', //'error',
\r
148 // 'suspend', 'abort', 'emptied', 'stalled',
\r
149 // 'play', 'pause', 'seeked', 'ratechange', 'volumechange',
\r
150 'loadedmetadata', 'loadeddata', 'canplay', 'canplaythrough',
\r
151 'playing', 'waiting', 'seeking',
\r
152 'durationchange', 'timeupdate', 'ended' ] );
\r
154 this[ 'listen' ]( [
\r
155 'loadstart', 'load',
\r
156 'progress', 'error',
\r
157 'suspend', 'abort', 'emptied', 'stalled',
\r
158 'play', 'pause', 'seeked', 'ratechange', 'volumechange',
\r
159 'loadedmetadata', 'loadeddata', 'canplay', 'canplaythrough',
\r
160 'playing', 'waiting', 'seeking',
\r
161 'durationchange', 'timeupdate', 'ended' ], this.onDebug );
\r
163 if( X_HTMLAudio_need1stTouch ){
\r
166 // if( this.autoplay ){
\r
167 raw.preload = 'auto';
\r
168 raw.autoplay = true; // Android 4.0-4.1.x で必要
\r
170 //raw.autobuffer = true;
\r
172 raw.load(); // Android4.1.1 HTL21 では必要!
\r
176 onDebug : function( e ){
\r
177 this.disatcher[ 'dispatch' ]( {
\r
178 type : X_EVENT_DEBUG,
\r
179 'rawEvent' : e.type,
\r
180 current : this[ '_rawObject' ].currentTime,
\r
181 duration : this[ '_rawObject' ].duration } );
\r
184 handleEvent : function( e ){
\r
185 if( !e || !e.type ) alert( 888 );
\r
187 var raw = this[ '_rawObject' ],
\r
188 actualEnded = e.type === 'ended',
\r
189 ended = actualEnded,
\r
191 eventType, duration, end, now;
\r
193 if( this._closed ) return;
\r
196 //window[ '__rawAudio' ] = this[ '_rawObject' ];
\r
198 e.type !== 'timeupdate' && console.log( ' > ' + e.type );
\r
202 case X_EVENT_KILL_INSTANCE :
\r
203 delete this._closed;
\r
204 this.playing && this.actualPause();
\r
206 // 【javascript】モバイル向けブラウザでも音を鳴らしたい【WebAudio】
\r
207 // http://ingaouhou.com/archives/3633
\r
208 // ・使い終わったインスタンスはload()しておくとやや安定
\r
212 // removeChild for video
\r
215 //case 'loadstart' : // ブラウザがコンテンツの検索を開始した場合に発生
\r
217 case 'progress' : // ブラウザがコンテンツの取得を実行した場合に発生
\r
218 // console.log( e.loaded + ' ' + e.total * 100 + '%' );
\r
219 // iem9 で常に0 raw.networkState;
\r
220 // opera Android 12 で buffered.end() へのアクセスはエラー try catch も無効、iem9 は常に end(0) = 0
\r
221 //console.log( 'buffered.end ' + raw.buffered && raw.buffered.end(0) );
\r
224 case 'loadeddata' : // コンテンツの表示を現在の再生位置で初めて行えるようになった場合に発生
\r
225 case 'canplaythrough' : // 今すぐに再生を開始してもバッファリングで停止することなく最後まで表示できると予測している場合に発生
\r
226 if( !this._endedFixON && !X_HTMLAudio_durationFix && !X_HTMLAudio_need1stTouch ){
\r
227 this._readyState |= 1;
\r
229 case 'canplay' : // 今すぐに再生を再開できるが、バッファリングが不十分でコンテンツを最後まで表示できないと予測している場合に発生
\r
230 if( this._durationFixPhase === 1 && !X_HTMLAudio_need1stTouch ){
\r
231 this._durationFixPhase = 2;
\r
233 raw.currentTime = 0; // Win8 + Opera12 で必要
\r
235 if( this._endedFixON ){
\r
236 console.log( '▽ onEndedFix の終了 @' + e.type );
\r
237 this._endedFixON = false;
\r
240 case 'loadedmetadata' : // ブラウザがメディアリソースの長さと寸法を判定した場合に発生
\r
241 case 'durationchange' : // duration属性が更新された場合に発生
\r
242 if( !this.duration || this.duration !== raw.duration * 1000 ){ // Blink28 duration が変わる
\r
243 duration = raw.duration;
\r
247 // TODO firefox で 短い音声でtimeupdate, ended が発火しない <- 最後の音に無音部分を追加する
\r
248 case 'timeupdate' : // 通常の再生が行われ現在の再生位置の変化が起こった場合に発生
\r
249 if( this._durationFixPhase === 8 ){
\r
250 this._durationFixPhase = 0;
\r
251 this._readyState |= 1;
\r
253 if( this._durationFixPhase === 4 ){ // 1 or 2
\r
254 duration = raw.duration;
\r
255 eventType = X_EVENT_MEDIA_WAITING;
\r
257 if( !X_HTMLAudio_durationFix && X_HTMLAudio_need1stTouch && this._touchState === 3 ){
\r
258 this._touchState = 0;
\r
259 this._readyState |= 1;
\r
261 if( ( now = this.getActualCurrentTime() ) === this._lastCurrentTime ){
\r
262 eventType = X_EVENT_MEDIA_WAITING;
\r
264 if( this.playing ){
\r
265 end = X_Audio_getEndTime( this ) + this._shortPlayFixTime;
\r
266 //console.log( now + ' / ' + end );
\r
267 // || now < this._lastCurrentTime // loop した場合
\r
268 if( 0 + end <= 0 + now ){ // 0+ なぜか iem9 で必要,,,
\r
269 if( this.autoLoop ){
\r
270 console.log( '☆★☆ 曲の最後に到達 @timeupdate now-end:' + ( now - end ) );
\r
272 if( X_HTMLAudio_endedFixIOS ) actualEnded = true;
\r
274 this.actualPause();
\r
275 eventType = X_EVENT_MEDIA_ENDED;
\r
278 eventType = X_EVENT_MEDIA_PLAYING;
\r
280 this._lastCurrentTime = now;
\r
284 //case 'stalled' : // ブラウザがコンテンツの取得を試みたが、データがまだ用意されていない場合に発生
\r
285 // Android2 で ready 扱い?
\r
286 //case 'suspend' : // ブラウザが意図的にコンテンツの取得を現在行っていない場合に発生(ダウンロードは未完了)
\r
288 //case 'emptied' : // 読み込み中に致命的なエラーが発生したか、実行状態ででload()メソッドが実行された場合に発生
\r
289 //case 'abort' : // ダウンロードの完了前にコンテンツの取得を停止した場合に発生(この停止はエラーによるものではない)
\r
292 //case 'error' : // コンテンツの取得実行中にエラーが発生した場合に発生
\r
293 // Opera12 src = '' で error が発生、無視する
\r
294 // eventType = X_EVENT_ERROR;
\r
297 case 'playing' : // 再生が開始された場合に発生
\r
298 if( X_HTMLAudio_volumeFix ){
\r
299 raw.volume = this.gain;
\r
301 //if( X_HTMLAudio_currentTimeFix && !this._currentFixStart ){
\r
302 //this._currentFixStart = X_Timer_now(); // 正確な再生開始時間に補正
\r
304 eventType = !this._durationFixPhase && !this._endedFixON ? X_EVENT_MEDIA_PLAYING : X_EVENT_MEDIA_WAITING;
\r
305 //case 'play' : // 再生が開始された。play()メソッドからの復帰後に発生する場合に発生
\r
306 //case 'pause' : // 再生が一時停止された。pauseメソッドからの復帰後に発生する場合に発生
\r
308 //case 'ratechange' : // defaultPlaybackRate属性とplaybackRate属性のどちらかが更新された場合に発生
\r
309 //case 'volumechange' : // volume属性とmuted属性のどちらかが変化した場合に発生
\r
312 case 'waiting' : // 次のフレームが利用不可のため再生を停止したが、そのフレームがやがて利用可能になると想定している場合に発生
\r
313 eventType = X_EVENT_MEDIA_WAITING;
\r
315 case 'seeking' : // シークがtrueに変化し、イベントを発生させるのに十分な時間がシーク操作にかかっている場合に発生
\r
316 eventType = X_EVENT_MEDIA_SEEKING;
\r
320 // duration は Infinity, NaN, 0 の場合があるため、これを除外する
\r
321 if( 0 < duration && X_Type_isFinite( duration ) ){
\r
322 this.duration = duration * 1000;
\r
324 if( this._durationFixPhase === 4 ){
\r
325 console.log( '▼ DurationFix の終了 @' + e.type );
\r
326 this._durationFixPhase = 8;
\r
328 if( this.autoplay ){
\r
329 console.log( '☆ 再生 <- DurationFix の終了' );
\r
332 if( X_HTMLAudio_pauseFix ){
\r
333 console.log( '☆ PAUSE <- DurationFix の終了' );
\r
336 this.actualPause();
\r
339 if( this._durationFixPhase & 3 ){ // === 1 | 2
\r
340 this._durationFixPhase = 8;
\r
345 if( this._touchState === 1 ){
\r
346 if( e.type === X_HTMLAudio_playTrigger ){
\r
347 this._touchState = 2;
\r
348 this.disatcher[ 'asyncDispatch' ]( X_EVENT_MEDIA_TOUCH_FOR_LOAD );
\r
352 if( this.autoLoop ){
\r
353 if( !( this.disatcher[ 'dispatch' ]( X_EVENT_MEDIA_BEFORE_LOOP ) & X_CALLBACK_PREVENT_DEFAULT ) ){
\r
354 this.looped = true;
\r
355 this.disatcher[ 'dispatch' ]( X_EVENT_MEDIA_LOOPED );
\r
356 ( X_HTMLAudio_endedFixAOSP3 || X_HTMLAudio_endedFixAOSP4 || X_HTMLAudio_endedFixCWV || X_HTMLAudio_endedFixIOS ) && actualEnded && console.log( '☆★☆ 音声の継続用の play() @ended' );
\r
358 ( X_HTMLAudio_endedFixAOSP4 || X_HTMLAudio_endedFixCWV || X_HTMLAudio_endedFixIOS ) && actualEnded,
\r
359 ( X_HTMLAudio_endedFixAOSP3 || X_HTMLAudio_endedFixAOSP2 ) && actualEnded );
\r
363 delete this.playing;
\r
364 this.disatcher[ 'dispatch' ]( X_EVENT_MEDIA_ENDED );
\r
367 if( this._readyState === 1 && this.duration ){
\r
368 this._readyState = 3;
\r
369 this.disatcher[ 'asyncDispatch' ]( X_EVENT_READY );
\r
370 this.autoplay && X_Timer_once( 16, this, this.actualPlay );
\r
371 console.log( '> Audio Loaded!! ' + e.type + ' d:' + ( this.duration | 0 ) );
\r
374 this.disatcher[ 'dispatch' ]( eventType );
\r
378 actualPlay : function( forcePlay, forceReload ){
\r
379 var raw = this[ '_rawObject' ],
\r
382 if( this._closed ) return;
\r
384 if( !raw.src ){ // X_HTMLAudio_pauseFix によって src が空になっている
\r
385 console.log( '○ 削除された audio.src の復帰' );
\r
386 raw.src = this._src;
\r
390 if( this._touchState === 2 ){
\r
391 e = X_EventDispatcher_CURRENT_EVENTS[ X_EventDispatcher_CURRENT_EVENTS.length - 1 ];
\r
392 if( !e || !e[ 'pointerType' ] ){
\r
393 alert( 'タッチイベント以外での play! ' + ( e ? e.type : '' ) );
\r
396 this._touchState = 3;
\r
398 if( this._readyState !== 3 && this._durationFixPhase < 2 ){
\r
399 this.autoplay = true;
\r
403 if( this._durationFixPhase & 3 ){ // 1 or 2
\r
404 console.log( '▲ DurationFix の開始' );
\r
405 this._durationFixPhase = 4;
\r
408 end = X_Audio_getEndTime( this );
\r
409 begin = X_Audio_getStartTime( this, end, true );
\r
411 this._lastCurrentTime = begin / 1000;
\r
413 if( this._shortPlayFixON ){
\r
414 this._shortPlayFixTime = ( 1000 < end - begin ) ? 200 : 400;
\r
415 if( this.duration < end + this._shortPlayFixTime ){
\r
416 this._shortPlayFixTime = this.duration - end;
\r
420 if( this._endedFixON ){
\r
421 console.log( '☆ audio.play をスキップ ' + begin + ' -> ' + end + ' crt:' + ( raw.currentTime | 0 ) );
\r
423 if( !this.playing ){
\r
424 if( X_HTMLAudio_volumeFix ){
\r
427 raw.volume = X_HTMLAudio_volumeEnabled ? this.gain : 1;
\r
430 this.playing = true;
\r
432 if( X_HTMLAudio_needPlayForSeek || forcePlay ){
\r
434 console.log( '[HTMLAudio] currentTime より先.' );
\r
437 //http://himaxoff.blog111.fc2.com/blog-entry-97.html
\r
438 //Firefox3.6では一度も play() していない状態で currentTime = 0 を実行するとエラーになる。
\r
439 //また、GoogleChrome7 では currentTime = 0 直後に play() すると、pause()した位置前後の音が混ざることがある。(少なくとも自分の環境では)
\r
441 // iOS で duration が 0 の時に触ると error
\r
443 if( !( this._durationFixPhase % 8 ) && this.duration ) raw.currentTime = this._lastCurrentTime;
\r
445 console.log( '[HTMLAudio] play ' + begin + ' -> ' + end + ' crt:' + ( raw.currentTime | 0 ) + ' last:' + this._lastCurrentTime );
\r
447 if( forceReload || ( X_HTMLAudio_endedFixAOSP4 && raw.duration && raw.currentTime === raw.duration ) ){
\r
449 raw.src = this._src;
\r
450 this.playing = false;
\r
451 this._endedFixON = true;
\r
452 console.log( '△ onEndedFix の開始' );
\r
453 raw.currentTime = this._lastCurrentTime;
\r
454 this.disatcher[ 'dispatch' ]( X_EVENT_MEDIA_WAITING );
\r
455 X_HTMLAudio_endedFixAOSP2 && raw.load();
\r
459 if( X_HTMLAudio_currentTimeFix ){
\r
460 this._currentFixBegin = begin;
\r
461 this._currentFixStart = X_Timer_now();
\r
465 actualPause : function(){
\r
466 var raw = this[ '_rawObject' ];
\r
468 console.log( '[HTMLAudio] pause' );
\r
470 this.seekTime = this.getActualCurrentTime();
\r
472 delete this._currentFixStart;
\r
474 !raw.error && raw.pause();
\r
476 if( X_HTMLAudio_pauseFix ){
\r
478 if( X_HTMLAudio_durationFix ){
\r
479 delete this._durationFixPhase;
\r
482 delete this.playing;
\r
485 getActualCurrentTime : function(){
\r
486 return ( X_HTMLAudio_currentTimeFix ?
\r
487 X_Timer_now() - this._currentFixStart + this._currentFixBegin :
\r
488 this[ '_rawObject' ].currentTime * 1000 | 0 );
\r
491 http://www.w3schools.com/tags/av_prop_error.asp
\r
492 1 = MEDIA_ERR_ABORTED - fetching process aborted by user
\r
493 2 = MEDIA_ERR_NETWORK - error occurred when downloading
\r
494 3 = MEDIA_ERR_DECODE - error occurred when decoding
\r
495 4 = MEDIA_ERR_SRC_NOT_SUPPORTED - audio/video not supported
\r
497 getActualError : function(){
\r
498 return this[ '_rawObject' ].error || 0;
\r
501 afterUpdateState : function( result ){
\r
502 if( result & 3 ){ // seek
\r
505 if( ( result & 4 ) && X_HTMLAudio_volumeEnabled ){
\r
506 this[ '_rawObject' ].volume = this.gain;
\r
513 X_HTMLAudio && X_Audio_BACKENDS.push(
\r
517 backendName : 'HTMLAudio',
\r
519 canPlay : X_Audio_codecs,
\r
521 * HTML5 の audio 要素と video 要素でサポートされているメディアフォーマット
\r
522 * https://developer.mozilla.org/ja/docs/Web/HTML/Supported_media_formats
\r
524 * 主要ブラウザのHTML5 audioタグで使えるファイル形式の再生対応状況を調べてみた
\r
525 * http://sothis.blog.so-net.ne.jp/2010-10-27
\r
526 * ダメ元で仕様に含まれていない SHOUTcast もテストしてみました。
\r
528 * IE9 の HTML5 Audio について
\r
529 * http://kentablog.cluscore.com/2011/05/ie9-html5-audio.html
\r
530 * 1.Audioオブジェクトを作ることができないので、Audioタグを使う
\r
531 * 2.クロスドメインアクセスには、「clientaccesspolicy.xml」か「crossdomain.xml」が必要
\r
534 * IE9でHTML5 autio タグが無効になる
\r
535 * http://bbs.wankuma.com/index.cgi?mode=al2&namber=64886&KLOG=109
\r
536 * IEのバージョン9.0.8112.16421では、Audioオブジェクトのnewも対応してました。
\r
537 * createElement等で動的生成すると、よろしくない
\r
539 * media-can-play-wav-audio.html
\r
540 * https://github.com/adobe/webkit/blob/master/LayoutTests/media/media-can-play-wav-audio.html
\r
541 * testExpected("audio.canPlayType('audio/wav; codecs=1')", "probably");
\r
543 * HTML5 audioタグ ブラウザ間の違い
\r
544 * 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
545 * - volume, muted iPhone(iOS4-6)、Android(2.3.6)では動作せず。
\r
546 * - FireFox3.6, Android 2.3.6については、src変更後、load()を呼び出さないと切り替わらなかった。iPhoneはload()が不要。
\r
548 detect : function( proxy, source, ext ){
\r
549 proxy[ 'asyncDispatch' ]( { type : X_EVENT_COMPLETE, canPlay : X_Audio_codecs[ ext ] } );
\r
552 klass : X_HTMLAudio
\r
560 mp3: !!audioTest.canPlayType('audio/mpeg;').replace(/^no$/, ''),
\r
561 opus: !!audioTest.canPlayType('audio/ogg; codecs="opus"').replace(/^no$/, ''),
\r
562 ogg: !!audioTest.canPlayType('audio/ogg; codecs="vorbis"').replace(/^no$/, ''),
\r
563 wav: !!audioTest.canPlayType('audio/wav; codecs="1"').replace(/^no$/, ''),
\r
564 aac: !!audioTest.canPlayType('audio/aac;').replace(/^no$/, ''),
\r
565 m4a: !!(audioTest.canPlayType('audio/x-m4a;') || audioTest.canPlayType('audio/m4a;') || audioTest.canPlayType('audio/aac;')).replace(/^no$/, ''),
\r
566 mp4: !!(audioTest.canPlayType('audio/x-mp4;') || audioTest.canPlayType('audio/mp4;') || audioTest.canPlayType('audio/aac;')).replace(/^no$/, ''),
\r
567 weba: !!audioTest.canPlayType('audio/webm; codecs="vorbis"').replace(/^no$/, '')
\r