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_HTML5Audio, X_Audio_HTML5AudioWrapper, X_Audio_rawAudio,
\r
8 X_Audio_HTML5Audio_LIVE_LIST = [],
\r
9 X_Audio_HTML5Audio_POOL_LIST = [];
\r
12 if( window.HTMLAudioElement ){
\r
13 function getHTML5AudioWrapper( proxy ){
\r
14 var i = X_Audio_HTML5Audio_LIVE_LIST.length;
\r
16 if( X_Audio_HTML5Audio_LIVE_LIST[ --i ].proxy === proxy ) return X_Audio_HTML5Audio_LIVE_LIST[ i ];
\r
20 X_Audio_HTML5Audio = X_Class_override(
\r
21 new X.EventDispatcher(),
\r
23 backendName : 'HTML5 Audio',
\r
25 * HTML5 の audio 要素と video 要素でサポートされているメディアフォーマット
\r
26 * https://developer.mozilla.org/ja/docs/Web/HTML/Supported_media_formats
\r
28 * 主要ブラウザのHTML5 audioタグで使えるファイル形式の再生対応状況を調べてみた
\r
29 * http://sothis.blog.so-net.ne.jp/2010-10-27
\r
30 * ダメ元で仕様に含まれていない SHOUTcast もテストしてみました。
\r
32 * IE9 の HTML5 Audio について
\r
33 * http://kentablog.cluscore.com/2011/05/ie9-html5-audio.html
\r
34 * 1.Audioオブジェクトを作ることができないので、Audioタグを使う
\r
35 * 2.クロスドメインアクセスには、「clientaccesspolicy.xml」か「crossdomain.xml」が必要
\r
38 * IE9でHTML5 autio タグが無効になる
\r
39 * http://bbs.wankuma.com/index.cgi?mode=al2&namber=64886&KLOG=109
\r
40 * IEのバージョン9.0.8112.16421では、Audioオブジェクトのnewも対応してました。
\r
41 * createElement等で動的生成すると、よろしくない
\r
43 * media-can-play-wav-audio.html
\r
44 * https://github.com/adobe/webkit/blob/master/LayoutTests/media/media-can-play-wav-audio.html
\r
45 * testExpected("audio.canPlayType('audio/wav; codecs=1')", "probably");
\r
47 * HTML5 audioタグ ブラウザ間の違い
\r
48 * 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
49 * - volume, muted iPhone(iOS4-6)、Android(2.3.6)では動作せず。
\r
50 * - FireFox3.6, Android 2.3.6については、src変更後、load()を呼び出さないと切り替わらなかった。iPhoneはload()が不要。
\r
52 detect : function( source, ext ){
\r
53 var ok, mineType = 'audio/' + ext;
\r
56 ok = X_UA.IE || X_UA.Chrome || X_UA.Safari; //( X_UA.OS === 'windows' && X_UA.Safari );
\r
57 mineType = 'audio/mpeg';
\r
60 ok = 15 <= X_UA.Gecko || X_UA.Chrome || X_UA.Opera;
\r
63 ok = X_UA.IE || X_UA.WebKit;
\r
64 mineType = 'audio/mp4';
\r
67 ok = 2 <= X_UA.Gecko || 10.6 <= X_UA.Opera; // firefox4+(Gecko2+)
\r
70 ok = X_UA.Gecko || X_UA.Opera || X_UA.Safari; //( X_UA.OS === 'windows' && X_UA.Safari );
\r
71 //mineType = 'audio/wav'; // audio/x-wav ?
\r
77 if( !ok && mineType ){
\r
78 if( !X_Audio_rawAudio ) X_Audio_rawAudio = new Audio;
\r
79 ok = X_Audio_rawAudio.canPlayType( mineType );
\r
82 this.asyncDispatch( ok ? 'support' : 'nosupport' );
\r
87 register : function( proxy, source, option ){
\r
88 X_Audio_HTML5Audio_LIVE_LIST.push( new X_Audio_HTML5AudioWrapper( proxy, source, option ) );
\r
92 return getHTML5AudioWrapper( this ).close();
\r
95 play : function( position ){
\r
96 return getHTML5AudioWrapper( this ).play( position );
\r
100 return getHTML5AudioWrapper( this ).pause();
\r
104 return getHTML5AudioWrapper( this ).stop();
\r
107 loop : function( v ){
\r
108 return getHTML5AudioWrapper( this ).loop( v );
\r
111 state : function(){
\r
112 return getHTML5AudioWrapper( this ).state();
\r
115 volume : function( v ){
\r
116 return getHTML5AudioWrapper( this ).volume( v );
\r
119 startTime : function( time ){
\r
120 return getHTML5AudioWrapper( this ).startTime( time );
\r
123 currentTime : function( time ){
\r
124 return getHTML5AudioWrapper( this ).currentTime( time );
\r
127 isPlaying : function(){
\r
128 return getHTML5AudioWrapper( this ).isPlaying();
\r
133 X_Audio_BACKENDS.push( X_Audio_HTML5Audio );
\r
135 X_Audio_HTML5AudioWrapper = X.EventDispatcher.inherits(
\r
136 'X.AV.HTML5AudioWrapper',
\r
137 X.Class.POOL_OBJECT,
\r
143 _lastUserAction : '',
\r
150 Constructor : function( proxy, source, option ){
\r
151 this.proxy = proxy;
\r
152 this._closed = false;
\r
154 if( option.loop ) this._loop = true;
\r
155 if( option.startTime ) this._startTime = option.startTime;
\r
156 if( option.volume ) this._volume = option.volume;
\r
158 this._rawObject = X_Audio_rawAudio || new Audio( source );//X.Node.create( 'audio', { src : source } ).appendToRoot();//( X.X_Node_systemNode );
\r
161 'loadstart', 'load', 'progress', 'suspend', 'abort', 'error', 'emptied', 'stalled', 'play', 'pause', 'loadedmetadata',
\r
162 'loadeddata', 'waiting', 'playing', 'canplay', 'canplaythrough', 'seeking', 'seeked', 'timeupdate', 'ended',
\r
163 'ratechange', 'durationchange', 'volumechange' ], this.handleEventProxy );
\r
165 if( X_Audio_rawAudio ){
\r
166 X_Audio_rawAudio.src = source;
\r
167 X_Audio_rawAudio.load(); // 要る?
\r
168 X_Audio_rawAudio = null;
\r
171 //document.body.appendChild( this._rawObject );
\r
173 this._rawObject.volume = this._volume;
\r
174 this._rawObject.autoplay = false;
\r
175 option.autoplay && X.Timer.once( 100, this, this.play );
\r
177 this.listenOnce( X.Event.KILL_INSTANCE );
\r
180 handleEvent : function( e ){
\r
183 case X.Event.KILL_INSTANCE :
\r
188 * http://uguisu.skr.jp/html/table3.html
\r
190 handleEventProxy : function( e ){
\r
191 console.log(e.type);
\r
194 case 'loadstart' : // ブラウザがコンテンツの検索を開始した場合に発生
\r
196 case 'progress' : // ブラウザがコンテンツの取得を実行した場合に発生
\r
197 //console.log( e.loaded + ' ' + e.total * 100 + '%' );
\r
198 console.log( this._rawObject.buffered.end(0) / this._rawObject.duration * 100 + '%' );
\r
200 case 'suspend' : // ブラウザが意図的にコンテンツの取得を現在行っていない場合に発生(ダウンロードは未完了)
\r
201 case 'abort' : // ダウンロードの完了前にコンテンツの取得を停止した場合に発生(この停止はエラーによるものではない)
\r
202 case 'error' : // コンテンツの取得実行中にエラーが発生した場合に発生
\r
203 case 'emptied' : // 読み込み中に致命的なエラーが発生したか、実行状態ででload()メソッドが実行された場合に発生
\r
204 case 'stalled' : // ブラウザがコンテンツの取得を試みたが、データがまだ用意されていない場合に発生
\r
205 case 'play' : // 再生が開始された。play()メソッドからの復帰後に発生する場合に発生
\r
206 case 'pause' : // 再生が一時停止された。pauseメソッドからの復帰後に発生する場合に発生
\r
207 case 'loadedmetadata' : // ブラウザがメディアリソースの長さと寸法を判定した場合に発生
\r
208 case 'loadeddata' : // コンテンツの表示を現在の再生位置で初めて行えるようになった場合に発生
\r
209 case 'waiting' : // 次のフレームが利用不可のため再生を停止したが、そのフレームがやがて利用可能になると想定している場合に発生
\r
210 case 'playing' : // 再生が開始された場合に発生
\r
211 case 'canplay' : // 今すぐに再生を再開できるが、バッファリングが不十分でコンテンツを最後まで表示できないと予測している場合に発生
\r
212 case 'canplaythrough' : // 今すぐに再生を開始してもバッファリングで停止することなく最後まで表示できると予測している場合に発生
\r
213 case 'seeking' : // シークがtrueに変化し、イベントを発生させるのに十分な時間がシーク操作にかかっている場合に発生
\r
214 case 'seeked' : // シークがfalseに変化した場合に発生
\r
215 case 'timeupdate' : // 通常の再生が行われ現在の再生位置の変化が起こった場合に発生
\r
219 //!this._closed && this._lastUserAction !== 'stop' && this._loop && this.play();
\r
222 case 'ratechange' : // defaultPlaybackRate属性とplaybackRate属性のどちらかが更新された場合に発生
\r
223 case 'durationchange' : // duration属性が更新された場合に発生
\r
224 case 'volumechange' : // volume属性とmuted属性のどちらかが変化した場合に発生
\r
226 //console.log( 'html5 ' + e.type + ' ' + ( this.proxy._listeners && this.proxy._listeners[ e.type ] && this.proxy._listeners[ e.type ].length ) );
\r
227 //e.type === 'canplaythrough' && console.dir( e );
\r
228 this.proxy.dispatch( e );
\r
231 close : function(){
\r
236 play : function( position ){
\r
237 // もし kill 後に autoplayTimer で呼ばれても、_closed==true なので平気
\r
238 if( this._closed ) return;
\r
239 this._lastUserAction = 'play';
\r
241 if( X_UA.Chrome ){ // [CHROME][FIX] volume TODO どの version で 修正される?
\r
243 X.Timer.once( 0, this, this._fixForChrome, [ this._rawObject.volume ] );
\r
244 this._rawObject.volume = 0;
\r
247 if( !this._rawObject.paused ){
\r
248 this.currentTime( this._startTime );
\r
250 this._rawObject.play();
\r
253 // [CHROME][FIX] volume
\r
254 _fixForChrome : X_UA.Chrome && function( volume ){
\r
255 !this._closed && ( this._rawObject.volume = volume );
\r
258 pause : function(){
\r
259 if( !this._closed && !this._rawObject.error ){
\r
260 this._lastUserAction = 'pause';
\r
261 this._rawObject.pause();
\r
266 if( !this._closed && !this._rawObject.error ){
\r
267 this._lastUserAction = 'stop';
\r
268 this._rawObject.pause();
\r
269 this.currentTime( this._startTime );
\r
273 loop : function( v ){
\r
274 if( v === undefined ) return this._loop;
\r
275 this._rawObject.loop = this._loop = v;
\r
278 state : function(){
\r
279 var paused = !!this._rawObject.paused,
\r
280 ended = !!this._rawObject.ended;
\r
282 if( this._lastUserAction === 'stop' ){
\r
290 loop : this._rawObject.loop,
\r
291 error : this._rawObject.error || 0, // 0, 1 ~ 4
\r
294 source : this._rawObject.src || '',
\r
295 duration : this._rawObject.duration || 0
\r
299 volume : function( v ){
\r
300 if( v === undefined ) return this.audio.volume;
\r
301 this._rawObject.volume = v;
\r
304 startTime : function( time ){
\r
305 if( time === undefined ) return this._startTime;
\r
306 this._startTime = time;
\r
309 currentTime : function( time ){
\r
310 if( time === undefined ) return this._rawObject.currentTime;
\r
311 this._rawObject.currentTime = time;
\r
314 isPlaying : function(){
\r
315 return !this._rawObject.error && !this._rawObject.paused && !this._rawObject.ended;
\r