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. AOSP4.2(Genymotion) どこかのタイミングでタッチによる play を行う
\r
20 * 3. AOSP oggはシークが乱れる m4a, mp3 は優秀
\r
24 * duration が取得できるタイミングが遅くそれまでは infinity(PC Opera12), NaN(WP9), 0(Android 標準ブラウザ ChromeWebView) が入っている
\r
26 * 1. touch が不要の場合、自動で再生を開始して duration を取得するまで再生する
\r
27 * -> 取得後に pause or 通常再生
\r
28 * 2. touch が必要な場合、タッチイベント内の audio.play() で duration 取得
\r
31 * 1. loadeddata 等では duration が infinity で、再生後の durationchange 時に duration が判明する
\r
32 * 2. duration 判明後には currentTime によるシークと、現在時間の取得が可能になる。
\r
33 * 3. Opera12.17 Win32(XP) portable apps は勝手に再生が始まる、、、Win8+Opera では発生しない
\r
34 * -> その際には timeupdate が発行されない、、、 iframe+image+audio で使わないときは破棄する、とか。
\r
35 * -> opera11、10.54 WinXP はまとも、、、 portable が怪しい??
\r
38 var X_HTMLAudio_playTrigger =
\r
39 X_UA[ 'iOS' ] < 5 ? 'stalled' :
\r
40 X_UA[ 'iOS' ] < 6 ? 'suspend' :
\r
41 X_UA[ 'iOS' ] < 7 ? 'suspend' :
\r
42 X_UA[ 'iOS' ] ? 'loadeddata' :
\r
43 X_UA[ 'Safari' ] < 4 ? 'canplaythrough' :
\r
44 X_UA[ 'ChromeWV' ] ? 'canplaythrough' :
\r
45 // Android 2.3.5(SBM101SH) では stalled は発生しない,,, ので必ず loadeddata もチェックする
\r
46 //X_UA[ 'AOSP' ] < 3 ? 'stalled' :
\r
47 //X_UA[ 'AOSP' ] ? 'playing' :
\r
48 X_UA[ 'OperaMobile' ] || X_UA[ 'OperaTablet' ] ? 'loadeddata' :
\r
49 'loadeddata', //'canplay',
\r
52 // ended が発生しない timeupdate 内で play() を呼ぶ (未検証) 不具合確認は iOS4,6
\r
53 X_HTMLAudio_endedFixIOS = X_UA[ 'iOS' ] < 7,
\r
54 // Android 2.3.5 で ended 時に audio.src='';audio.src=src;audio.load() を実施。 2.3.4 でも問題なし。
\r
55 X_HTMLAudio_endedFixAOSP2 = X_UA[ 'AOSP' ] < 3,
\r
56 // Android 3.1 で ended 時に src='';src=src を実施。
\r
57 X_HTMLAudio_endedFixAOSP3 = !X_HTMLAudio_endedFixAOSP2 && X_UA[ 'AOSP' ] < 4,
\r
58 // ended 時に play() を実施, currentTime が duration に張り付き更新されなければ src='';src=src を実施。
\r
59 X_HTMLAudio_endedFixAOSP4 = 4 <= X_UA[ 'AOSP' ],
\r
60 // ended 時に play() を実施
\r
61 X_HTMLAudio_endedFixCWV = X_UA[ 'ChromeWV' ],
\r
63 // Opera Mobile 12 は 2回目以降の currentTime へのセットで currentTime が更新されなくなるため、タイマーを使用する
\r
64 X_HTMLAudio_currentTimeFix = !!X_UA[ 'OperaMobile' ] || !!X_UA[ 'OperaTablet' ],
\r
66 X_HTMLAudio_volumeFix = X_UA[ 'Chrome' ],
\r
68 * win opera12 volume, mute の変更が2度目以降できない
\r
70 X_HTMLAudio_volumeEnabled = !( X_UA[ 'WinPhone' ] && X_UA[ 'IE9' ] ) && !X_UA[ 'Opera' ],
\r
71 // Gecko PC + Android でseek時に再生がしばしば止まる問題の修正
\r
72 X_HTMLAudio_needPlayForSeek = X_UA[ 'Gecko' ],
\r
74 X_HTMLAudio_pauseFix = ( X_UA[ 'Windows' ] && 12 <= X_UA[ 'Opera' ] && 0 < ' XP XPSP2 2003|XP64'.indexOf( X_UA[ 'Windows' ] ) ), // XP + Opera12 のみ?
\r
76 X_HTMLAudio_need1stTouch = X_UA[ 'iOS' ] || 4 <= X_UA[ 'AOSP' ] || X_UA[ 'ChromeWV' ] || X_UA[ 'WinPhone' ],
\r
77 X_HTMLAudio_durationFix = X_UA[ 'iOS' ] || X_UA[ 'ChromeWV' ] || ( X_UA[ 'Windows' ] && 12 <= X_UA[ 'Opera' ] ) || ( X_UA[ 'WinPhone' ] && X_UA[ 'IE9' ] ),
\r
79 X_HTMLAudio_shortPlayFix = X_UA[ 'AOSP' ]; // Android 4.1.1 でも遭遇
\r
83 if( X_Audio_constructor ){
\r
85 X_HTMLAudio = X_AudioBase[ 'inherits' ](
\r
87 X_Class.POOL_OBJECT,
\r
94 _currentFixStart : 0,
\r
95 _currentFixBegin : 0,
\r
97 _durationFixPhase : X_HTMLAudio_durationFix ? 1 : 0,
\r
98 _durationFixSkip : X_HTMLAudio_durationFix && !X_HTMLAudio_need1stTouch,
\r
99 _lastCurrentTime : 0,
\r
101 _shortPlayFixON : false,
\r
102 _shortPlayFixTime : 0,
\r
104 _endedFixON : false,
\r
106 'Constructor' : function( disatcher, source, option ){
\r
109 this.disatcher = disatcher || this;
\r
110 this._closed = false;
\r
111 this._src = source;
\r
113 if( X_HTMLAudio_shortPlayFix ){
\r
114 this._shortPlayFixON = X_URL_getEXT( source ) === 'm4a';
\r
117 this.setState( option );
\r
119 if( option[ 'useVideo' ] ){
\r
120 raw = document.createElement( 'video' );
\r
121 raw.preload = 'none'; // auto, metadata, none
\r
122 raw.autoplay = false, // no-auto
\r
125 raw.crossorigin = option[ 'crossorigin' ] || ''; //crossorigin: "anonymous", X.URL.isSameDomain() で切り替え
\r
126 raw.style.cssText = 'position:absolute;bottom:0;left:-50px;width:100px;height:100px;opacity:0;';
\r
127 raw.controls = false;
\r
128 raw.WebKitPlaysInline = true;
\r
130 X_elmBody.appendChild( raw );
\r
132 raw = X_TEMP.rawAudio || new X_Audio_constructor( source );
\r
133 // X_Doc_create( 'audio', { src : source } )[ 'appendTo' ]( X.Doc.body );
\r
134 raw.autobuffer = raw.autoplay = false;
\r
137 this[ '_rawObject' ] = raw;
\r
139 this[ 'listen' ]( [
\r
140 X_EVENT_KILL_INSTANCE,
\r
141 X_HTMLAudio_playTrigger,
\r
142 //'loadstart', 'load',
\r
143 'progress', 'error',
\r
144 // 'suspend', 'abort', 'emptied', 'stalled',
\r
145 // 'play', 'pause', 'seeked', 'ratechange', 'volumechange',
\r
146 'loadedmetadata', 'loadeddata', 'canplay', 'canplaythrough',
\r
147 'playing', 'waiting', 'seeking',
\r
148 'durationchange', 'timeupdate', 'ended' ] );
\r
151 this[ 'listen' ]( [
\r
152 'loadstart', 'load',
\r
153 'progress', 'error',
\r
154 'suspend', 'abort', 'emptied', 'stalled',
\r
155 'play', 'pause', 'seeked', 'ratechange', 'volumechange',
\r
156 'loadedmetadata', 'loadeddata', 'canplay', 'canplaythrough',
\r
157 'playing', 'waiting', 'seeking',
\r
158 'durationchange', 'timeupdate', 'ended' ], this.onDebug );
\r
160 if( X_TEMP.rawAudio ){
\r
162 X_UA[ 'AOSP' ] < 3 && raw.load();
\r
163 delete X_TEMP.rawAudio;
\r
167 onDebug : function( e ){
\r
168 this.disatcher[ 'dispatch' ]( {
\r
169 type : X_EVENT_DEBUG,
\r
170 'rawEvent' : e.type,
\r
171 current : this.getActualCurrentTime() | 0,
\r
172 duration : this.duration | 0 } );
\r
175 handleEvent : function( e ){
\r
176 var raw = this[ '_rawObject' ],
\r
177 actualEnded = e.type === 'ended',
\r
178 ended = actualEnded,
\r
179 ready = e.type === X_HTMLAudio_playTrigger,
\r
180 eventType, duration, end, now;
\r
183 //window[ '__rawAudio' ] = this[ '_rawObject' ];
\r
185 e.type !== 'timeupdate' && console.log( ' > ' + e.type );
\r
189 case X_EVENT_KILL_INSTANCE :
\r
190 // 【javascript】モバイル向けブラウザでも音を鳴らしたい【WebAudio】
\r
191 // http://ingaouhou.com/archives/3633
\r
192 // ・使い終わったインスタンスはload()しておくとやや安定
\r
193 this.playing && this.actualPause();
\r
194 delete this._closed;
\r
195 delete this._loaded;
\r
200 // removeChild for video
\r
203 //case 'loadstart' : // ブラウザがコンテンツの検索を開始した場合に発生
\r
205 case 'progress' : // ブラウザがコンテンツの取得を実行した場合に発生
\r
206 // console.log( e.loaded + ' ' + e.total * 100 + '%' );
\r
207 // iem9 で常に0 raw.networkState;
\r
208 // opera Android 12 で buffered.end() へのアクセスはエラー try catch も無効、iem9 は常に end(0) = 0
\r
209 //console.log( 'buffered.end ' + raw.buffered && raw.buffered.end(0) );
\r
212 case 'loadeddata' : // コンテンツの表示を現在の再生位置で初めて行えるようになった場合に発生
\r
213 if( !this._endedFixON ){
\r
216 case 'canplay' : // 今すぐに再生を再開できるが、バッファリングが不十分でコンテンツを最後まで表示できないと予測している場合に発生
\r
217 if( X_HTMLAudio_durationFix && !X_HTMLAudio_need1stTouch && this._durationFixPhase === 1 ){
\r
218 console.log( '▲ DurationFix の開始 @' + e.type );
\r
219 this._durationFixPhase = 2;
\r
221 raw.currentTime = 0; // 必要!
\r
223 case 'canplaythrough' : // 今すぐに再生を開始してもバッファリングで停止することなく最後まで表示できると予測している場合に発生
\r
224 if( this._endedFixON ){
\r
225 console.log( '▽ onEndedFix の終了 @' + e.type );
\r
226 this._endedFixON = false;
\r
229 case 'loadedmetadata' : // ブラウザがメディアリソースの長さと寸法を判定した場合に発生
\r
230 case 'durationchange' : // duration属性が更新された場合に発生
\r
231 if( !this.duration ){
\r
232 duration = raw.duration;
\r
236 // TODO firefox で 短い音声でtimeupdate, ended が発火しない <- 最後の音に無音部分を追加する
\r
237 case 'timeupdate' : // 通常の再生が行われ現在の再生位置の変化が起こった場合に発生
\r
238 if( this._durationFixPhase & 3 ){ // 1 or 2
\r
239 duration = raw.duration;
\r
241 if( this.getActualCurrentTime() === this._lastCurrentTime ){
\r
242 eventType = X_EVENT_MEDIA_WAITING;
\r
244 this._lastCurrentTime = this.getActualCurrentTime();
\r
246 if( this.playing ){
\r
247 end = X_Audio_getEndTime( this ) + this._shortPlayFixTime;
\r
248 now = this.getActualCurrentTime();
\r
249 //console.log( now + ' / ' + end );
\r
250 if( 0 + end <= 0 + now ){ // 0+ なぜか iem9 で必要,,,
\r
251 if( this.autoLoop ){
\r
252 console.log( '☆★☆ 曲の最後に到達 @timeupdate now-end:' + ( now - end ) );
\r
254 if( X_HTMLAudio_endedFixIOS ) actualEnded = true;
\r
256 this.actualPause();
\r
257 eventType = X_EVENT_MEDIA_ENDED;
\r
260 eventType = X_EVENT_MEDIA_PLAYING;
\r
266 //case 'stalled' : // ブラウザがコンテンツの取得を試みたが、データがまだ用意されていない場合に発生
\r
267 // Android2 で ready 扱い?
\r
268 //case 'suspend' : // ブラウザが意図的にコンテンツの取得を現在行っていない場合に発生(ダウンロードは未完了)
\r
270 //case 'emptied' : // 読み込み中に致命的なエラーが発生したか、実行状態ででload()メソッドが実行された場合に発生
\r
271 //case 'abort' : // ダウンロードの完了前にコンテンツの取得を停止した場合に発生(この停止はエラーによるものではない)
\r
274 case 'error' : // コンテンツの取得実行中にエラーが発生した場合に発生
\r
275 // Opera12 src = '' で error が発生、無視する
\r
276 // eventType = X_EVENT_ERROR;
\r
279 case 'playing' : // 再生が開始された場合に発生
\r
280 if( X_HTMLAudio_volumeFix ){
\r
281 raw.volume = this.gain;
\r
283 if( X_HTMLAudio_currentTimeFix && !this._currentFixStart ){
\r
284 //this._currentFixStart = X_Timer_now(); // 正確な再生開始時間に補正
\r
286 eventType = !this._durationFixSkip && !this._endedFixON ? X_EVENT_MEDIA_PLAYING : X_EVENT_MEDIA_WAITING;
\r
287 //case 'play' : // 再生が開始された。play()メソッドからの復帰後に発生する場合に発生
\r
288 //case 'pause' : // 再生が一時停止された。pauseメソッドからの復帰後に発生する場合に発生
\r
290 //case 'ratechange' : // defaultPlaybackRate属性とplaybackRate属性のどちらかが更新された場合に発生
\r
291 //case 'volumechange' : // volume属性とmuted属性のどちらかが変化した場合に発生
\r
294 case 'waiting' : // 次のフレームが利用不可のため再生を停止したが、そのフレームがやがて利用可能になると想定している場合に発生
\r
295 eventType = X_EVENT_MEDIA_WAITING;
\r
297 case 'seeking' : // シークがtrueに変化し、イベントを発生させるのに十分な時間がシーク操作にかかっている場合に発生
\r
298 eventType = X_EVENT_MEDIA_SEEKING;
\r
302 // duration は Infinity, NaN, 0 の場合があるため、これを除外する
\r
303 if( 0 < duration && X_Type_isFinite( duration ) ){
\r
304 this.duration = duration * 1000;
\r
306 if( this._durationFixPhase === 2 ){
\r
307 console.log( '▼ DurationFix の終了 @' + e.type );
\r
308 this._durationFixPhase = 4;
\r
312 if( this.autoplay || this._loaded ){
\r
313 console.log( '☆ 再生 <- DurationFix の終了' );
\r
314 this._durationFixSkip = false;
\r
317 if( X_HTMLAudio_pauseFix ){
\r
318 console.log( '☆ PAUSE <- DurationFix の終了' );
\r
321 this.actualPause();
\r
324 if( this._durationFixPhase ){
\r
325 this._durationFixPhase = 4;
\r
329 this._loaded = this._loaded || ready;
\r
333 if( !this._closed && this.autoLoop ){
\r
334 if( !( this.disatcher[ 'dispatch' ]( X_EVENT_MEDIA_BEFORE_LOOP ) & X_CALLBACK_PREVENT_DEFAULT ) ){
\r
335 this.looped = true;
\r
336 this.disatcher[ 'dispatch' ]( X_EVENT_MEDIA_LOOPED );
\r
337 ( X_HTMLAudio_endedFixAOSP3 || X_HTMLAudio_endedFixAOSP4 || X_HTMLAudio_endedFixCWV || X_HTMLAudio_endedFixIOS ) && actualEnded && console.log( '☆★☆ 音声の継続用の play() @ended' );
\r
339 ( X_HTMLAudio_endedFixAOSP4 || X_HTMLAudio_endedFixCWV || X_HTMLAudio_endedFixIOS ) && actualEnded,
\r
340 ( X_HTMLAudio_endedFixAOSP3 || X_HTMLAudio_endedFixAOSP2 ) && actualEnded );
\r
344 delete this.playing;
\r
345 this.disatcher[ 'dispatch' ]( X_EVENT_MEDIA_ENDED );
\r
348 if( this._loaded && this.duration && !this._ready ){
\r
349 this._ready = true;
\r
350 this.disatcher[ 'asyncDispatch' ]( X_EVENT_READY );
\r
351 this.autoplay && !X_WebAudio_need1stTouch && X_Timer_once( 16, this, this.play );
\r
352 console.log( '> Audio Loaded!! ' + e.type + ' d:' + ( this.duration | 0 ) );
\r
355 this.disatcher[ 'dispatch' ]( eventType );
\r
356 eventType === X_EVENT_ERROR && this[ 'kill' ]();
\r
360 actualPlay : function( forcePlay, forceReload ){
\r
361 var raw = this[ '_rawObject' ],
\r
364 // もし kill 後に autoplayTimer で呼ばれても、_closed==true なので平気
\r
365 if( this._closed ) return;
\r
368 if( !raw.src ){ // X_HTMLAudio_pauseFix によって src が空になっている
\r
369 console.log( '○ 削除された audio.src の復帰' );
\r
370 raw.src = this._src;
\r
374 if( !this._ready && ( !X_HTMLAudio_durationFix || !X_HTMLAudio_need1stTouch ) ){
\r
375 this.autoplay = true;
\r
379 if( X_HTMLAudio_durationFix && X_HTMLAudio_need1stTouch && this._durationFixPhase === 1 ){
\r
380 console.log( '▲ DurationFix の開始(タッチ用)' );
\r
381 this._durationFixPhase = 2;
\r
384 end = X_Audio_getEndTime( this );
\r
385 begin = X_Audio_getStartTime( this, end, true );
\r
387 this._lastCurrentTime = begin / 1000;
\r
389 if( this._shortPlayFixON ){
\r
390 this._shortPlayFixTime = ( 1000 < end - begin ) ? 200 : 400;
\r
391 if( this.duration < end + this._shortPlayFixTime ){
\r
392 this._shortPlayFixTime = this.duration - end;
\r
396 if( this._durationFixSkip || this._endedFixON ){
\r
397 console.log( '☆ audio.play をスキップ ' + begin + ' -> ' + end + ' crt:' + ( raw.currentTime | 0 ) );
\r
399 if( !this.playing ){
\r
400 if( X_HTMLAudio_volumeFix ){
\r
403 raw.volume = X_HTMLAudio_volumeEnabled ? this.gain : 1;
\r
406 this.playing = true;
\r
408 if( X_HTMLAudio_needPlayForSeek || forcePlay ){
\r
410 console.log( '[HTMLAudio] currentTime より先.' );
\r
413 //http://himaxoff.blog111.fc2.com/blog-entry-97.html
\r
414 //Firefox3.6では一度も play() していない状態で currentTime = 0 を実行するとエラーになる。
\r
415 //また、GoogleChrome7 では currentTime = 0 直後に play() すると、pause()した位置前後の音が混ざることがある。(少なくとも自分の環境では)
\r
416 raw.currentTime = this._lastCurrentTime;
\r
417 console.log( '[HTMLAudio] play ' + begin + ' -> ' + end + ' crt:' + ( raw.currentTime | 0 ) + ' last:' + this._lastCurrentTime );
\r
419 if( forceReload || ( X_HTMLAudio_endedFixAOSP4 && raw.duration && raw.currentTime === raw.duration ) ){
\r
421 raw.src = this._src;
\r
422 this.playing = false;
\r
423 this._endedFixON = true;
\r
424 console.log( '△ onEndedFix の開始' );
\r
425 raw.currentTime = this._lastCurrentTime;
\r
426 this.disatcher[ 'dispatch' ]( X_EVENT_MEDIA_WAITING );
\r
427 X_HTMLAudio_endedFixAOSP2 && raw.load();
\r
431 if( X_HTMLAudio_currentTimeFix ){
\r
432 this._currentFixBegin = begin;
\r
433 this._currentFixStart = X_Timer_now();
\r
437 actualPause : function(){
\r
438 console.log( '[HTMLAudio] pause' );
\r
440 this.seekTime = this.getActualCurrentTime();
\r
442 delete this._currentFixStart;
\r
444 !this[ '_rawObject' ].error && this[ '_rawObject' ].pause();
\r
446 if( X_HTMLAudio_pauseFix ){
\r
447 this[ '_rawObject' ].src = '';
\r
448 if( X_HTMLAudio_durationFix ){
\r
449 delete this._durationFixPhase;
\r
450 delete this._durationFixSkip;
\r
453 delete this.playing;
\r
456 getActualCurrentTime : function(){
\r
457 return ( X_HTMLAudio_currentTimeFix ?
\r
458 X_Timer_now() - this._currentFixStart + this._currentFixBegin :
\r
459 this[ '_rawObject' ].currentTime * 1000 | 0 );
\r
462 http://www.w3schools.com/tags/av_prop_error.asp
\r
463 1 = MEDIA_ERR_ABORTED - fetching process aborted by user
\r
464 2 = MEDIA_ERR_NETWORK - error occurred when downloading
\r
465 3 = MEDIA_ERR_DECODE - error occurred when decoding
\r
466 4 = MEDIA_ERR_SRC_NOT_SUPPORTED - audio/video not supported
\r
468 getActualError : function(){
\r
469 return this[ '_rawObject' ].error || 0;
\r
472 afterUpdateState : function( result ){
\r
473 if( result & 3 ){ // seek
\r
476 if( ( result & 4 ) && X_HTMLAudio_volumeEnabled ){
\r
477 this[ '_rawObject' ].volume = this.gain;
\r
484 X_HTMLAudio && X_Audio_BACKENDS.push(
\r
488 backendName : 'HTMLAudio',
\r
490 canPlay : X_Audio_codecs,
\r
492 * HTML5 の audio 要素と video 要素でサポートされているメディアフォーマット
\r
493 * https://developer.mozilla.org/ja/docs/Web/HTML/Supported_media_formats
\r
495 * 主要ブラウザのHTML5 audioタグで使えるファイル形式の再生対応状況を調べてみた
\r
496 * http://sothis.blog.so-net.ne.jp/2010-10-27
\r
497 * ダメ元で仕様に含まれていない SHOUTcast もテストしてみました。
\r
499 * IE9 の HTML5 Audio について
\r
500 * http://kentablog.cluscore.com/2011/05/ie9-html5-audio.html
\r
501 * 1.Audioオブジェクトを作ることができないので、Audioタグを使う
\r
502 * 2.クロスドメインアクセスには、「clientaccesspolicy.xml」か「crossdomain.xml」が必要
\r
505 * IE9でHTML5 autio タグが無効になる
\r
506 * http://bbs.wankuma.com/index.cgi?mode=al2&namber=64886&KLOG=109
\r
507 * IEのバージョン9.0.8112.16421では、Audioオブジェクトのnewも対応してました。
\r
508 * createElement等で動的生成すると、よろしくない
\r
510 * media-can-play-wav-audio.html
\r
511 * https://github.com/adobe/webkit/blob/master/LayoutTests/media/media-can-play-wav-audio.html
\r
512 * testExpected("audio.canPlayType('audio/wav; codecs=1')", "probably");
\r
514 * HTML5 audioタグ ブラウザ間の違い
\r
515 * 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
516 * - volume, muted iPhone(iOS4-6)、Android(2.3.6)では動作せず。
\r
517 * - FireFox3.6, Android 2.3.6については、src変更後、load()を呼び出さないと切り替わらなかった。iPhoneはload()が不要。
\r
519 detect : function( proxy, source, ext ){
\r
520 proxy[ 'asyncDispatch' ]( { type : X_EVENT_COMPLETE, canPlay : X_Audio_codecs[ ext ] } );
\r
523 klass : X_HTMLAudio
\r
531 mp3: !!audioTest.canPlayType('audio/mpeg;').replace(/^no$/, ''),
\r
532 opus: !!audioTest.canPlayType('audio/ogg; codecs="opus"').replace(/^no$/, ''),
\r
533 ogg: !!audioTest.canPlayType('audio/ogg; codecs="vorbis"').replace(/^no$/, ''),
\r
534 wav: !!audioTest.canPlayType('audio/wav; codecs="1"').replace(/^no$/, ''),
\r
535 aac: !!audioTest.canPlayType('audio/aac;').replace(/^no$/, ''),
\r
536 m4a: !!(audioTest.canPlayType('audio/x-m4a;') || audioTest.canPlayType('audio/m4a;') || audioTest.canPlayType('audio/aac;')).replace(/^no$/, ''),
\r
537 mp4: !!(audioTest.canPlayType('audio/x-mp4;') || audioTest.canPlayType('audio/mp4;') || audioTest.canPlayType('audio/aac;')).replace(/^no$/, ''),
\r
538 weba: !!audioTest.canPlayType('audio/webm; codecs="vorbis"').replace(/^no$/, '')
\r