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. Android4.1 iframe 内の Audio は親に focus が移っても再生を継続する
\r
21 var X_HTMLAudio_playTrigger =
\r
22 6 <= X_UA[ 'iOS' ] ? 'loadeddata' :
\r
23 X_UA[ 'iOS' ] < 5 ? 'stalled' :
\r
24 X_UA[ 'iOS' ] ? 'suspend' :
\r
25 X_UA[ 'Safari' ] < 4 ? 'canplaythrough' :
\r
26 X_UA[ 'ChromeWV' ] ? 'canplaythrough' :
\r
27 // Android 2.3.5(SBM101SH) では stalled は発生しない,,, ので必ず loadeddata もチェックする
\r
28 X_UA[ 'AOSP' ] ? 'stalled' :
\r
29 X_UA[ 'OperaMobile' ] || X_UA[ 'OperaTablet' ] ? 'loadeddata' :
\r
30 'loadeddata', //'canplay',
\r
33 // ended が発生しない timeupdate 内で play() を呼ぶ (未検証) 不具合確認は iOS4,6
\r
34 X_HTMLAudio_endedFixIOS = X_UA[ 'iOS' ] < 7,
\r
35 // Android 2.3.5 で ended 時に audio.src='';audio.src=src;audio.load() を実施。 2.3.4 でも問題なし。
\r
36 X_HTMLAudio_endedFixAOSP2 = X_UA[ 'AOSP' ] < 3,
\r
37 // Android 3.1 で ended 時に src='';src=src を実施。
\r
38 X_HTMLAudio_endedFixAOSP3 = !X_HTMLAudio_endedFixAOSP2 && X_UA[ 'AOSP' ] < 4,
\r
39 // ended 時に play() を実施, currentTime が duration に張り付き更新されなければ src='';src=src を実施。
\r
40 X_HTMLAudio_endedFixAOSP4 = 4 <= X_UA[ 'AOSP' ] || X_UA[ 'ChromeWV' ],
\r
42 // Opera Mobile 12 は 2回目以降の currentTime へのセットで currentTime が更新されなくなるため、タイマーを使用する
\r
43 X_HTMLAudio_currentTimeFix = !!X_UA[ 'OperaMobile' ] || !!X_UA[ 'OperaTablet' ],
\r
45 X_HTMLAudio_volumeFix = X_UA[ 'Chrome' ],
\r
47 * win opera12 volume, mute の変更が2度目以降できない
\r
49 X_HTMLAudio_volumeEnabled = !( X_UA[ 'WinPhone' ] && X_UA[ 'IE9' ] ) && !X_UA[ 'Opera' ],
\r
50 // Gecko PC + Android でseek時に再生がしばしば止まる問題の修正
\r
51 X_HTMLAudio_needPlayForSeek = X_UA[ 'Gecko' ],
\r
53 X_HTMLAudio_pauseFix = ( X_UA[ 'Windows' ] && 12 <= X_UA[ 'Opera' ] && 0 < ' XP XPSP2 2003|XP64'.indexOf( X_UA[ 'Windows' ] ) ), // XP + Opera12 のみ?
\r
56 * duration が取得できるタイミングが遅くそれまでは infinity(PC Opera12), NaN(WP9), 0(Android 標準ブラウザ ChromeWebView) が入っている
\r
58 * 1. touch が不要の場合、自動で再生を開始して duration を取得するまで再生する
\r
59 * -> 取得後に pause or 通常再生
\r
60 * 2. touch が必要な場合、タッチイベント内の audio.play() で duration 取得
\r
63 * 1. loadeddata 等では duration が infinity で、再生後の durationchange 時に duration が判明する
\r
64 * 2. duration 判明後には currentTime によるシークと、現在時間の取得が可能になる。
\r
65 * 3. Opera12.17 Win32(XP) portable apps は勝手に再生が始まる、、、Win8+Opera では発生しない
\r
66 * -> その際には timeupdate が発行されない、、、 iframe+image+audio で使わないときは破棄する、とか。
\r
67 * -> opera11、10.54 WinXP はまとも、、、 portable が怪しい??
\r
69 X_HTMLAudio_need1stTouch = X_UA[ 'iOS' ] || X_UA[ 'ChromeWV' ] || ( X_UA[ 'WinPhone' ] && X_UA[ 'IE9' ] ),
\r
70 X_HTMLAudio_durationFix = ( X_UA[ 'Windows' ] && 12 <= X_UA[ 'Opera' ] ) || X_UA[ 'ChromeWV' ] || ( X_UA[ 'WinPhone' ] && X_UA[ 'IE9' ] ),
\r
72 X_HTMLAudio_shortPlayFix = X_UA[ 'AOSP' ]; // Android 4.1.1 でも遭遇(ただしm4a, mp3は優秀, oggはシークが乱れる)
\r
76 if( X_Audio_constructor ){
\r
78 X_HTMLAudio = X_AudioBase[ 'inherits' ](
\r
80 X_Class.POOL_OBJECT,
\r
87 _currentFixStart : 0,
\r
88 _currentFixBegin : 0,
\r
90 _durationFixPhase : X_HTMLAudio_durationFix ? 1 : 0,
\r
91 _durationFixSkip : X_HTMLAudio_durationFix && !X_HTMLAudio_need1stTouch,
\r
92 _lastCurrentTime : 0,
\r
94 _shortPlayFixON : false,
\r
95 _shortPlayFixTime : 0,
\r
97 _endedFixON : false,
\r
99 _kickTimerID : false, // 処理が混み入ると AOSP で音声が再生されない
\r
102 'Constructor' : function( disatcher, source, option ){
\r
105 this.disatcher = disatcher || this;
\r
106 this._closed = false;
\r
107 this._src = source;
\r
109 if( X_HTMLAudio_shortPlayFix ){
\r
110 this._shortPlayFixON = X_URL_getEXT( source ) === 'm4a';
\r
113 this.setState( option );
\r
115 if( option[ 'useVideo' ] ){
\r
116 raw = document.createElement( 'video' );
\r
117 raw.preload = 'none'; // auto, metadata, none
\r
118 raw.autoplay = false, // no-auto
\r
121 raw.crossorigin = option[ 'crossorigin' ] || ''; //crossorigin: "anonymous", X.URL.isSameDomain() で切り替え
\r
122 raw.style.cssText = 'position:absolute;bottom:0;left:-50px;width:100px;height:100px;opacity:0;';
\r
123 raw.controls = false;
\r
124 raw.WebKitPlaysInline = true;
\r
126 X_elmBody.appendChild( raw );
\r
128 raw = X_TEMP.rawAudio || new X_Audio_constructor( source );
\r
129 // X_Doc_create( 'audio', { src : source } )[ 'appendTo' ]( X.Doc.body );
\r
130 raw.autobuffer = raw.autoplay = false;
\r
133 this[ '_rawObject' ] = raw;
\r
135 this[ 'listen' ]( [
\r
136 X_EVENT_KILL_INSTANCE,
\r
137 X_HTMLAudio_playTrigger,
\r
138 //'loadstart', 'load',
\r
139 'progress', 'error',
\r
140 // 'suspend', 'abort', 'emptied', 'stalled',
\r
141 // 'play', 'pause', 'seeked', 'ratechange', 'volumechange',
\r
142 'loadedmetadata', 'loadeddata', 'canplay', 'canplaythrough',
\r
143 'playing', 'waiting', 'seeking',
\r
144 'durationchange', 'timeupdate', 'ended' ] );
\r
146 if( X_TEMP.rawAudio ){
\r
148 raw.load(); // AOSP2 で必要
\r
149 delete X_TEMP.rawAudio;
\r
153 handleEvent : function( e ){
\r
154 var raw = this[ '_rawObject' ],
\r
155 actualEnded = e.type === 'ended',
\r
156 ended = actualEnded,
\r
157 ready = e.type === X_HTMLAudio_playTrigger,
\r
158 eventType, duration, end, now;
\r
161 //window[ '__rawAudio' ] = this[ '_rawObject' ];
\r
163 e.type !== 'timeupdate' && console.log( ' > ' + e.type );
\r
167 case X_EVENT_KILL_INSTANCE :
\r
168 // 【javascript】モバイル向けブラウザでも音を鳴らしたい【WebAudio】
\r
169 // http://ingaouhou.com/archives/3633
\r
170 // ・使い終わったインスタンスはload()しておくとやや安定
\r
171 this.playing && this.actualPause();
\r
172 delete this._closed;
\r
173 delete this._loaded;
\r
178 // removeChild for video
\r
181 //case 'loadstart' : // ブラウザがコンテンツの検索を開始した場合に発生
\r
183 case 'progress' : // ブラウザがコンテンツの取得を実行した場合に発生
\r
184 // console.log( e.loaded + ' ' + e.total * 100 + '%' );
\r
185 // iem9 で常に0 raw.networkState;
\r
186 // opera Android 12 で buffered.end() へのアクセスはエラー try catch も無効、iem9 は常に end(0) = 0
\r
187 //console.log( 'buffered.end ' + raw.buffered && raw.buffered.end(0) );
\r
190 case 'loadeddata' : // コンテンツの表示を現在の再生位置で初めて行えるようになった場合に発生
\r
191 if( !this._endedFixON ){
\r
194 case 'canplay' : // 今すぐに再生を再開できるが、バッファリングが不十分でコンテンツを最後まで表示できないと予測している場合に発生
\r
195 if( X_HTMLAudio_durationFix && !X_HTMLAudio_need1stTouch && this._durationFixPhase === 1 ){
\r
196 console.log( '▲ DurationFix の開始 @' + e.type );
\r
197 this._durationFixPhase = 2;
\r
199 raw.currentTime = 0; // 必要!
\r
201 case 'canplaythrough' : // 今すぐに再生を開始してもバッファリングで停止することなく最後まで表示できると予測している場合に発生
\r
202 if( this._endedFixON ){
\r
203 console.log( '▽ onEndedFix の終了 @' + e.type );
\r
204 this._endedFixON = false;
\r
207 case 'loadedmetadata' : // ブラウザがメディアリソースの長さと寸法を判定した場合に発生
\r
208 case 'durationchange' : // duration属性が更新された場合に発生
\r
209 if( !this.duration ){
\r
210 duration = raw.duration;
\r
214 // TODO firefox で 短い音声でtimeupdate, ended が発火しない <- 最後の音に無音部分を追加する
\r
215 case 'timeupdate' : // 通常の再生が行われ現在の再生位置の変化が起こった場合に発生
\r
216 if( this._durationFixPhase & 3 ){ // 1 or 2
\r
217 duration = raw.duration;
\r
219 if( raw.currentTime === this._lastCurrentTime ){
\r
220 eventType = X_EVENT_MEDIA_WAITING;
\r
222 this._lastCurrentTime = raw.currentTime;
\r
224 if( this.playing ){
\r
225 end = X_Audio_getEndTime( this ) + this._shortPlayFixTime;
\r
226 now = this.getActualCurrentTime();
\r
227 //console.log( now + ' / ' + end );
\r
228 if( 0 + end <= 0 + now ){ // 0+ なぜか iem9 で必要,,,
\r
229 if( this.autoLoop ){
\r
230 console.log( '☆★☆ 曲の最後に到達 @timeupdate now-end:' + ( now - end ) );
\r
232 if( X_HTMLAudio_endedFixIOS ) actualEnded = true;
\r
234 this.actualPause();
\r
235 eventType = X_EVENT_MEDIA_ENDED;
\r
238 eventType = X_EVENT_MEDIA_PLAYING;
\r
244 //case 'stalled' : // ブラウザがコンテンツの取得を試みたが、データがまだ用意されていない場合に発生
\r
245 // Android2 で ready 扱い?
\r
246 //case 'suspend' : // ブラウザが意図的にコンテンツの取得を現在行っていない場合に発生(ダウンロードは未完了)
\r
248 //case 'emptied' : // 読み込み中に致命的なエラーが発生したか、実行状態ででload()メソッドが実行された場合に発生
\r
249 //case 'abort' : // ダウンロードの完了前にコンテンツの取得を停止した場合に発生(この停止はエラーによるものではない)
\r
252 case 'error' : // コンテンツの取得実行中にエラーが発生した場合に発生
\r
253 // Opera12 src = '' で error が発生、無視する
\r
254 // eventType = X_EVENT_ERROR;
\r
257 case 'playing' : // 再生が開始された場合に発生
\r
258 if( X_HTMLAudio_volumeFix ){
\r
259 raw.volume = this.gain;
\r
261 if( X_HTMLAudio_currentTimeFix ){
\r
262 this._currentFixStart = X_Timer_now(); // 正確な再生開始時間に補正
\r
264 eventType = !this._durationFixSkip && !this._endedFixON ? X_EVENT_MEDIA_PLAYING : X_EVENT_MEDIA_WAITING;
\r
265 //case 'play' : // 再生が開始された。play()メソッドからの復帰後に発生する場合に発生
\r
266 //case 'pause' : // 再生が一時停止された。pauseメソッドからの復帰後に発生する場合に発生
\r
268 //case 'ratechange' : // defaultPlaybackRate属性とplaybackRate属性のどちらかが更新された場合に発生
\r
269 //case 'volumechange' : // volume属性とmuted属性のどちらかが変化した場合に発生
\r
272 case 'waiting' : // 次のフレームが利用不可のため再生を停止したが、そのフレームがやがて利用可能になると想定している場合に発生
\r
273 eventType = X_EVENT_MEDIA_WAITING;
\r
275 case 'seeking' : // シークがtrueに変化し、イベントを発生させるのに十分な時間がシーク操作にかかっている場合に発生
\r
276 eventType = X_EVENT_MEDIA_SEEKING;
\r
280 // duration は Infinity, NaN, 0 の場合があるため、これを除外する
\r
281 if( 0 < duration && X_Type_isFinite( duration ) ){
\r
282 this.duration = duration * 1000;
\r
284 if( this._durationFixPhase === 2 ){
\r
285 console.log( '▼ DurationFix の終了 @' + e.type );
\r
286 this._durationFixPhase = 4;
\r
290 if( this.autoplay || this._loaded ){
\r
291 console.log( '☆ 再生 <- DurationFix の終了' );
\r
292 this._durationFixSkip = false;
\r
295 if( X_HTMLAudio_pauseFix ){
\r
296 console.log( '☆ PAUSE <- DurationFix の終了' );
\r
299 this.actualPause();
\r
302 if( this._durationFixPhase ){
\r
303 this._durationFixPhase = 4;
\r
307 this._loaded = this._loaded || ready;
\r
311 if( !this._closed && this.autoLoop ){
\r
312 if( !( this.disatcher[ 'dispatch' ]( X_EVENT_MEDIA_BEFORE_LOOP ) & X_CALLBACK_PREVENT_DEFAULT ) ){
\r
313 this.looped = true;
\r
314 this.disatcher[ 'dispatch' ]( X_EVENT_MEDIA_LOOPED );
\r
315 ( X_HTMLAudio_endedFixAOSP3 || X_HTMLAudio_endedFixAOSP4 || X_HTMLAudio_endedFixIOS ) && actualEnded && console.log( '☆★☆ 音声の継続用の play() @ended' );
\r
317 ( X_HTMLAudio_endedFixAOSP4 || X_HTMLAudio_endedFixIOS ) && actualEnded,
\r
318 ( X_HTMLAudio_endedFixAOSP3 || X_HTMLAudio_endedFixAOSP2 ) && actualEnded );
\r
322 delete this.playing;
\r
323 this.disatcher[ 'dispatch' ]( X_EVENT_MEDIA_ENDED );
\r
326 if( this._loaded && this.duration && !this._ready ){
\r
327 this._ready = true;
\r
328 this.autoplay && X_Timer_once( 16, this, this.play );
\r
329 this.disatcher[ 'asyncDispatch' ]( X_EVENT_READY );
\r
330 console.log( '> Audio Loaded!! ' + e.type + ' d:' + ( this.duration | 0 ) );
\r
333 this.disatcher[ 'dispatch' ]( eventType );
\r
334 eventType === X_EVENT_ERROR && this[ 'kill' ]();
\r
338 actualPlay : function( forcePlay, forceReload ){
\r
339 var raw = this[ '_rawObject' ],
\r
342 // もし kill 後に autoplayTimer で呼ばれても、_closed==true なので平気
\r
343 if( this._closed ) return;
\r
346 if( !raw.src ){ // X_HTMLAudio_pauseFix によって src が空になっている
\r
347 console.log( '○ 削除された audio.src の復帰' );
\r
348 raw.src = this._src;
\r
352 if( !this._ready && ( !X_HTMLAudio_durationFix || !X_HTMLAudio_need1stTouch ) ){
\r
353 this.autoplay = true;
\r
357 if( X_HTMLAudio_durationFix && X_HTMLAudio_need1stTouch && this._durationFixPhase === 1 ){
\r
358 console.log( '▲ DurationFix の開始(タッチ用)' );
\r
359 this._durationFixPhase = 2;
\r
362 end = X_Audio_getEndTime( this );
\r
363 begin = X_Audio_getStartTime( this, end, true );
\r
365 this._lastCurrentTime = begin / 1000;
\r
367 if( this._shortPlayFixON ){
\r
368 this._shortPlayFixTime = ( 1000 < end - begin ) ? 200 : 400;
\r
369 if( this.duration < end + this._shortPlayFixTime ){
\r
370 this._shortPlayFixTime = this.duration - end;
\r
374 if( this._durationFixSkip || this._endedFixON ){
\r
375 console.log( '☆ audio.play をスキップ ' + begin + ' -> ' + end + ' crt:' + ( raw.currentTime | 0 ) );
\r
377 if( !this.playing ){
\r
378 if( X_HTMLAudio_volumeFix ){
\r
381 raw.volume = X_HTMLAudio_volumeEnabled ? this.gain : 1;
\r
384 this.playing = true;
\r
386 if( X_UA[ 'AOSP' ] && !this._kickTimerID ){
\r
387 this._kickTimerID = X_Timer_add( 100, 0, this, this._kick );
\r
390 if( X_HTMLAudio_needPlayForSeek || forcePlay ){
\r
392 console.log( '[HTMLAudio] currentTime より先.' );
\r
395 //http://himaxoff.blog111.fc2.com/blog-entry-97.html
\r
396 //Firefox3.6では一度も play() していない状態で currentTime = 0 を実行するとエラーになる。
\r
397 //また、GoogleChrome7 では currentTime = 0 直後に play() すると、pause()した位置前後の音が混ざることがある。(少なくとも自分の環境では)
\r
398 raw.currentTime = this._lastKickTime = this._lastCurrentTime;
\r
399 console.log( '[HTMLAudio] play ' + begin + ' -> ' + end + ' crt:' + ( raw.currentTime | 0 ) + ' last:' + this._lastCurrentTime );
\r
401 // Android4.0.5 で ended イベント時に currentTime が duration に張り付いたまま変更できない
\r
402 if( forceReload || ( X_HTMLAudio_endedFixAOSP4 && raw.duration && raw.currentTime === raw.duration ) ){
\r
404 raw.src = this._src;
\r
405 this.playing = false;
\r
406 this._endedFixON = true;
\r
407 console.log( '△ onEndedFix の開始' );
\r
408 raw.currentTime = this._lastCurrentTime;
\r
409 this.disatcher[ 'dispatch' ]( X_EVENT_MEDIA_WAITING );
\r
410 X_HTMLAudio_endedFixAOSP2 && raw.load();
\r
414 if( X_HTMLAudio_currentTimeFix ){
\r
415 this._currentFixBegin = begin;
\r
416 this._currentFixStart = X_Timer_now();
\r
420 _kick : function(){
\r
421 var raw = this[ '_rawObject' ];
\r
423 console.log( ' >> KICK ? ct:' + ( raw.currentTime ) + ' lkt:' + this._lastKickTime );
\r
424 if( this.playing && raw.currentTime === this._lastKickTime ){
\r
425 this.disatcher[ 'dispatch' ]( X_EVENT_MEDIA_WAITING );
\r
426 console.log( ' >> KICK !!' );
\r
429 delete this._kickTimerID;
\r
430 return X_CALLBACK_UN_LISTEN;
\r
434 actualPause : function(){
\r
435 console.log( '[HTMLAudio] pause' );
\r
437 this.seekTime = this.getActualCurrentTime();
\r
439 delete this._currentFixStart;
\r
441 !this[ '_rawObject' ].error && this[ '_rawObject' ].pause();
\r
443 if( X_HTMLAudio_pauseFix ){
\r
444 this[ '_rawObject' ].src = '';
\r
445 if( X_HTMLAudio_durationFix ){
\r
446 delete this._durationFixPhase;
\r
447 delete this._durationFixSkip;
\r
450 delete this.playing;
\r
453 getActualCurrentTime : function(){
\r
454 return ( X_HTMLAudio_currentTimeFix ?
\r
455 X_Timer_now() - this._currentFixStart + this._currentFixBegin :
\r
456 this[ '_rawObject' ].currentTime * 1000 | 0 );
\r
459 http://www.w3schools.com/tags/av_prop_error.asp
\r
460 1 = MEDIA_ERR_ABORTED - fetching process aborted by user
\r
461 2 = MEDIA_ERR_NETWORK - error occurred when downloading
\r
462 3 = MEDIA_ERR_DECODE - error occurred when decoding
\r
463 4 = MEDIA_ERR_SRC_NOT_SUPPORTED - audio/video not supported
\r
465 getActualError : function(){
\r
466 return this[ '_rawObject' ].error || 0;
\r
469 afterUpdateState : function( result ){
\r
470 if( result & 3 ){ // seek
\r
473 if( ( result & 4 ) && X_HTMLAudio_volumeEnabled ){
\r
474 this[ '_rawObject' ].volume = this.gain;
\r
481 X_HTMLAudio && X_Audio_BACKENDS.push(
\r
485 backendName : 'HTMLAudio',
\r
487 canPlay : X_Audio_codecs,
\r
489 * HTML5 の audio 要素と video 要素でサポートされているメディアフォーマット
\r
490 * https://developer.mozilla.org/ja/docs/Web/HTML/Supported_media_formats
\r
492 * 主要ブラウザのHTML5 audioタグで使えるファイル形式の再生対応状況を調べてみた
\r
493 * http://sothis.blog.so-net.ne.jp/2010-10-27
\r
494 * ダメ元で仕様に含まれていない SHOUTcast もテストしてみました。
\r
496 * IE9 の HTML5 Audio について
\r
497 * http://kentablog.cluscore.com/2011/05/ie9-html5-audio.html
\r
498 * 1.Audioオブジェクトを作ることができないので、Audioタグを使う
\r
499 * 2.クロスドメインアクセスには、「clientaccesspolicy.xml」か「crossdomain.xml」が必要
\r
502 * IE9でHTML5 autio タグが無効になる
\r
503 * http://bbs.wankuma.com/index.cgi?mode=al2&namber=64886&KLOG=109
\r
504 * IEのバージョン9.0.8112.16421では、Audioオブジェクトのnewも対応してました。
\r
505 * createElement等で動的生成すると、よろしくない
\r
507 * media-can-play-wav-audio.html
\r
508 * https://github.com/adobe/webkit/blob/master/LayoutTests/media/media-can-play-wav-audio.html
\r
509 * testExpected("audio.canPlayType('audio/wav; codecs=1')", "probably");
\r
511 * HTML5 audioタグ ブラウザ間の違い
\r
512 * 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
513 * - volume, muted iPhone(iOS4-6)、Android(2.3.6)では動作せず。
\r
514 * - FireFox3.6, Android 2.3.6については、src変更後、load()を呼び出さないと切り替わらなかった。iPhoneはload()が不要。
\r
516 detect : function( proxy, source, ext ){
\r
517 proxy[ 'asyncDispatch' ]( { type : X_EVENT_COMPLETE, canPlay : X_Audio_codecs[ ext ] } );
\r
520 klass : X_HTMLAudio
\r
528 mp3: !!audioTest.canPlayType('audio/mpeg;').replace(/^no$/, ''),
\r
529 opus: !!audioTest.canPlayType('audio/ogg; codecs="opus"').replace(/^no$/, ''),
\r
530 ogg: !!audioTest.canPlayType('audio/ogg; codecs="vorbis"').replace(/^no$/, ''),
\r
531 wav: !!audioTest.canPlayType('audio/wav; codecs="1"').replace(/^no$/, ''),
\r
532 aac: !!audioTest.canPlayType('audio/aac;').replace(/^no$/, ''),
\r
533 m4a: !!(audioTest.canPlayType('audio/x-m4a;') || audioTest.canPlayType('audio/m4a;') || audioTest.canPlayType('audio/aac;')).replace(/^no$/, ''),
\r
534 mp4: !!(audioTest.canPlayType('audio/x-mp4;') || audioTest.canPlayType('audio/mp4;') || audioTest.canPlayType('audio/aac;')).replace(/^no$/, ''),
\r
535 weba: !!audioTest.canPlayType('audio/webm; codecs="vorbis"').replace(/^no$/, '')
\r