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
7 var X_Audio_HTMLAudio_playTrigger =
\r
8 6 <= X_UA[ 'iOS' ] ? 'loadeddata' :
\r
9 X_UA[ 'iOS' ] < 5 ? 'stalled' :
\r
10 X_UA[ 'iOS' ] ? 'suspend' :
\r
11 X_UA[ 'AndroidBrowser2' ] || X_UA[ 'AndroidBrowser3' ] ? 'stalled' : // Android 2.3.5(SBM101SH) では stalled は発生しない,,,
\r
12 X_UA[ 'AndroidBrowser4' ] ? 'loadeddata' :
\r
13 X_UA[ 'OperaMobile' ] || X_UA[ 'OperaTablet' ] ? 'loadeddata' :
\r
14 //X_UA[ 'WinPhone' ] && X_UA[ 'IE9' ] ? 'canplay' :
\r
15 'loadeddata', //'canplay',
\r
16 X_Audio_HTMLAudioWrapper,
\r
17 X_Audio_constructor = window[ 'Audio' ] || window.HTMLAudioElement,
\r
19 // Opera Mobile 12 android4.4.4 & 2.3.5 は 2回目以降の currentTime へのセットで currentTime が更新されなくなるため、タイマーを使用する
\r
20 X_Audio_HTMLAudioWrapper_currentTimeFix = !!X_UA[ 'OperaMobile' ] || !!X_UA[ 'OperaTablet' ], // || ( X_UA[ 'WinPhone' ] && X_UA[ 'IE9' ] ),
\r
21 // Android1.6+MobileOpera12では無理っぽい、、、
\r
22 X_Audio_HTMLAudioWrapper_badOperaAndroid = X_Audio_HTMLAudioWrapper_currentTimeFix && X_UA[ 'Android' ] < 2,
\r
24 // 一方 Desktop の Opera12 は、loadeddata 等では duration が infinity で、再生後の durationchange 時に duration が判明する。
\r
25 // opera12 volume, mute の変更が2度目以降できない
\r
26 // duration 判明後には currentTime によるシークと、現在時間の取得が可能になる。
\r
27 // canplay で play() durationchange で duration が取れたら loadedmetadata->loadeddata -> canplay する
\r
28 // boombox.js に書いてあった currentTime の効かないブラウザってこいつのことみたい、、、
\r
29 // Opera12.17 Win32(XP) portable apps は勝手に再生が始まる、、、その際には timeupdate が発行されない、、、 iframe+image+audio で使わないときは破棄する、とか。
\r
30 // opera11、10.54 WinXP はまとも、、、
\r
31 // X_Audio_Sprite_handleEvent でも使用
\r
32 X_Audio_HTMLAudioWrapper_ieMobile9Fix = ( X_UA[ 'WinPhone' ] && X_UA[ 'IE9' ] ),
\r
33 X_Audio_HTMLAudioWrapper_durationFix = ( !X_Audio_HTMLAudioWrapper_currentTimeFix && 12 <= X_UA[ 'Opera' ] ),
\r
35 X_Audio_HTMLAudioWrapper_shortPlayFix = X_UA[ 'AndroidBrowser' ] && X_UA[ 'AndroidWebkit' ] <= 534.3, // Android 4.1.1 でも遭遇
\r
39 if( X_Audio_constructor && !X_Audio_HTMLAudioWrapper_badOperaAndroid ){
\r
40 //http://himaxoff.blog111.fc2.com/blog-entry-97.html
\r
41 //引数なしで new Audio() とすると、Operaでエラーになるそうなので注意。
\r
42 X_Audio_rawAudio = new X_Audio_constructor( '' );
\r
44 // https://html5experts.jp/miyuki-baba/3766/
\r
45 // Chrome for Android31 で HE-AAC が低速再生されるバグ
\r
46 if( X_Audio_rawAudio.canPlayType ){
\r
48 'mp3' : X_Audio_rawAudio.canPlayType('audio/mpeg'),
\r
49 'opus' : X_Audio_rawAudio.canPlayType('audio/ogg; codecs="opus"'),
\r
50 'ogg' : X_Audio_rawAudio.canPlayType('audio/ogg; codecs="vorbis"'),
\r
51 'wav' : X_Audio_rawAudio.canPlayType('audio/wav; codecs="1"'),
\r
52 'aac' : X_Audio_rawAudio.canPlayType('audio/aac'),
\r
53 'm4a' : X_Audio_rawAudio.canPlayType('audio/x-m4a') + X_Audio_rawAudio.canPlayType('audio/m4a') + X_Audio_rawAudio.canPlayType('audio/aac'),
\r
54 'mp4' : X_Audio_rawAudio.canPlayType('audio/x-mp4') + X_Audio_rawAudio.canPlayType('audio/mp4') + X_Audio_rawAudio.canPlayType('audio/aac'),
\r
55 'weba' : X_Audio_rawAudio.canPlayType('audio/webm; codecs="vorbis"')
\r
58 for( k in X_Audio_codecs ){
\r
59 if( X_EMPTY_OBJECT[ k ] ) continue;
\r
60 v = X_Audio_codecs[ k ];
\r
61 X_Audio_codecs[ k ] = v && v.split( 'no' ).join( '' );
\r
62 console.log( k + ' ' + X_Audio_codecs[ k ] );
\r
68 'mp3' : X_UA[ 'IE' ] || X_UA[ 'Chrome' ] || ( X_UA[ 'Windows' ] && X_UA[ 'Safari' ] ),
\r
69 'ogg' : 5 <= X_UA[ 'Gecko' ] || X_UA[ 'Chrome' ] || X_UA[ 'Opera' ] ,
\r
70 'wav' : X_UA[ 'Gecko' ] || X_UA[ 'Opera' ] || ( X_UA[ 'Windows' ] && X_UA[ 'Safari' ] ),
\r
71 'aac' : X_UA[ 'IE' ] || X_UA[ 'WebKit' ],
\r
72 'm4a' : X_UA[ 'IE' ] || X_UA[ 'WebKit' ],
\r
73 'mp4' : X_UA[ 'IE' ] || X_UA[ 'WebKit' ],
\r
74 'weba' : 2 <= X_UA[ 'Gecko' ] || 10.6 <= X_UA[ 'Opera' ] // firefox4+(Gecko2+)
\r
78 X_Audio_HTMLAudioWrapper = X_Audio_AbstractAudioBackend[ 'inherits' ](
\r
79 'X.AV.HTML5AudioWrapper',
\r
80 X_Class.POOL_OBJECT,
\r
86 _playForDuration : 0,
\r
87 _lastCurrentTime : 0,
\r
90 Constructor : function( target, source, option ){
\r
93 this.target = target || this;
\r
94 this._closed = false;
\r
96 this.setState( option );
\r
98 if( option[ 'useVideo' ] ){
\r
99 this[ '_rawObject' ] = raw = document.createElement( 'video' );
\r
100 raw.preload = 'none'; // auto, metadata, none
\r
101 //raw.autoplay = false, // no-auto
\r
104 raw.crossorigin = option[ 'crossorigin' ] || ''; //crossorigin: "anonymous", X.URL.isSameDomain() で切り替え
\r
105 raw.style.cssText = 'position:absolute;bottom:0;left:-50px;width:100px;height:100px;opacity:0;';
\r
106 raw.controls = false;
\r
107 raw.WebKitPlaysInline = true;
\r
109 //raw.onclick = "alert('play');this.actualPlay();";
\r
110 document.body.appendChild( raw );
\r
113 this[ '_rawObject' ] = X_Audio_rawAudio || new X_Audio_constructor( source );
\r
114 // X_Doc_create( 'audio', { src : source } )[ 'appendTo' ]( X.Doc.body );
\r
115 this[ '_rawObject' ].autobuffer = false;
\r
116 this._src = source;
\r
119 this[ 'listen' ]( [
\r
120 'loadstart', 'load', 'progress', 'suspend', 'abort', 'error', 'emptied', 'stalled', 'play', 'pause', 'loadedmetadata',
\r
121 'loadeddata', 'waiting', 'playing', 'canplay', 'canplaythrough', 'seeking', 'seeked', 'timeupdate', 'ended',
\r
122 'ratechange', 'durationchange', 'volumechange' ], this.handleEventProxy );
\r
124 //document.body.appendChild( this[ '_rawObject' ] );
\r
125 this[ '_rawObject' ].autoplay = false;
\r
127 if( X_Audio_rawAudio === this[ '_rawObject' ] ){
\r
128 if( X_Audio_HTMLAudioWrapper_badOperaAndroid ){
\r
129 X_Audio_HTMLAudioWrapper_badOperaAndroid && alert( 12 );
\r
130 X_EventDispatcher_toggleAllEvents( this, false );
\r
131 this[ '_rawObject' ] = new X_Audio_constructor( X_URL_toAbsolutePath( source ) );
\r
132 //X_EventDispatcher_toggleAllEvents( this, true );
\r
134 X_Audio_rawAudio.src = source;
\r
137 //this[ '_rawObject' ] = new X_Audio_constructor( X_URL_toAbsolutePath( source ) );
\r
138 /*!X_Audio_Sprite_needTouchFirst && */ X_Audio_rawAudio.load(); // 要る?
\r
139 X_Audio_rawAudio = null;
\r
142 this[ 'listenOnce' ]( X_EVENT_KILL_INSTANCE );
\r
145 handleEvent : function( e ){
\r
148 case X_EVENT_KILL_INSTANCE :
\r
149 // 【javascript】モバイル向けブラウザでも音を鳴らしたい【WebAudio】
\r
150 // http://ingaouhou.com/archives/3633
\r
151 // ・使い終わったインスタンスはload()しておくとやや安定
\r
152 this.playing && this.actualPause();
\r
153 delete this._closed;
\r
154 delete this._loaded;
\r
156 this[ '_rawObject' ].src = '';
\r
157 this[ '_rawObject' ].load();
\r
159 // removeChild for video
\r
164 * http://uguisu.skr.jp/html/table3.html
\r
166 handleEventProxy : function( e ){
\r
167 var type, loaded, end, now;
\r
169 X_Audio_HTMLAudioWrapper_badOperaAndroid && alert( e.type );
\r
171 X_Audio_HTMLAudioWrapper_ieMobile9Fix && e.type !== 'timeupdate' && console.log( e.type );
\r
174 case 'loadstart' : // ブラウザがコンテンツの検索を開始した場合に発生
\r
176 case 'progress' : // ブラウザがコンテンツの取得を実行した場合に発生
\r
177 console.log( e.loaded + ' ' + e.total * 100 + '%' );
\r
178 // iem9 で常に0 this[ '_rawObject' ].networkState;
\r
179 // opera Android 12 で buffered.end() へのアクセスはエラー try catch も無効、iem9 は常に end(0) = 0
\r
180 //console.log( 'buffered.end ' + this[ '_rawObject' ].buffered && this[ '_rawObject' ].buffered.end(0) );
\r
183 case 'canplay' : // 今すぐに再生を再開できるが、バッファリングが不十分でコンテンツを最後まで表示できないと予測している場合に発生
\r
184 if( X_Audio_HTMLAudioWrapper_durationFix && this._playForDuration === 0 ){
\r
185 //console.log( 'DurationFix開始 - ' + this[ '_rawObject' ].duration );
\r
186 this._playForDuration = 1;
\r
187 this[ '_rawObject' ].play();
\r
188 this[ '_rawObject' ].currentTime = this._beginTime / 1000; // 必要!
\r
191 case 'loadedmetadata' : // ブラウザがメディアリソースの長さと寸法を判定した場合に発生
\r
192 case 'loadeddata' : // コンテンツの表示を現在の再生位置で初めて行えるようになった場合に発生
\r
193 case 'canplaythrough' : // 今すぐに再生を開始してもバッファリングで停止することなく最後まで表示できると予測している場合に発生
\r
194 if( X_Audio_HTMLAudioWrapper_durationFix && this._playForDuration !== 2 ) return;
\r
195 this.duration = this.duration || this[ '_rawObject' ].duration * 1000;
\r
198 case 'stalled' : // ブラウザがコンテンツの取得を試みたが、データがまだ用意されていない場合に発生
\r
199 // Android2 で ready 扱い?
\r
200 case 'suspend' : // ブラウザが意図的にコンテンツの取得を現在行っていない場合に発生(ダウンロードは未完了)
\r
202 case 'emptied' : // 読み込み中に致命的なエラーが発生したか、実行状態ででload()メソッドが実行された場合に発生
\r
203 case 'abort' : // ダウンロードの完了前にコンテンツの取得を停止した場合に発生(この停止はエラーによるものではない)
\r
206 case 'error' : // コンテンツの取得実行中にエラーが発生した場合に発生
\r
207 type = X_EVENT_ERROR;
\r
210 case 'playing' : // 再生が開始された場合に発生
\r
211 if( X_Audio_HTMLAudioWrapper_currentTimeFix ){
\r
212 this._playTime = X_Timer_now();
\r
214 type = X_EVENT_MEDIA_PLAYING;
\r
215 case 'play' : // 再生が開始された。play()メソッドからの復帰後に発生する場合に発生
\r
216 case 'pause' : // 再生が一時停止された。pauseメソッドからの復帰後に発生する場合に発生
\r
217 case 'seeked' : // シークがfalseに変化した場合に発生
\r
218 case 'ratechange' : // defaultPlaybackRate属性とplaybackRate属性のどちらかが更新された場合に発生
\r
219 case 'volumechange' : // volume属性とmuted属性のどちらかが変化した場合に発生
\r
220 if( this._playForDuration === 1 ) return;
\r
223 case 'waiting' : // 次のフレームが利用不可のため再生を停止したが、そのフレームがやがて利用可能になると想定している場合に発生
\r
224 type = X_EVENT_MEDIA_WAITING;
\r
225 case 'seeking' : // シークがtrueに変化し、イベントを発生させるのに十分な時間がシーク操作にかかっている場合に発生
\r
226 type = type || X_EVENT_MEDIA_SEEKING;
\r
227 if( this._playForDuration === 1 ) return;
\r
231 if( !this._closed && this.autoLoop ){
\r
232 if( !( this.target[ 'dispatch' ]( X_EVENT_MEDIA_BEFORE_LOOP ) & X_Callback_PREVENT_DEFAULT ) ){
\r
233 this.looped = true;
\r
234 this.target[ 'dispatch' ]( X_EVENT_MEDIA_LOOPED );
\r
239 type = X_EVENT_MEDIA_ENDED;
\r
241 delete this.playing;
\r
244 case 'timeupdate' : // 通常の再生が行われ現在の再生位置の変化が起こった場合に発生
\r
245 if( X_Audio_HTMLAudioWrapper_ieMobile9Fix ){
\r
246 if( this._playForDuration === 1 ){
\r
247 console.log( 'tu ' + this[ '_rawObject' ].duration );
\r
248 if( !this.duration && X_Type_isFinite( this[ '_rawObject' ].duration ) ){
\r
249 this.duration = this.duration || this[ '_rawObject' ].duration * 1000;
\r
250 this._playForDuration = 2;
\r
252 //console.log( 'durationFix が完了' + this.duration );
\r
255 this[ '_rawObject' ].currentTime = this._beginTime / 1000; // 必要!
\r
259 if( this[ '_rawObject' ].currentTime === this._lastCurrentTime ){
\r
260 //this.target[ 'dispatch' ]( 'seeking' );
\r
261 this.target[ 'dispatch' ]( X_EVENT_MEDIA_WAITING );
\r
264 this._lastCurrentTime = this[ '_rawObject' ].currentTime;
\r
266 this.duration = this.duration || this[ '_rawObject' ].duration * 1000;
\r
268 if( this.playing ){
\r
269 end = X_AudioWrapper_getEndTime( this );
\r
270 now = this.getActualCurrentTime();
\r
271 console.log( end + ' / ' + now );
\r
272 if( 0 + end <= 0 + now ){ // なぜか iem9 で必要,,,
\r
273 if( this.autoLoop ){
\r
274 if( !( this.target[ 'dispatch' ]( X_EVENT_MEDIA_BEFORE_LOOP ) & X_Callback_PREVENT_DEFAULT ) ){
\r
275 this.looped = true;
\r
276 this.target[ 'dispatch' ]( X_EVENT_MEDIA_LOOPED );
\r
280 this.actualPause();
\r
281 this.target[ 'dispatch' ]( X_EVENT_MEDIA_ENDED );
\r
288 type = X_EVENT_MEDIA_PLAYING;
\r
291 case 'durationchange' : // duration属性が更新された場合に発生
\r
293 if( !X_Audio_HTMLAudioWrapper_durationFix ){
\r
294 this.duration = this[ '_rawObject' ].duration * 1000;
\r
296 // Desktop Opera では Infinity, IEM9 では NaN
\r
297 if( !this.duration && X_Type_isFinite( this[ '_rawObject' ].duration ) ){
\r
299 //console.log( this[ '_rawObject' ].duration );
\r
301 this.duration = this[ '_rawObject' ].duration * 1000;
\r
303 if( this._playForDuration === 0 ) this._playForDuration = 2;
\r
305 if( this._playForDuration === 1 ){
\r
306 this._playForDuration = 2;
\r
308 console.log( 'Loaded ' + this._loaded );
\r
310 if( this._loaded ){
\r
311 this[ '_rawObject' ].currentTime = this._beginTime / 1000;
\r
312 console.log( '設定 ' + this._beginTime );
\r
317 console.log( 'durationFix が完了' + this.duration );
\r
319 if( this.autoplay ){
\r
320 this[ '_rawObject' ].currentTime = this._beginTime / 1000;
\r
322 // Opera12.17 WinXP で勝手に再生される不具合
\r
324 this[ '_rawObject' ].src = '';
\r
325 //this[ '_rawObject' ].load();
\r
335 if( !this._loaded && ( loaded || e.type === X_Audio_HTMLAudio_playTrigger || e.type === 'loadeddata' ) ){
\r
336 this.autoplay && X_Timer_once( 16, this, this.play );
\r
337 this._loaded = true;
\r
338 this.target[ 'asyncDispatch' ]( X_EVENT_READY );
\r
339 console.log( 'Loaded! ' + e.type + ' d:' + ( this.duration | 0 ) );
\r
343 if( !loaded && type ){
\r
344 this.target[ 'dispatch' ]( type );
\r
345 type === X_EVENT_ERROR && this[ 'kill' ]();
\r
349 actualPlay : function(){
\r
352 // もし kill 後に autoplayTimer で呼ばれても、_closed==true なので平気
\r
353 if( this._closed ) return;
\r
354 if( !this._loaded && !X_Audio_HTMLAudioWrapper_ieMobile9Fix /* && !X_Audio_Sprite_inTouchAction */ ){
\r
355 this.autoplay = true;
\r
359 if( X_Audio_HTMLAudioWrapper_ieMobile9Fix && this._playForDuration === 0 ){
\r
360 console.log( 'DurationFix開始 - ' + this[ '_rawObject' ].duration );
\r
361 this._playForDuration = 1;
\r
364 end = X_AudioWrapper_getEndTime( this );
\r
365 begin = this._beginTime = X_AudioWrapper_getStartTime( this, end, true );
\r
367 if( X_Audio_HTMLAudioWrapper_shortPlayFix ){
\r
368 begin -= end - begin > 1000 ? 200 : 400;
\r
369 begin = begin < 0 ? 0 : begin;
\r
372 if( !this[ '_rawObject' ].src ){
\r
373 this._beginTime = begin;
\r
374 this[ '_rawObject' ].src = this._src;
\r
375 delete this._playForDuration;
\r
378 if( !this.playing ){
\r
379 if( X_UA[ 'Chrome' ] ){ // [CHROME][FIX] volume TODO どの version で 修正される?
\r
381 X_Timer_once( 0, this, this._fixForChrome );
\r
382 this[ '_rawObject' ].volume = 0;
\r
384 this[ '_rawObject' ].volume = X_Audio_HTMLAudioWrapper_ieMobile9Fix ? 1 : this.gain;
\r
386 this[ '_rawObject' ].play();
\r
387 this.playing = true;
\r
389 //http://himaxoff.blog111.fc2.com/blog-entry-97.html
\r
390 //Firefox3.6では一度も play() していない状態で currentTime = 0 を実行するとエラーになる。
\r
391 //また、GoogleChrome7 では currentTime = 0 直後に play() すると、pause()した位置前後の音が混ざることがある。(少なくとも自分の環境では)
\r
392 this[ '_rawObject' ].currentTime = this._lastCurrentTime = begin / 1000;
\r
394 console.log( '[HTMLAudio] play ' + begin + ' -> ' + end );
\r
396 if( X_Audio_HTMLAudioWrapper_currentTimeFix ){
\r
397 this._beginTime = begin;
\r
398 this._playTime = X_Timer_now();
\r
403 // [CHROME][FIX] volume
\r
404 _fixForChrome : X_UA[ 'Chrome' ] && function(){
\r
405 !this._closed && ( this[ '_rawObject' ].volume = this.gain );
\r
408 actualPause : function(){
\r
409 if( !this.playing ) return;
\r
411 this.seekTime = this.getActualCurrentTime();
\r
413 delete this._playTime;
\r
415 !this[ '_rawObject' ].error && this[ '_rawObject' ].pause();
\r
417 if( X_Audio_HTMLAudioWrapper_durationFix ){
\r
418 this[ '_rawObject' ].src = '';
\r
421 delete this.playing;
\r
424 getActualCurrentTime : function(){
\r
425 return ( X_Audio_HTMLAudioWrapper_currentTimeFix ?
\r
426 X_Timer_now() - this._playTime + this._beginTime :
\r
427 this[ '_rawObject' ].currentTime * 1000 | 0 );
\r
430 http://www.w3schools.com/tags/av_prop_error.asp
\r
431 1 = MEDIA_ERR_ABORTED - fetching process aborted by user
\r
432 2 = MEDIA_ERR_NETWORK - error occurred when downloading
\r
433 3 = MEDIA_ERR_DECODE - error occurred when decoding
\r
434 4 = MEDIA_ERR_SRC_NOT_SUPPORTED - audio/video not supported
\r
436 getActualError : function(){
\r
437 return this[ '_rawObject' ].error || 0;
\r
440 afterUpdateState : function( result ){
\r
441 if( result & 3 ){ // seek
\r
445 this[ '_rawObject' ].volume = X_Audio_HTMLAudioWrapper_ieMobile9Fix ? 1 : this.gain;
\r
452 X_Audio_BACKENDS.push(
\r
454 backendName : 'HTML Audio',
\r
456 * HTML5 の audio 要素と video 要素でサポートされているメディアフォーマット
\r
457 * https://developer.mozilla.org/ja/docs/Web/HTML/Supported_media_formats
\r
459 * 主要ブラウザのHTML5 audioタグで使えるファイル形式の再生対応状況を調べてみた
\r
460 * http://sothis.blog.so-net.ne.jp/2010-10-27
\r
461 * ダメ元で仕様に含まれていない SHOUTcast もテストしてみました。
\r
463 * IE9 の HTML5 Audio について
\r
464 * http://kentablog.cluscore.com/2011/05/ie9-html5-audio.html
\r
465 * 1.Audioオブジェクトを作ることができないので、Audioタグを使う
\r
466 * 2.クロスドメインアクセスには、「clientaccesspolicy.xml」か「crossdomain.xml」が必要
\r
469 * IE9でHTML5 autio タグが無効になる
\r
470 * http://bbs.wankuma.com/index.cgi?mode=al2&namber=64886&KLOG=109
\r
471 * IEのバージョン9.0.8112.16421では、Audioオブジェクトのnewも対応してました。
\r
472 * createElement等で動的生成すると、よろしくない
\r
474 * media-can-play-wav-audio.html
\r
475 * https://github.com/adobe/webkit/blob/master/LayoutTests/media/media-can-play-wav-audio.html
\r
476 * testExpected("audio.canPlayType('audio/wav; codecs=1')", "probably");
\r
478 * HTML5 audioタグ ブラウザ間の違い
\r
479 * 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
480 * - volume, muted iPhone(iOS4-6)、Android(2.3.6)では動作せず。
\r
481 * - FireFox3.6, Android 2.3.6については、src変更後、load()を呼び出さないと切り替わらなかった。iPhoneはload()が不要。
\r
483 detect : function( proxy, source, ext ){
\r
485 var ok, mineType = 'audio/' + ext;
\r
488 ok = X_UA[ 'IE' ] || X_UA[ 'Chrome' ] || ( X_UA[ 'Windows' ] && X_UA[ 'Safari' ] );
\r
489 mineType = 'audio/mpeg';
\r
490 //if( X_UA[ 'Android' ] && X_UA[ 'Gecko' ] ) mineType = '';
\r
493 ok = 15 <= X_UA[ 'Gecko' ] || X_UA[ 'Chrome' ] || X_UA[ 'Opera' ] ;
\r
494 if( X_UA[ 'AndroidBrowser' ] ) mineType = '';
\r
497 ok = X_UA[ 'IE' ] || X_UA[ 'WebKit' ];
\r
498 mineType = 'audio/mp4';
\r
501 ok = 2 <= X_UA[ 'Gecko' ] || 10.6 <= X_UA[ 'Opera' ] ; // firefox4+(Gecko2+)
\r
504 ok = X_UA[ 'Gecko' ] || X_UA[ 'Opera' ] || ( X_UA[ 'Windows' ] && X_UA[ 'Safari' ] );
\r
505 //mineType = 'audio/wav'; // audio/x-wav ?
\r
511 if( !ok && mineType ){
\r
512 if( !X_Audio_rawAudio ) X_Audio_rawAudio = new Audio;
\r
513 ok = X_Audio_rawAudio.canPlayType( mineType );
\r
514 //console.log( 'HTML Audio ' + ok + ' ext:' + ext );
\r
516 console.log( 'HTML Audio ' + ok + ' ext:' + ext );
\r
519 proxy[ 'asyncDispatch' ]( { type : X_EVENT_COMPLETE, canPlay : X_Audio_codecs[ ext ] } );
\r
522 klass : X_Audio_HTMLAudioWrapper
\r
530 mp3: !!audioTest.canPlayType('audio/mpeg;').replace(/^no$/, ''),
\r
531 opus: !!audioTest.canPlayType('audio/ogg; codecs="opus"').replace(/^no$/, ''),
\r
532 ogg: !!audioTest.canPlayType('audio/ogg; codecs="vorbis"').replace(/^no$/, ''),
\r
533 wav: !!audioTest.canPlayType('audio/wav; codecs="1"').replace(/^no$/, ''),
\r
534 aac: !!audioTest.canPlayType('audio/aac;').replace(/^no$/, ''),
\r
535 m4a: !!(audioTest.canPlayType('audio/x-m4a;') || audioTest.canPlayType('audio/m4a;') || audioTest.canPlayType('audio/aac;')).replace(/^no$/, ''),
\r
536 mp4: !!(audioTest.canPlayType('audio/x-mp4;') || audioTest.canPlayType('audio/mp4;') || audioTest.canPlayType('audio/aac;')).replace(/^no$/, ''),
\r
537 weba: !!audioTest.canPlayType('audio/webm; codecs="vorbis"').replace(/^no$/, '')
\r