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