1 /****************************************************************************
  2  Copyright (c) 2008-2010 Ricardo Quesada
  3  Copyright (c) 2011-2012 cocos2d-x.org
  4  Copyright (c) 2013-2014 Chukong Technologies Inc.
  5 
  6  http://www.cocos2d-x.org
  7 
  8  Permission is hereby granted, free of charge, to any person obtaining a copy
  9  of this software and associated documentation files (the "Software"), to deal
 10  in the Software without restriction, including without limitation the rights
 11  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 12  copies of the Software, and to permit persons to whom the Software is
 13  furnished to do so, subject to the following conditions:
 14 
 15  The above copyright notice and this permission notice shall be included in
 16  all copies or substantial portions of the Software.
 17 
 18  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 19  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 20  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 21  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 22  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 23  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 24  THE SOFTWARE.
 25  ****************************************************************************/
 26 
 27 /**
 28  * Audio support in the browser
 29  *
 30  * MULTI_CHANNEL        : Multiple audio while playing - If it doesn't, you can only play background music
 31  * WEB_AUDIO            : Support for WebAudio - Support W3C WebAudio standards, all of the audio can be played
 32  * AUTOPLAY             : Supports auto-play audio - if Don‘t support it, On a touch detecting background music canvas, and then replay
 33  * REPLAY_AFTER_TOUCH   : The first music will fail, must be replay after touchstart
 34  * USE_EMPTIED_EVENT    : Whether to use the emptied event to replace load callback
 35  * DELAY_CREATE_CTX     : delay created the context object - only webAudio
 36  * NEED_MANUAL_LOOP     : loop attribute failure, need to perform loop manually
 37  *
 38  * May be modifications for a few browser version
 39  */
 40 (function(){
 41 
 42     var DEBUG = false;
 43 
 44     var sys = cc.sys;
 45     var version = sys.browserVersion;
 46 
 47     // check if browser supports Web Audio
 48     // check Web Audio's context
 49     var supportWebAudio = !!(window.AudioContext || window.webkitAudioContext || window.mozAudioContext);
 50 
 51     var support = {ONLY_ONE: false, WEB_AUDIO: supportWebAudio, DELAY_CREATE_CTX: false, ONE_SOURCE: false };
 52 
 53     if (sys.browserType === sys.BROWSER_TYPE_FIREFOX) {
 54         support.DELAY_CREATE_CTX = true;
 55         support.USE_LOADER_EVENT = 'canplay';
 56     }
 57 
 58     if (sys.os === sys.OS_ANDROID) {
 59         if (sys.browserType === sys.BROWSER_TYPE_UC) {
 60             support.ONE_SOURCE = true;
 61         }
 62     }
 63 
 64     window.__audioSupport = support;
 65 
 66     if(DEBUG){
 67         setTimeout(function(){
 68             cc.log("browse type: " + sys.browserType);
 69             cc.log("browse version: " + version);
 70             cc.log("MULTI_CHANNEL: " + window.__audioSupport.MULTI_CHANNEL);
 71             cc.log("WEB_AUDIO: " + window.__audioSupport.WEB_AUDIO);
 72             cc.log("AUTOPLAY: " + window.__audioSupport.AUTOPLAY);
 73         }, 0);
 74     }
 75 
 76 })();
 77 
 78 /**
 79  * Encapsulate DOM and webAudio
 80  */
 81 cc.Audio = cc.Class.extend({
 82     src: null,
 83     _element: null,
 84     _AUDIO_TYPE: "AUDIO",
 85 
 86     ctor: function(url){
 87         this.src = url;
 88     },
 89 
 90     setBuffer: function (buffer) {
 91         this._AUDIO_TYPE = "WEBAUDIO";
 92         this._element = new cc.Audio.WebAudio(buffer);
 93     },
 94 
 95     setElement: function (element) {
 96         this._AUDIO_TYPE = "AUDIO";
 97         this._element = element;
 98 
 99         // Prevent partial browser from playing after the end does not reset the paused tag
100         // Will cause the player to judge the status of the error
101         element.addEventListener('ended', function () {
102             if (!element.loop) {
103                 element.paused = true;
104             }
105         });
106     },
107 
108     play: function (offset, loop) {
109         if (!this._element) return;
110         this._element.loop = loop;
111         this._element.play();
112         if (this._AUDIO_TYPE === 'AUDIO' && this._element.paused) {
113             this.stop();
114             cc.Audio.touchPlayList.push({ loop: loop, offset: offset, audio: this._element });
115         }
116 
117         if (cc.Audio.bindTouch === false) {
118             cc.Audio.bindTouch = true;
119             // Listen to the touchstart body event and play the audio when necessary.
120             cc.game.canvas.addEventListener('touchstart', cc.Audio.touchStart);
121         }
122     },
123 
124     getPlaying: function () {
125         if (!this._element) return true;
126         return !this._element.paused;
127     },
128 
129     stop: function () {
130         if (!this._element) return;
131         this._element.pause();
132         try{
133             this._element.currentTime = 0;
134         } catch (err) {}
135     },
136 
137     pause: function () {
138         if (!this._element) return;
139         this._element.pause();
140     },
141 
142     resume: function () {
143         if (!this._element) return;
144         this._element.play();
145     },
146 
147     setVolume: function (volume) {
148         if (!this._element) return;
149         this._element.volume = volume;
150     },
151 
152     getVolume: function () {
153         if (!this._element) return;
154         return this._element.volume;
155     },
156 
157     cloneNode: function () {
158         var audio = new cc.Audio(this.src);
159         if (this._AUDIO_TYPE === "AUDIO") {
160             var elem = document.createElement("audio");
161             var sources = elem.getElementsByTagName('source');
162             for (var i=0; i<sources.length; i++) {
163                 elem.appendChild(sources[i]);
164             }
165             elem.src = this.src;
166             audio.setElement(elem);
167         } else {
168             audio.setBuffer(this._element.buffer);
169         }
170         return audio;
171     }
172 });
173 
174 cc.Audio.touchPlayList = [
175     //{ offset: 0, audio: audio }
176 ];
177 
178 cc.Audio.bindTouch = false;
179 cc.Audio.touchStart = function () {
180     var list = cc.Audio.touchPlayList;
181     var item = null;
182     while (item = list.pop()) {
183         item.audio.loop = !!item.loop;
184         item.audio.play(item.offset);
185     }
186 };
187 
188 cc.Audio.WebAudio = function (buffer) {
189     this.buffer = buffer;
190     this.context = cc.Audio._context;
191 
192     var volume = this.context['createGain']();
193     volume['gain'].value = 1;
194     volume['connect'](this.context['destination']);
195     this._volume = volume;
196 
197     this._loop = false;
198 
199     // The time stamp on the audio time axis when the recording begins to play.
200     this._startTime = -1;
201     // Record the currently playing Source
202     this._currentSource = null;
203     // Record the time has been played
204     this.playedLength = 0;
205 
206     this._currextTimer = null;
207 };
208 
209 cc.Audio.WebAudio.prototype = {
210     constructor: cc.Audio.WebAudio,
211 
212     get paused () {
213         // If the current audio is a loop, then paused is false
214         if (this._currentSource && this._currentSource.loop)
215             return false;
216 
217         // StartTime does not have value, as the default -1, it does not begin to play
218         if (this._startTime === -1)
219             return true;
220 
221         // currentTime - startTime > durationTime
222         return this.context.currentTime - this._startTime > this.buffer.duration;
223     },
224     set paused (bool) {},
225 
226     get loop () { return this._loop; },
227     set loop (bool) { return this._loop = bool; },
228 
229     get volume () { return this._volume['gain'].value; },
230     set volume (num) { return this._volume['gain'].value = num; },
231 
232     get currentTime () { return this.playedLength; },
233     set currentTime (num) { return this.playedLength = num; },
234 
235     play: function (offset) {
236 
237         // If repeat play, you need to stop before an audio
238         if (this._currentSource && !this.paused) {
239             this._currentSource.stop(0);
240             this.playedLength = 0;
241         }
242 
243         var audio = this.context["createBufferSource"]();
244         audio.buffer = this.buffer;
245         audio["connect"](this._volume);
246         audio.loop = this._loop;
247 
248         this._startTime = this.context.currentTime;
249         offset = offset || this.playedLength;
250 
251         var duration = this.buffer.duration;
252         if (!this._loop) {
253             if (audio.start)
254                 audio.start(0, offset, duration - offset);
255             else if (audio["notoGrainOn"])
256                 audio["noteGrainOn"](0, offset, duration - offset);
257             else
258                 audio["noteOn"](0, offset, duration - offset);
259         } else {
260             if (audio.start)
261                 audio.start(0);
262             else if (audio["notoGrainOn"])
263                 audio["noteGrainOn"](0);
264             else
265                 audio["noteOn"](0);
266         }
267 
268         this._currentSource = audio;
269 
270         // If the current audio context time stamp is 0
271         // There may be a need to touch events before you can actually start playing audio
272         // So here to add a timer to determine whether the real start playing audio, if not, then the incoming touchPlay queue
273         if (this.context.currentTime === 0) {
274             var self = this;
275             clearTimeout(this._currextTimer);
276             this._currextTimer = setTimeout(function () {
277                 if (self.context.currentTime === 0) {
278                     cc.Audio.touchPlayList.push({
279                         offset: offset,
280                         audio: self
281                     });
282                 }
283             }, 10);
284         }
285     },
286     pause: function () {
287         // Record the time the current has been played
288         this.playedLength = this.context.currentTime - this._startTime;
289         //If the duration of playedLendth exceeds the audio, you should take the remainder
290         this.playedLength %= this.buffer.duration;
291         var audio = this._currentSource;
292         this._currentSource = null;
293         this._startTime = -1;
294         if (audio)
295             audio.stop(0);
296     }
297 };
298 
299 (function(polyfill){
300 
301     var SWA = polyfill.WEB_AUDIO, SWB = polyfill.ONLY_ONE;
302 
303     var support = [];
304 
305     (function(){
306         var audio = document.createElement("audio");
307         if(audio.canPlayType) {
308             var ogg = audio.canPlayType('audio/ogg; codecs="vorbis"');
309             if (ogg && ogg !== "") support.push(".ogg");
310             var mp3 = audio.canPlayType("audio/mpeg");
311             if (mp3 && mp3 !== "") support.push(".mp3");
312             var wav = audio.canPlayType('audio/wav; codecs="1"');
313             if (wav && wav !== "") support.push(".wav");
314             var mp4 = audio.canPlayType("audio/mp4");
315             if (mp4 && mp4 !== "") support.push(".mp4");
316             var m4a = audio.canPlayType("audio/x-m4a");
317             if (m4a && m4a !== "") support.push(".m4a");
318         }
319     })();
320     try{
321         if(SWA){
322             var context = new (window.AudioContext || window.webkitAudioContext || window.mozAudioContext)();
323             cc.Audio._context = context;
324             if(polyfill.DELAY_CREATE_CTX)
325                 setTimeout(function(){
326                     context = new (window.AudioContext || window.webkitAudioContext || window.mozAudioContext)();
327                     cc.Audio._context = context;
328                 }, 0);
329         }
330     }catch(error){
331         SWA = false;
332         cc.log("browser don't support web audio");
333     }
334 
335     var loader = {
336 
337         cache: {},
338 
339         useWebAudio: false,
340 
341         loadBuffer: function (url, cb) {
342             if (!SWA) return; // WebAudio Buffer
343 
344             var request = new XMLHttpRequest();
345             request.open("GET", url, true);
346             request.responseType = "arraybuffer";
347 
348             // Our asynchronous callback
349             request.onload = function () {
350                 context["decodeAudioData"](request.response, function(buffer){
351                     //success
352                     cb(null, buffer);
353                     //audio.setBuffer(buffer);
354                 }, function(){
355                     //error
356                     cb('decode error - ' + url);
357                 });
358             };
359 
360             request.onerror = function(){
361                 cb('request error - ' + url);
362             };
363 
364             request.send();
365         },
366 
367         load: function(realUrl, url, res, cb){
368 
369             if(support.length === 0)
370                 return cb("can not support audio!");
371 
372             var audio = cc.loader.getRes(url);
373             if (audio)
374                 return cb(null, audio);
375 
376             var i;
377 
378             if(cc.loader.audioPath)
379                 realUrl = cc.path.join(cc.loader.audioPath, realUrl);
380 
381             var extname = cc.path.extname(realUrl);
382 
383             var typeList = [extname];
384             for(i=0; i<support.length; i++){
385                 if(extname !== support[i]){
386                     typeList.push(support[i]);
387                 }
388             }
389 
390             audio = new cc.Audio(realUrl);
391             cc.loader.cache[url] = audio;
392             this.loadAudioFromExtList(realUrl, typeList, audio, cb);
393             return audio;
394         },
395 
396         loadAudioFromExtList: function(realUrl, typeList, audio, cb){
397             if(typeList.length === 0){
398                 var ERRSTR = "can not found the resource of audio! Last match url is : ";
399                 ERRSTR += realUrl.replace(/\.(.*)?$/, "(");
400                 support.forEach(function(ext){
401                     ERRSTR += ext + "|";
402                 });
403                 ERRSTR = ERRSTR.replace(/\|$/, ")");
404                 return cb({status:520, errorMessage:ERRSTR}, null);
405             }
406 
407             if (SWA && this.useWebAudio) {
408                 this.loadBuffer(realUrl, function (error, buffer) {
409                     if (error)
410                         cc.log(error);
411 
412                     if (buffer)
413                         audio.setBuffer(buffer);
414 
415                     cb(null, audio);
416                 });
417                 return;
418             }
419 
420             var num = polyfill.ONE_SOURCE ? 1 : typeList.length;
421 
422             // 加载统一使用dom
423             var dom = document.createElement('audio');
424             for (var i=0; i<num; i++) {
425                 var source = document.createElement('source');
426                 source.src = cc.path.changeExtname(realUrl, typeList[i]);
427                 dom.appendChild(source);
428             }
429 
430             audio.setElement(dom);
431 
432             var timer = setTimeout(function(){
433                 if (dom.readyState === 0) {
434                     failure();
435                 } else {
436                     success();
437                 }
438             }, 8000);
439 
440             var success = function () {
441                 dom.removeEventListener("canplaythrough", success, false);
442                 dom.removeEventListener("error", failure, false);
443                 dom.removeEventListener("emptied", success, false);
444                 if (polyfill.USE_LOADER_EVENT)
445                     dom.removeEventListener(polyfill.USE_LOADER_EVENT, success, false);
446                 clearTimeout(timer);
447                 cb(null, audio);
448             };
449             var failure = function () {
450                 cc.log('load audio failure - ' + realUrl);
451                 success();
452             };
453             dom.addEventListener("canplaythrough", success, false);
454             dom.addEventListener("error", failure, false);
455             if(polyfill.USE_LOADER_EVENT)
456                 dom.addEventListener(polyfill.USE_LOADER_EVENT, success, false);
457         }
458     };
459     cc.loader.register(["mp3", "ogg", "wav", "mp4", "m4a"], loader);
460 
461     /**
462      * cc.audioEngine is the singleton object, it provide simple audio APIs.
463      * @namespace
464      */
465     cc.audioEngine = {
466         _currMusic: null,
467         _musicVolume: 1,
468 
469         features: polyfill,
470 
471         /**
472          * Indicates whether any background music can be played or not.
473          * @returns {boolean} <i>true</i> if the background music is playing, otherwise <i>false</i>
474          */
475         willPlayMusic: function(){return false;},
476 
477         /**
478          * Play music.
479          * @param {String} url The path of the music file without filename extension.
480          * @param {Boolean} loop Whether the music loop or not.
481          * @example
482          * //example
483          * cc.audioEngine.playMusic(path, false);
484          */
485         playMusic: function(url, loop){
486             var bgMusic = this._currMusic;
487             if (bgMusic && bgMusic.getPlaying()) {
488                 bgMusic.stop();
489             }
490             var audio = cc.loader.getRes(url);
491             if (!audio) {
492                 cc.loader.load(url);
493                 audio = cc.loader.getRes(url);
494             }
495             audio.setVolume(this._musicVolume);
496             audio.play(0, loop || false);
497 
498             this._currMusic = audio;
499         },
500 
501         /**
502          * Stop playing music.
503          * @param {Boolean} [releaseData] If release the music data or not.As default value is false.
504          * @example
505          * //example
506          * cc.audioEngine.stopMusic();
507          */
508         stopMusic: function(releaseData){
509             var audio = this._currMusic;
510             if (audio) {
511                 audio.stop();
512                 if (releaseData)
513                     cc.loader.release(audio.src);
514             }
515         },
516 
517         /**
518          * Pause playing music.
519          * @example
520          * //example
521          * cc.audioEngine.pauseMusic();
522          */
523         pauseMusic: function(){
524             var audio = this._currMusic;
525             if (audio)
526                 audio.pause();
527         },
528 
529         /**
530          * Resume playing music.
531          * @example
532          * //example
533          * cc.audioEngine.resumeMusic();
534          */
535         resumeMusic: function(){
536             var audio = this._currMusic;
537             if (audio)
538                 audio.resume();
539         },
540 
541         /**
542          * Rewind playing music.
543          * @example
544          * //example
545          * cc.audioEngine.rewindMusic();
546          */
547         rewindMusic: function(){
548             var audio = this._currMusic;
549             if (audio){
550                 audio.stop();
551                 audio.play();
552             }
553         },
554 
555         /**
556          * The volume of the music max value is 1.0,the min value is 0.0 .
557          * @return {Number}
558          * @example
559          * //example
560          * var volume = cc.audioEngine.getMusicVolume();
561          */
562         getMusicVolume: function(){
563             return this._musicVolume;
564         },
565 
566         /**
567          * Set the volume of music.
568          * @param {Number} volume Volume must be in 0.0~1.0 .
569          * @example
570          * //example
571          * cc.audioEngine.setMusicVolume(0.5);
572          */
573         setMusicVolume: function(volume){
574             volume = volume - 0;
575             if (isNaN(volume)) volume = 1;
576             if (volume > 1) volume = 1;
577             if (volume < 0) volume = 0;
578 
579             this._musicVolume = volume;
580             var audio = this._currMusic;
581             if (audio) {
582                 audio.setVolume(volume);
583             }
584         },
585 
586         /**
587          * Whether the music is playing.
588          * @return {Boolean} If is playing return true,or return false.
589          * @example
590          * //example
591          *  if (cc.audioEngine.isMusicPlaying()) {
592          *      cc.log("music is playing");
593          *  }
594          *  else {
595          *      cc.log("music is not playing");
596          *  }
597          */
598         isMusicPlaying: function(){
599             var audio = this._currMusic;
600             if (audio) {
601                 return audio.getPlaying();
602             } else {
603                 return false;
604             }
605         },
606 
607         _audioPool: {},
608         _maxAudioInstance: 10,
609         _effectVolume: 1,
610         /**
611          * Play sound effect.
612          * @param {String} url The path of the sound effect with filename extension.
613          * @param {Boolean} loop Whether to loop the effect playing, default value is false
614          * @return {Number|null} the audio id
615          * @example
616          * //example
617          * var soundId = cc.audioEngine.playEffect(path);
618          */
619         playEffect: function(url, loop){
620 
621             if (SWB && this._currMusic && this._currMusic.getPlaying()) {
622                 cc.log('Browser is only allowed to play one audio');
623                 return null;
624             }
625 
626             var effectList = this._audioPool[url];
627             if (!effectList) {
628                 effectList = this._audioPool[url] = [];
629             }
630 
631             var i;
632 
633             for (i = 0; i < effectList.length; i++) {
634                 if (!effectList[i].getPlaying()) {
635                     break;
636                 }
637             }
638 
639             if (!SWA && i > this._maxAudioInstance) {
640                 var first = effectList.shift();
641                 first.stop();
642                 effectList.push(first);
643                 i = effectList.length - 1;
644                 // cc.log("Error: %s greater than %d", url, this._maxAudioInstance);
645             }
646 
647             var audio;
648             if (effectList[i]) {
649                 audio = effectList[i];
650                 audio.setVolume(this._effectVolume);
651                 audio.play(0, loop || false);
652                 return audio;
653             }
654 
655             audio = cc.loader.getRes(url);
656 
657             if (audio && SWA && audio._AUDIO_TYPE === 'AUDIO') {
658                 cc.loader.release(url);
659                 audio = null;
660             }
661 
662             if (audio) {
663 
664                 if (SWA && audio._AUDIO_TYPE === 'AUDIO') {
665                     loader.loadBuffer(url, function (error, buffer) {
666                         audio.setBuffer(buffer);
667                         audio.setVolume(cc.audioEngine._effectVolume);
668                         if (!audio.getPlaying())
669                             audio.play(0, loop || false);
670                     });
671                 } else {
672                     audio = audio.cloneNode();
673                     audio.setVolume(this._effectVolume);
674                     audio.play(0, loop || false);
675                     effectList.push(audio);
676                     return audio;
677                 }
678 
679             }
680 
681             loader.useWebAudio = true;
682             cc.loader.load(url, function (audio) {
683                 audio = cc.loader.getRes(url);
684                 audio = audio.cloneNode();
685                 audio.setVolume(cc.audioEngine._effectVolume);
686                 audio.play(0, loop || false);
687                 effectList.push(audio);
688             });
689             loader.useWebAudio = false;
690 
691             return audio;
692         },
693 
694         /**
695          * Set the volume of sound effects.
696          * @param {Number} volume Volume must be in 0.0~1.0 .
697          * @example
698          * //example
699          * cc.audioEngine.setEffectsVolume(0.5);
700          */
701         setEffectsVolume: function(volume){
702             volume = volume - 0;
703             if(isNaN(volume)) volume = 1;
704             if(volume > 1) volume = 1;
705             if(volume < 0) volume = 0;
706 
707             this._effectVolume = volume;
708             var audioPool = this._audioPool;
709             for(var p in audioPool){
710                 var audioList = audioPool[p];
711                 if(Array.isArray(audioList))
712                     for(var i=0; i<audioList.length; i++){
713                         audioList[i].setVolume(volume);
714                     }
715             }
716         },
717 
718         /**
719          * The volume of the effects max value is 1.0,the min value is 0.0 .
720          * @return {Number}
721          * @example
722          * //example
723          * var effectVolume = cc.audioEngine.getEffectsVolume();
724          */
725         getEffectsVolume: function(){
726             return this._effectVolume;
727         },
728 
729         /**
730          * Pause playing sound effect.
731          * @param {Number} audio The return value of function playEffect.
732          * @example
733          * //example
734          * cc.audioEngine.pauseEffect(audioID);
735          */
736         pauseEffect: function(audio){
737             if(audio){
738                 audio.pause();
739             }
740         },
741 
742         /**
743          * Pause all playing sound effect.
744          * @example
745          * //example
746          * cc.audioEngine.pauseAllEffects();
747          */
748         pauseAllEffects: function(){
749             var ap = this._audioPool;
750             for(var p in ap){
751                 var list = ap[p];
752                 for(var i=0; i<ap[p].length; i++){
753                     if(list[i].getPlaying()){
754                         list[i].pause();
755                     }
756                 }
757             }
758         },
759 
760         /**
761          * Resume playing sound effect.
762          * @param {Number} audio The return value of function playEffect.
763          * @audioID
764          * //example
765          * cc.audioEngine.resumeEffect(audioID);
766          */
767         resumeEffect: function(audio){
768             if(audio)
769                 audio.resume();
770         },
771 
772         /**
773          * Resume all playing sound effect
774          * @example
775          * //example
776          * cc.audioEngine.resumeAllEffects();
777          */
778         resumeAllEffects: function(){
779             var ap = this._audioPool;
780             for(var p in ap){
781                 var list = ap[p];
782                 for(var i=0; i<ap[p].length; i++){
783                     list[i].resume();
784                 }
785             }
786         },
787 
788         /**
789          * Stop playing sound effect.
790          * @param {Number} audio The return value of function playEffect.
791          * @example
792          * //example
793          * cc.audioEngine.stopEffect(audioID);
794          */
795         stopEffect: function(audio){
796             if(audio)
797                 audio.stop();
798         },
799 
800         /**
801          * Stop all playing sound effects.
802          * @example
803          * //example
804          * cc.audioEngine.stopAllEffects();
805          */
806         stopAllEffects: function(){
807             var ap = this._audioPool;
808             for(var p in ap){
809                 var list = ap[p];
810                 for(var i=0; i<ap[p].length; i++){
811                     list[i].stop();
812                 }
813             }
814         },
815 
816         /**
817          * Unload the preloaded effect from internal buffer
818          * @param {String} url
819          * @example
820          * //example
821          * cc.audioEngine.unloadEffect(EFFECT_FILE);
822          */
823         unloadEffect: function(url){
824             if(!url){
825                 return;
826             }
827 
828             cc.loader.release(url);
829             var pool = this._audioPool[url];
830             if(pool) pool.length = 0;
831             delete this._audioPool[url];
832         },
833 
834         /**
835          * End music and effects.
836          */
837         end: function(){
838             this.stopMusic();
839             this.stopAllEffects();
840         },
841 
842         _pauseCache: [],
843         _pausePlaying: function(){
844             var bgMusic = this._currMusic;
845             if(bgMusic && bgMusic.getPlaying()){
846                 bgMusic.pause();
847                 this._pauseCache.push(bgMusic);
848             }
849             var ap = this._audioPool;
850             for(var p in ap){
851                 var list = ap[p];
852                 for(var i=0; i<ap[p].length; i++){
853                     if(list[i].getPlaying()){
854                         list[i].pause();
855                         this._pauseCache.push(list[i]);
856                     }
857                 }
858             }
859         },
860 
861         _resumePlaying: function(){
862             var list = this._pauseCache;
863             for(var i=0; i<list.length; i++){
864                 list[i].resume();
865             }
866             list.length = 0;
867         }
868     };
869 
870 })(window.__audioSupport);
871