1 /**
  2  * Hilo
  3  * Copyright 2015 alibaba.com
  4  * Licensed under the MIT License
  5  */
  6 
  7 /**
  8  * @class HTMLAudio is an audio playing module, which uses HTMLAudioElement to play audio.
  9  * Limits: iOS platform requires user action events to start playing, and many Android browser can only play one audio at a time.
 10  * @param {Object} properties create object properties, include all writable properties of this class.
 11  * @module hilo/media/HTMLAudio
 12  * @requires hilo/core/Class
 13  * @requires hilo/util/util
 14  * @requires hilo/event/EventMixin
 15  * @property {String} src The source of the playing audio.
 16  * @property {Boolean} loop Is loop playback, default value is false.
 17  * @property {Boolean} autoPlay Is the audio autoplay, default value is false.
 18  * @property {Boolean} loaded Is the audio resource loaded, readonly!
 19  * @property {Boolean} playing Is the audio playing, readonly!
 20  * @property {Number} duration The duration of the audio, readonly!
 21  * @property {Number} volume The volume of the audio, value between 0 to 1.
 22  * @property {Boolean} muted Is the audio muted, default value is false.
 23  */
 24 var HTMLAudio = Class.create(/** @lends HTMLAudio.prototype */{
 25     Mixes: EventMixin,
 26     constructor: function(properties){
 27         util.copy(this, properties, true);
 28 
 29         this._onAudioEvent = this._onAudioEvent.bind(this);
 30     },
 31 
 32     src: null,
 33     loop: false,
 34     autoPlay: false,
 35     loaded: false,
 36     playing: false,
 37     duration: 0,
 38     volume: 1,
 39     muted: false,
 40 
 41     _element: null, //HTMLAudioElement对象
 42 
 43     /**
 44      * Load audio file.
 45      */
 46     load: function(){
 47         if(!this._element){
 48             var elem;
 49             try{
 50                 elem = this._element = new Audio();
 51                 elem.addEventListener('canplaythrough', this._onAudioEvent, false);
 52                 elem.addEventListener('ended', this._onAudioEvent, false);
 53                 elem.addEventListener('error', this._onAudioEvent, false);
 54                 elem.src = this.src;
 55                 elem.volume = this.volume;
 56                 elem.load();
 57             }
 58             catch(err){
 59                 //ie9 某些版本有Audio对象,但是执行play,pause会报错!
 60                 elem = this._element = {};
 61                 elem.play = elem.pause = function(){
 62 
 63                 };
 64             }
 65         }
 66         return this;
 67     },
 68 
 69     /**
 70      * @private
 71      */
 72     _onAudioEvent: function(e){
 73         // console.log('onAudioEvent:', e.type);
 74         var type = e.type;
 75 
 76         switch(type){
 77             case 'canplaythrough':
 78                 e.target.removeEventListener(type, this._onAudioEvent);
 79                 this.loaded = true;
 80                 this.duration = this._element.duration;
 81                 this.fire('load');
 82                 if(this.autoPlay) this._doPlay();
 83                 break;
 84             case 'ended':
 85                 this.playing = false;
 86                 this.fire('end');
 87                 if(this.loop) this._doPlay();
 88                 break;
 89             case 'error':
 90                 this.fire('error');
 91                 break;
 92         }
 93     },
 94 
 95     /**
 96      * @private
 97      */
 98     _doPlay: function(){
 99         if(!this.playing){
100             this._element.volume = this.muted ? 0 : this.volume;
101             this._element.play();
102             this.playing = true;
103         }
104     },
105 
106     /**
107      * Start playing the audio. And play the audio from the beginning if the audio is already playing.
108      * Note: To prevent failing to play at the first time, play when the audio is loaded.
109      */
110     play: function(){
111         if(this.playing) this.stop();
112 
113         if(!this._element){
114             this.autoPlay = true;
115             this.load();
116         }else if(this.loaded){
117             this._doPlay();
118         }
119 
120         return this;
121     },
122 
123     /**
124      * Pause (halt) the currently playing audio.
125      */
126     pause: function(){
127         if(this.playing){
128             this._element.pause();
129             this.playing = false;
130         }
131         return this;
132     },
133 
134     /**
135      * Continue to play the audio.
136      */
137     resume: function(){
138         if(!this.playing){
139             this._doPlay();
140         }
141         return this;
142     },
143 
144     /**
145      * Stop playing the audio.
146      */
147     stop: function(){
148         if(this.playing){
149             this._element.pause();
150             this._element.currentTime = 0;
151             this.playing = false;
152         }
153         return this;
154     },
155 
156     /**
157      * Set the volume. Note: iOS devices cannot set volume.
158      */
159     setVolume: function(volume){
160         if(this.volume != volume){
161             this.volume = volume;
162             this._element.volume = volume;
163         }
164         return this;
165     },
166 
167     /**
168      * Set mute mode. Note: iOS devices cannot set mute mode.
169      */
170     setMute: function(muted){
171         if(this.muted != muted){
172             this.muted = muted;
173             this._element.volume = muted ? 0 : this.volume;
174         }
175         return this;
176     },
177 
178     Statics: /** @lends HTMLAudio */ {
179         /**
180          * Does the browser supports HTMLAudio.
181          */
182         isSupported: window.Audio !== null
183     }
184 
185 });