SimpleAudioEngine.js 68 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003
  1. /****************************************************************************
  2. Copyright (c) 2010-2012 cocos2d-x.org
  3. Copyright (c) 2008-2010 Ricardo Quesada
  4. Copyright (c) 2011 Zynga Inc.
  5. http://www.cocos2d-x.org
  6. Permission is hereby granted, free of charge, to any person obtaining a copy
  7. of this software and associated documentation files (the "Software"), to deal
  8. in the Software without restriction, including without limitation the rights
  9. to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  10. copies of the Software, and to permit persons to whom the Software is
  11. furnished to do so, subject to the following conditions:
  12. The above copyright notice and this permission notice shall be included in
  13. all copies or substantial portions of the Software.
  14. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  15. IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  16. FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  17. AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  18. LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  19. OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  20. THE SOFTWARE.
  21. ****************************************************************************/
  22. var cc = cc || {};
  23. /**
  24. * A simple Audio Engine engine API.
  25. * @class
  26. * @extends cc.Class
  27. */
  28. cc.AudioEngine = cc.Class.extend(/** @lends cc.AudioEngine# */{
  29. _audioID:0,
  30. _audioIDList:null,
  31. _supportedFormat:null,
  32. _soundSupported:false, // if sound is not enabled, this engine's init() will return false
  33. _effectsVolume:1, // the volume applied to all effects
  34. _playingMusic:null, // the music being played, when null, no music is being played; when not null, it may be playing or paused
  35. _resPath : "", //root path for resources
  36. _pausedPlayings: null,
  37. ctor:function(){
  38. this._audioIDList = {};
  39. this._supportedFormat = [];
  40. this._pausedPlayings = [];
  41. },
  42. /**
  43. * Set root path for music resources.
  44. * @param resPath
  45. */
  46. setResPath : function(resPath){
  47. if(!resPath || resPath.length == 0) return;
  48. this._resPath = resPath.substring(resPath.length - 1) == "/" ? resPath : resPath + "/";
  49. },
  50. /**
  51. * Check each type to see if it can be played by current browser
  52. * @param {Object} capabilities The results are filled into this dict
  53. * @protected
  54. */
  55. _checkCanPlay: function(capabilities) {
  56. var au = document.createElement('audio');
  57. if (au.canPlayType) {
  58. // <audio> tag is supported, go on
  59. var _check = function(typeStr) {
  60. var result = au.canPlayType(typeStr);
  61. return result != "no" && result != "";
  62. };
  63. capabilities["mp3"] = _check("audio/mpeg");
  64. capabilities["mp4"] = _check("audio/mp4");
  65. capabilities["m4a"] = _check("audio/x-m4a") || _check("audio/aac");
  66. capabilities["ogg"] = _check('audio/ogg; codecs="vorbis"');
  67. capabilities["wav"] = _check('audio/wav; codecs="1"');
  68. } else {
  69. // <audio> tag is not supported, nothing is supported
  70. var formats = ["mp3", "mp4", "m4a", "ogg", "wav"];
  71. for (var idx in formats) {
  72. capabilities[formats[idx]] = false;
  73. }
  74. }
  75. },
  76. /**
  77. * Helper function for cutting out the extension from the path
  78. * @param {String} fullpath
  79. * @return {String|null} path without ext name
  80. * @protected
  81. */
  82. _getPathWithoutExt: function (fullpath) {
  83. if (typeof(fullpath) != "string") {
  84. return null;
  85. }
  86. var endPos = fullpath.lastIndexOf(".");
  87. if (endPos !== -1)
  88. return fullpath.substring(0, endPos);
  89. return fullpath;
  90. },
  91. /**
  92. * Helper function for extracting the extension from the path
  93. * @param {String} fullpath
  94. * @protected
  95. */
  96. _getExtFromFullPath: function (fullpath) {
  97. var startPos = fullpath.lastIndexOf(".");
  98. if (startPos !== -1) {
  99. return fullpath.substring(startPos + 1, fullpath.length);
  100. }
  101. return -1;
  102. },
  103. /**
  104. * Indicates whether any background music can be played or not.
  105. * @returns {boolean} <i>true</i> if the background music is playing, otherwise <i>false</i>
  106. */
  107. willPlayMusic: function() {
  108. return false;
  109. },
  110. /**
  111. * Preload music resource.
  112. * @param {String} path
  113. * @param {Function} selector
  114. * @param {Object} target
  115. */
  116. preloadMusic:function(path, selector, target){
  117. this.preloadSound(path, selector, target);
  118. },
  119. /**
  120. * Preload effect resource.
  121. * @param {String} path
  122. * @param {Function} selector
  123. * @param {Object} target
  124. */
  125. preloadEffect:function(path, selector, target){
  126. this.preloadSound(path, selector, target);
  127. },
  128. /**
  129. * search in this._supportedFormat if ext is there
  130. * @param {String} ext
  131. * @returns {Boolean}
  132. */
  133. isFormatSupported: function (ext) {
  134. var locSupportedFormat = this._supportedFormat;
  135. for (var i = 0, len = locSupportedFormat.length; i < len; i++) {
  136. if (locSupportedFormat[i] == ext)
  137. return true;
  138. }
  139. return false;
  140. },
  141. /**
  142. * The volume of the effects max value is 1.0,the min value is 0.0 .
  143. * @return {Number}
  144. * @example
  145. * //example
  146. * var effectVolume = cc.AudioEngine.getInstance().getEffectsVolume();
  147. */
  148. getEffectsVolume: function() {
  149. return this._effectsVolume;
  150. }
  151. });
  152. /**
  153. * the entity stored in soundList and effectList, containing the audio element and the extension name.
  154. * used in cc.SimpleAudioEngine
  155. */
  156. cc.SimpleSFX = function (audio, ext) {
  157. this.audio = audio;
  158. this.ext = ext || ".ogg";
  159. };
  160. /**
  161. * The Audio Engine implementation via <audio> tag in HTML5.
  162. * @class
  163. * @extends cc.AudioEngine
  164. */
  165. cc.SimpleAudioEngine = cc.AudioEngine.extend(/** @lends cc.SimpleAudioEngine# */{
  166. _effectList:null,
  167. _soundList:null,
  168. _maxAudioInstance:5,
  169. _canPlay:true,
  170. _musicListenerBound:null,
  171. _musicIsStopped: false,
  172. /**
  173. * Constructor
  174. */
  175. ctor:function () {
  176. cc.AudioEngine.prototype.ctor.call(this);
  177. this._effectList = {};
  178. this._soundList = {};
  179. this._musicListenerBound = this._musicListener.bind(this);
  180. var ua = navigator.userAgent;
  181. if(/Mobile/.test(ua) && (/iPhone OS/.test(ua)||/iPad/.test(ua)||/Firefox/.test(ua)) || /MSIE/.test(ua)){
  182. this._canPlay = false;
  183. }
  184. },
  185. /**
  186. * Initialize sound type
  187. * @return {Boolean}
  188. */
  189. init:function () {
  190. // gather capabilities information, enable sound if any of the audio format is supported
  191. var capabilities = {};
  192. this._checkCanPlay(capabilities);
  193. var formats = ["ogg", "mp3", "wav", "mp4", "m4a"], locSupportedFormat = this._supportedFormat;
  194. for (var idx in formats) {
  195. var name = formats[idx];
  196. if (capabilities[name])
  197. locSupportedFormat.push(name);
  198. }
  199. this._soundSupported = locSupportedFormat.length > 0;
  200. return this._soundSupported;
  201. },
  202. /**
  203. * Preload music resource.<br />
  204. * This method is called when cc.Loader preload resources.
  205. * @param {String} path The path of the music file with filename extension.
  206. * @param {Function} selector
  207. * @param {Object} target
  208. */
  209. preloadSound:function (path, selector, target) {
  210. if (this._soundSupported) {
  211. var realPath = this._resPath + path;
  212. var extName = this._getExtFromFullPath(path);
  213. var keyname = this._getPathWithoutExt(path);
  214. if (!this._soundList[keyname] && this.isFormatSupported(extName)) {
  215. if(this._canPlay){
  216. var sfxCache = new cc.SimpleSFX();
  217. sfxCache.ext = extName;
  218. sfxCache.audio = new Audio(realPath);
  219. sfxCache.audio.preload = 'auto';
  220. var soundPreloadCanplayHandler = function () {
  221. cc.doCallback(selector, target);
  222. this.removeEventListener('canplaythrough', soundPreloadCanplayHandler, false);
  223. this.removeEventListener('error', soundPreloadErrorHandler, false);
  224. };
  225. var soundPreloadErrorHandler = function (e) {
  226. cc.doCallback(selector, target,e.srcElement.src);
  227. this.removeEventListener('canplaythrough', soundPreloadCanplayHandler, false);
  228. this.removeEventListener('error', soundPreloadErrorHandler, false);
  229. };
  230. sfxCache.audio.addEventListener('canplaythrough', soundPreloadCanplayHandler, false);
  231. sfxCache.audio.addEventListener("error", soundPreloadErrorHandler, false);
  232. this._soundList[keyname] = sfxCache;
  233. sfxCache.audio.load();
  234. return;
  235. }
  236. }
  237. }
  238. cc.doCallback(selector, target);
  239. },
  240. /**
  241. * Play music.
  242. * @param {String} path The path of the music file without filename extension.
  243. * @param {Boolean} loop Whether the music loop or not.
  244. * @example
  245. * //example
  246. * cc.AudioEngine.getInstance().playMusic(path, false);
  247. */
  248. playMusic:function (path, loop) {
  249. if (!this._soundSupported)
  250. return;
  251. var keyname = this._getPathWithoutExt(path);
  252. var extName = this._getExtFromFullPath(path);
  253. var au;
  254. var locSoundList = this._soundList;
  255. if (locSoundList[this._playingMusic])
  256. locSoundList[this._playingMusic].audio.pause();
  257. this._playingMusic = keyname;
  258. if (locSoundList[this._playingMusic])
  259. au = locSoundList[this._playingMusic].audio;
  260. else {
  261. var sfxCache = new cc.SimpleSFX();
  262. sfxCache.ext = extName;
  263. au = sfxCache.audio = new Audio(path);
  264. sfxCache.audio.preload = 'auto';
  265. locSoundList[keyname] = sfxCache;
  266. sfxCache.audio.load();
  267. }
  268. au.addEventListener("pause", this._musicListenerBound , false);
  269. au.loop = loop || false;
  270. au.play();
  271. cc.AudioEngine.isMusicPlaying = true;
  272. this._musicIsStopped = false;
  273. },
  274. _musicListener:function(e){
  275. cc.AudioEngine.isMusicPlaying = false;
  276. if (this._soundList[this._playingMusic]) {
  277. var au = this._soundList[this._playingMusic].audio;
  278. au.removeEventListener('pause', this._musicListener, false);
  279. }
  280. },
  281. /**
  282. * Stop playing music.
  283. * @param {Boolean} releaseData If release the music data or not.As default value is false.
  284. * @example
  285. * //example
  286. * cc.AudioEngine.getInstance().stopMusic();
  287. */
  288. stopMusic:function (releaseData) {
  289. var locSoundList = this._soundList, locPlayingMusic = this._playingMusic;
  290. if (locSoundList[locPlayingMusic]) {
  291. var au = locSoundList[locPlayingMusic].audio;
  292. au.pause();
  293. au.duration && (au.currentTime = au.duration);
  294. if (releaseData)
  295. delete locSoundList[locPlayingMusic];
  296. cc.AudioEngine.isMusicPlaying = false;
  297. this._musicIsStopped = true;
  298. }
  299. },
  300. /**
  301. * Pause playing music.
  302. * @example
  303. * //example
  304. * cc.AudioEngine.getInstance().pauseMusic();
  305. */
  306. pauseMusic:function () {
  307. if (!this._musicIsStopped && this._soundList[this._playingMusic]) {
  308. var au = this._soundList[this._playingMusic].audio;
  309. au.pause();
  310. cc.AudioEngine.isMusicPlaying = false;
  311. }
  312. },
  313. /**
  314. * Resume playing music.
  315. * @example
  316. * //example
  317. * cc.AudioEngine.getInstance().resumeMusic();
  318. */
  319. resumeMusic:function () {
  320. if (!this._musicIsStopped && this._soundList[this._playingMusic]) {
  321. var au = this._soundList[this._playingMusic].audio;
  322. au.play();
  323. au.addEventListener("pause", this._musicListenerBound , false);
  324. cc.AudioEngine.isMusicPlaying = true;
  325. }
  326. },
  327. /**
  328. * Rewind playing music.
  329. * @example
  330. * //example
  331. * cc.AudioEngine.getInstance().rewindMusic();
  332. */
  333. rewindMusic:function () {
  334. if (this._soundList[this._playingMusic]) {
  335. var au = this._soundList[this._playingMusic].audio;
  336. au.currentTime = 0;
  337. au.play();
  338. au.addEventListener("pause", this._musicListenerBound , false);
  339. cc.AudioEngine.isMusicPlaying = true;
  340. this._musicIsStopped = false;
  341. }
  342. },
  343. /**
  344. * The volume of the music max value is 1.0,the min value is 0.0 .
  345. * @return {Number}
  346. * @example
  347. * //example
  348. * var volume = cc.AudioEngine.getInstance().getMusicVolume();
  349. */
  350. getMusicVolume:function () {
  351. if (this._soundList[this._playingMusic]) {
  352. return this._soundList[this._playingMusic].audio.volume;
  353. }
  354. return 0;
  355. },
  356. /**
  357. * Set the volume of music.
  358. * @param {Number} volume Volume must be in 0.0~1.0 .
  359. * @example
  360. * //example
  361. * cc.AudioEngine.getInstance().setMusicVolume(0.5);
  362. */
  363. setMusicVolume:function (volume) {
  364. if (this._soundList[this._playingMusic]) {
  365. var music = this._soundList[this._playingMusic].audio;
  366. if (volume > 1) {
  367. music.volume = 1;
  368. } else if (volume < 0) {
  369. music.volume = 0;
  370. } else {
  371. music.volume = volume;
  372. }
  373. }
  374. },
  375. /**
  376. * Whether the music is playing.
  377. * @return {Boolean} If is playing return true,or return false.
  378. * @example
  379. * //example
  380. * if (cc.AudioEngine.getInstance().isMusicPlaying()) {
  381. * cc.log("music is playing");
  382. * }
  383. * else {
  384. * cc.log("music is not playing");
  385. * }
  386. */
  387. isMusicPlaying: function () {
  388. return cc.AudioEngine.isMusicPlaying;
  389. },
  390. /**
  391. * Play sound effect.
  392. * @param {String} path The path of the sound effect with filename extension.
  393. * @param {Boolean} loop Whether to loop the effect playing, default value is false
  394. * @return {Number|null} the audio id
  395. * @example
  396. * //example
  397. * var soundId = cc.AudioEngine.getInstance().playEffect(path);
  398. */
  399. playEffect: function (path, loop) {
  400. if (!this._soundSupported)
  401. return null;
  402. var keyname = this._getPathWithoutExt(path), actExt;
  403. if (this._soundList[keyname]) {
  404. actExt = this._soundList[keyname].ext;
  405. } else {
  406. actExt = this._getExtFromFullPath(path);
  407. }
  408. var reclaim = this._getEffectList(keyname), au;
  409. if (reclaim.length > 0) {
  410. for (var i = 0; i < reclaim.length; i++) {
  411. //if one of the effect ended, play it
  412. if (reclaim[i].ended) {
  413. au = reclaim[i];
  414. au.currentTime = 0;
  415. if (window.chrome)
  416. au.load();
  417. break;
  418. }
  419. }
  420. }
  421. if (!au) {
  422. if (reclaim.length >= this._maxAudioInstance) {
  423. cc.log("Error: " + path + " greater than " + this._maxAudioInstance);
  424. return null;
  425. }
  426. au = new Audio(keyname + "." + actExt);
  427. au.volume = this._effectsVolume;
  428. reclaim.push(au);
  429. }
  430. if (loop)
  431. au.loop = loop;
  432. au.play();
  433. var audioID = this._audioID++;
  434. this._audioIDList[audioID] = au;
  435. return audioID;
  436. },
  437. /**
  438. * Set the volume of sound effects.
  439. * @param {Number} volume Volume must be in 0.0~1.0 .
  440. * @example
  441. * //example
  442. * cc.AudioEngine.getInstance().setEffectsVolume(0.5);
  443. */
  444. setEffectsVolume:function (volume) {
  445. if (volume > 1)
  446. this._effectsVolume = 1;
  447. else if (volume < 0)
  448. this._effectsVolume = 0;
  449. else
  450. this._effectsVolume = volume;
  451. var tmpArr, au, locEffectList = this._effectList;
  452. for (var key in locEffectList) {
  453. tmpArr = locEffectList[key];
  454. if (tmpArr.length > 0) {
  455. for (var j = 0; j < tmpArr.length; j++) {
  456. au = tmpArr[j];
  457. au.volume = this._effectsVolume;
  458. }
  459. }
  460. }
  461. },
  462. /**
  463. * Pause playing sound effect.
  464. * @param {Number} audioID The return value of function playEffect.
  465. * @example
  466. * //example
  467. * cc.AudioEngine.getInstance().pauseEffect(audioID);
  468. */
  469. pauseEffect:function (audioID) {
  470. if (audioID == null) return;
  471. if (this._audioIDList[audioID]) {
  472. var au = this._audioIDList[audioID];
  473. if (!au.ended) {
  474. au.pause();
  475. }
  476. }
  477. },
  478. /**
  479. * Pause all playing sound effect.
  480. * @example
  481. * //example
  482. * cc.AudioEngine.getInstance().pauseAllEffects();
  483. */
  484. pauseAllEffects:function () {
  485. var tmpArr, au;
  486. var locEffectList = this._effectList;
  487. for (var i in locEffectList) {
  488. tmpArr = locEffectList[i];
  489. for (var j = 0; j < tmpArr.length; j++) {
  490. au = tmpArr[j];
  491. if (!au.ended)
  492. au.pause();
  493. }
  494. }
  495. },
  496. /**
  497. * Resume playing sound effect.
  498. * @param {Number} audioID The return value of function playEffect.
  499. * @audioID
  500. * //example
  501. * cc.AudioEngine.getInstance().resumeEffect(audioID);
  502. */
  503. resumeEffect:function (audioID) {
  504. if (audioID == null) return;
  505. if (this._audioIDList[audioID]) {
  506. var au = this._audioIDList[audioID];
  507. if (!au.ended)
  508. au.play();
  509. }
  510. },
  511. /**
  512. * Resume all playing sound effect
  513. * @example
  514. * //example
  515. * cc.AudioEngine.getInstance().resumeAllEffects();
  516. */
  517. resumeAllEffects:function () {
  518. var tmpArr, au;
  519. var locEffectList = this._effectList;
  520. for (var i in locEffectList) {
  521. tmpArr = locEffectList[i];
  522. if (tmpArr.length > 0) {
  523. for (var j = 0; j < tmpArr.length; j++) {
  524. au = tmpArr[j];
  525. if (!au.ended)
  526. au.play();
  527. }
  528. }
  529. }
  530. },
  531. /**
  532. * Stop playing sound effect.
  533. * @param {Number} audioID The return value of function playEffect.
  534. * @example
  535. * //example
  536. * cc.AudioEngine.getInstance().stopEffect(audioID);
  537. */
  538. stopEffect:function (audioID) {
  539. if (audioID == null) return;
  540. if (this._audioIDList[audioID]) {
  541. var au = this._audioIDList[audioID];
  542. if (!au.ended) {
  543. au.loop = false;
  544. au.duration && (au.currentTime = au.duration);
  545. }
  546. }
  547. },
  548. /**
  549. * Stop all playing sound effects.
  550. * @example
  551. * //example
  552. * cc.AudioEngine.getInstance().stopAllEffects();
  553. */
  554. stopAllEffects:function () {
  555. var tmpArr, au, locEffectList = this._effectList;
  556. for (var i in locEffectList) {
  557. tmpArr = locEffectList[i];
  558. for (var j = 0; j < tmpArr.length; j++) {
  559. au = tmpArr[j];
  560. if (!au.ended) {
  561. au.loop = false;
  562. au.duration && (au.currentTime = au.duration);
  563. }
  564. }
  565. }
  566. },
  567. /**
  568. * Unload the preloaded effect from internal buffer
  569. * @param {String} path
  570. * @example
  571. * //example
  572. * cc.AudioEngine.getInstance().unloadEffect(EFFECT_FILE);
  573. */
  574. unloadEffect:function (path) {
  575. if (!path) return;
  576. var keyname = this._getPathWithoutExt(path);
  577. if (this._effectList[keyname]) {
  578. delete this._effectList[keyname];
  579. }
  580. var au, pathName, locAudioIDList = this._audioIDList;
  581. for (var k in locAudioIDList) {
  582. au = locAudioIDList[k];
  583. pathName = this._getPathWithoutExt(au.src);
  584. if(pathName.indexOf(keyname) > -1){
  585. this.stopEffect(k);
  586. delete locAudioIDList[k];
  587. }
  588. }
  589. },
  590. _getEffectList:function (elt) {
  591. var locEffectList = this._effectList;
  592. if (locEffectList[elt]) {
  593. return locEffectList[elt];
  594. } else {
  595. locEffectList[elt] = [];
  596. return locEffectList[elt];
  597. }
  598. },
  599. _pausePlaying: function(){
  600. var locPausedPlayings = this._pausedPlayings, locSoundList = this._soundList;
  601. var tmpArr, au;
  602. if (!this._musicIsStopped && locSoundList[this._playingMusic]) {
  603. au = locSoundList[this._playingMusic].audio;
  604. if (!au.paused) {
  605. au.pause();
  606. cc.AudioEngine.isMusicPlaying = false;
  607. locPausedPlayings.push(au);
  608. }
  609. }
  610. var locEffectList = this._effectList;
  611. for (var selKey in locEffectList) {
  612. tmpArr = locEffectList[selKey];
  613. for (var j = 0; j < tmpArr.length; j++) {
  614. au = tmpArr[j];
  615. if (!au.ended && !au.paused) {
  616. au.pause();
  617. locPausedPlayings.push(au);
  618. }
  619. }
  620. }
  621. },
  622. _resumePlaying: function(){
  623. var locPausedPlayings = this._pausedPlayings, locSoundList = this._soundList;
  624. var tmpArr, au;
  625. if (!this._musicIsStopped && locSoundList[this._playingMusic]) {
  626. au = locSoundList[this._playingMusic].audio;
  627. if (locPausedPlayings.indexOf(au) !== -1) {
  628. au.play();
  629. au.addEventListener("pause", this._musicListenerBound, false);
  630. cc.AudioEngine.isMusicPlaying = true;
  631. }
  632. }
  633. var locEffectList = this._effectList;
  634. for (var selKey in locEffectList) {
  635. tmpArr = locEffectList[selKey];
  636. for (var j = 0; j < tmpArr.length; j++) {
  637. au = tmpArr[j];
  638. if (!au.ended && locPausedPlayings.indexOf(au) !== -1)
  639. au.play();
  640. }
  641. }
  642. locPausedPlayings.length = 0;
  643. }
  644. });
  645. cc.PlayingTask = function(id, audio,isLoop, status){
  646. this.id = id;
  647. this.audio = audio;
  648. this.isLoop = isLoop || false;
  649. this.status = status || cc.PlayingTaskStatus.stop;
  650. };
  651. cc.PlayingTaskStatus = {playing:1, pause:2, stop:3, waiting:4};
  652. cc.SimpleAudioEngineForMobile = cc.SimpleAudioEngine.extend({
  653. _playingList: null,
  654. _currentTask:null,
  655. _isPauseForList: false,
  656. _checkFlag: true,
  657. _audioEndedCallbackBound: null,
  658. ctor:function(){
  659. cc.SimpleAudioEngine.prototype.ctor.call(this);
  660. this._maxAudioInstance = 2;
  661. this._playingList = [];
  662. this._isPauseForList = false;
  663. this._checkFlag = true;
  664. this._audioEndedCallbackBound = this._audioEndCallback.bind(this);
  665. },
  666. _stopAllEffectsForList: function(){
  667. var tmpArr, au, locEffectList = this._effectList;
  668. for (var i in locEffectList) {
  669. tmpArr = locEffectList[i];
  670. for (var j = 0; j < tmpArr.length; j++) {
  671. au = tmpArr[j];
  672. if (!au.ended) {
  673. au.removeEventListener('ended', this._audioEndedCallbackBound, false);
  674. au.loop = false;
  675. au.duration && (au.currentTime = au.duration);
  676. }
  677. }
  678. }
  679. this._playingList.length = 0;
  680. this._currentTask = null;
  681. },
  682. /**
  683. * Play music.
  684. * @param {String} path The path of the music file without filename extension.
  685. * @param {Boolean} loop Whether the music loop or not.
  686. * @example
  687. * //example
  688. * cc.AudioEngine.getInstance().playMusic(path, false);
  689. */
  690. playMusic:function (path, loop) {
  691. if (!this._soundSupported)
  692. return;
  693. this._stopAllEffectsForList();
  694. var keyname = this._getPathWithoutExt(path);
  695. var extName = this._getExtFromFullPath(path);
  696. var au;
  697. var locSoundList = this._soundList;
  698. if (locSoundList[this._playingMusic]){
  699. var currMusic = locSoundList[this._playingMusic];
  700. currMusic.audio.removeEventListener("pause",this._musicListenerBound , false)
  701. currMusic.audio.pause();
  702. }
  703. this._playingMusic = keyname;
  704. if (locSoundList[this._playingMusic])
  705. au = locSoundList[this._playingMusic].audio;
  706. else {
  707. var sfxCache = new cc.SimpleSFX();
  708. sfxCache.ext = extName;
  709. au = sfxCache.audio = new Audio(path);
  710. sfxCache.audio.preload = 'auto';
  711. locSoundList[keyname] = sfxCache;
  712. sfxCache.audio.load();
  713. }
  714. au.addEventListener("pause", this._musicListenerBound , false);
  715. au.loop = loop || false;
  716. au.play();
  717. cc.AudioEngine.isMusicPlaying = true;
  718. this._musicIsStopped = false;
  719. },
  720. isMusicPlaying:function(){
  721. var locSoundList = this._soundList, locPlayingMusic = this._playingMusic;
  722. if (locSoundList[locPlayingMusic]) {
  723. var au = locSoundList[locPlayingMusic].audio;
  724. return (!au.paused && !au.ended);
  725. }
  726. return false;
  727. },
  728. _musicListener:function(){
  729. cc.AudioEngine.isMusicPlaying = false;
  730. if (this._soundList[this._playingMusic]) {
  731. var au = this._soundList[this._playingMusic].audio;
  732. au.removeEventListener('pause', this._musicListener, false);
  733. }
  734. if(this._checkFlag)
  735. this._isPauseForList = false;
  736. else
  737. this._checkFlag = true;
  738. },
  739. _stopExpiredTask:function(expendTime){
  740. var locPlayingList = this._playingList, locAudioIDList = this._audioIDList;
  741. for(var i = 0; i < locPlayingList.length; ){
  742. var selTask = locPlayingList[i];
  743. if ((selTask.status === cc.PlayingTaskStatus.waiting)){
  744. if (selTask.audio.currentTime + expendTime >= selTask.audio.duration) {
  745. locPlayingList.splice(i, 1);
  746. if (locAudioIDList[selTask.id]) {
  747. var au = locAudioIDList[selTask.id];
  748. if (!au.ended) {
  749. au.removeEventListener('ended', this._audioEndedCallbackBound, false);
  750. au.loop = false;
  751. au.duration && (au.currentTime = au.duration);
  752. }
  753. }
  754. continue;
  755. } else
  756. selTask.audio.currentTime = selTask.audio.currentTime + expendTime;
  757. }
  758. i++;
  759. }
  760. },
  761. _audioEndCallback: function () {
  762. var locCurrentTask = this._currentTask;
  763. var expendTime = locCurrentTask.audio.currentTime;
  764. this._stopExpiredTask(expendTime);
  765. if (locCurrentTask.isLoop) {
  766. locCurrentTask.audio.play();
  767. return;
  768. }
  769. locCurrentTask.audio.removeEventListener('ended', this._audioEndedCallbackBound, false);
  770. cc.ArrayRemoveObject(this._playingList, locCurrentTask);
  771. locCurrentTask = this._getNextTaskToPlay();
  772. if (!locCurrentTask) {
  773. this._currentTask = null;
  774. if (this._isPauseForList) {
  775. this._isPauseForList = false;
  776. this.resumeMusic();
  777. }
  778. } else {
  779. this._currentTask = locCurrentTask;
  780. locCurrentTask.status = cc.PlayingTaskStatus.playing;
  781. locCurrentTask.audio.play();
  782. }
  783. },
  784. _pushingPlayingTask: function(playingTask){
  785. if(!playingTask)
  786. throw "cc.SimpleAudioEngineForMobile._pushingPlayingTask(): playingTask should be non-null.";
  787. var locPlayingTaskList = this._playingList;
  788. if(!this._currentTask){
  789. if(this.isMusicPlaying()){
  790. this._checkFlag = false;
  791. this.pauseMusic();
  792. this._isPauseForList = true;
  793. }
  794. }else{
  795. this._currentTask.status = cc.PlayingTaskStatus.waiting;
  796. this._currentTask.audio.pause();
  797. }
  798. locPlayingTaskList.push(playingTask);
  799. this._currentTask = playingTask;
  800. this._playingAudioTask(playingTask)
  801. },
  802. _playingAudioTask: function(playTask){
  803. playTask.audio.addEventListener("ended", this._audioEndedCallbackBound, false);
  804. playTask.audio.play();
  805. playTask.status = cc.PlayingTaskStatus.playing;
  806. },
  807. _getPlayingTaskFromList:function(audioID){
  808. var locPlayList = this._playingList;
  809. for(var i = 0, len = locPlayList.length;i< len;i++){
  810. if(locPlayList[i].id === audioID)
  811. return locPlayList[i];
  812. }
  813. return null;
  814. },
  815. _getNextTaskToPlay: function(){
  816. var locPlayingList = this._playingList;
  817. for(var i = locPlayingList.length -1; i >= 0; i--){
  818. var selTask = locPlayingList[i];
  819. if(selTask.status === cc.PlayingTaskStatus.waiting)
  820. return selTask;
  821. }
  822. return null;
  823. },
  824. _playingNextTask:function(){
  825. var locCurrentTask = this._currentTask = this._getNextTaskToPlay();
  826. if(locCurrentTask){
  827. locCurrentTask.status = cc.PlayingTaskStatus.playing;
  828. locCurrentTask.audio.play();
  829. } else {
  830. if(this._isPauseForList){
  831. this._isPauseForList = false;
  832. this.resumeMusic();
  833. }
  834. }
  835. },
  836. _deletePlayingTaskFromList: function(audioID){
  837. var locPlayList = this._playingList;
  838. for(var i = 0, len = locPlayList.length;i< len;i++){
  839. var selTask = locPlayList[i];
  840. if(selTask.id === audioID){
  841. locPlayList.splice(i,1);
  842. if(selTask == this._currentTask)
  843. this._playingNextTask();
  844. return;
  845. }
  846. }
  847. },
  848. _pausePlayingTaskFromList: function (audioID) {
  849. var locPlayList = this._playingList;
  850. for (var i = 0, len = locPlayList.length; i < len; i++) {
  851. var selTask = locPlayList[i];
  852. if (selTask.id === audioID) {
  853. selTask.status = cc.PlayingTaskStatus.pause;
  854. if (selTask == this._currentTask)
  855. this._playingNextTask();
  856. return;
  857. }
  858. }
  859. },
  860. _resumePlayingTaskFromList: function(audioID){
  861. var locPlayList = this._playingList;
  862. for (var i = 0, len = locPlayList.length; i < len; i++) {
  863. var selTask = locPlayList[i];
  864. if (selTask.id === audioID) {
  865. selTask.status = cc.PlayingTaskStatus.waiting;
  866. if(!this._currentTask){
  867. var locCurrentTask = this._getNextTaskToPlay();
  868. if(locCurrentTask){
  869. //pause music
  870. if(this.isMusicPlaying()){
  871. this._checkFlag = false;
  872. this.pauseMusic();
  873. this._isPauseForList = true;
  874. }
  875. locCurrentTask.status = cc.PlayingTaskStatus.playing;
  876. locCurrentTask.audio.play();
  877. }
  878. }
  879. return;
  880. }
  881. }
  882. },
  883. /**
  884. * Play sound effect.
  885. * @param {String} path The path of the sound effect with filename extension.
  886. * @param {Boolean} loop Whether to loop the effect playing, default value is false
  887. * @return {Number|null} the audio id
  888. * @example
  889. * //example
  890. * var soundId = cc.AudioEngine.getInstance().playEffect(path);
  891. */
  892. playEffect: function (path, loop) {
  893. if (!this._soundSupported)
  894. return null;
  895. var keyname = this._getPathWithoutExt(path), actExt;
  896. if (this._soundList[keyname])
  897. actExt = this._soundList[keyname].ext;
  898. else
  899. actExt = this._getExtFromFullPath(path);
  900. var reclaim = this._getEffectList(keyname), au;
  901. if (reclaim.length > 0) {
  902. for (var i = 0; i < reclaim.length; i++) {
  903. //if one of the effect ended, play it
  904. if (reclaim[i].ended) {
  905. au = reclaim[i];
  906. au.currentTime = 0;
  907. if (window.chrome)
  908. au.load();
  909. break;
  910. }
  911. }
  912. }
  913. if (!au) {
  914. if (reclaim.length >= this._maxAudioInstance) {
  915. cc.log("Error: " + path + " greater than " + this._maxAudioInstance);
  916. return null;
  917. }
  918. au = new Audio(keyname + "." + actExt);
  919. au.volume = this._effectsVolume;
  920. reclaim.push(au);
  921. }
  922. var playingTask = new cc.PlayingTask(this._audioID++, au, loop);
  923. this._pushingPlayingTask(playingTask);
  924. this._audioIDList[playingTask.id] = au;
  925. return playingTask.id;
  926. },
  927. /**
  928. * Pause playing sound effect.
  929. * @param {Number} audioID The return value of function playEffect.
  930. * @example
  931. * //example
  932. * cc.AudioEngine.getInstance().pauseEffect(audioID);
  933. */
  934. pauseEffect:function (audioID) {
  935. if (audioID == null) return;
  936. var strID = audioID.toString();
  937. if (this._audioIDList[strID]) {
  938. var au = this._audioIDList[strID];
  939. if (!au.ended) au.pause();
  940. }
  941. this._pausePlayingTaskFromList(audioID);
  942. },
  943. /**
  944. * Pause all playing sound effect.
  945. * @example
  946. * //example
  947. * cc.AudioEngine.getInstance().pauseAllEffects();
  948. */
  949. pauseAllEffects:function () {
  950. var tmpArr, au;
  951. var locEffectList = this._effectList;
  952. for (var selKey in locEffectList) {
  953. tmpArr = locEffectList[selKey];
  954. for (var j = 0; j < tmpArr.length; j++) {
  955. au = tmpArr[j];
  956. if (!au.ended) au.pause();
  957. }
  958. }
  959. var locPlayTask = this._playingList;
  960. for(var i = 0, len = locPlayTask.length; i < len; i++)
  961. locPlayTask[i].status = cc.PlayingTaskStatus.pause;
  962. this._currentTask = null;
  963. if(this._isPauseForList){
  964. this._isPauseForList = false;
  965. this.resumeMusic();
  966. }
  967. },
  968. /**
  969. * Resume playing sound effect.
  970. * @param {Number} audioID The return value of function playEffect.
  971. * @audioID
  972. * //example
  973. * cc.AudioEngine.getInstance().resumeEffect(audioID);
  974. */
  975. resumeEffect:function (audioID) {
  976. if (audioID == null) return;
  977. if (this._audioIDList[audioID]) {
  978. var au = this._audioIDList[audioID];
  979. if (!au.ended)
  980. au.play();
  981. }
  982. this._resumePlayingTaskFromList(audioID);
  983. },
  984. /**
  985. * Resume all playing sound effect
  986. * @example
  987. * //example
  988. * cc.AudioEngine.getInstance().resumeAllEffects();
  989. */
  990. resumeAllEffects:function () {
  991. var tmpArr, au;
  992. var locEffectList = this._effectList;
  993. for (var selKey in locEffectList) {
  994. tmpArr = locEffectList[selKey];
  995. if (tmpArr.length > 0) {
  996. for (var j = 0; j < tmpArr.length; j++) {
  997. au = tmpArr[j];
  998. if (!au.ended) au.play();
  999. }
  1000. }
  1001. }
  1002. var locPlayingList = this._playingList;
  1003. for(var i = 0, len = locPlayingList.length; i < len; i++){
  1004. var selTask = locPlayingList[i];
  1005. if(selTask.status === cc.PlayingTaskStatus.pause)
  1006. selTask.status = cc.PlayingTaskStatus.waiting;
  1007. }
  1008. if(this._currentTask == null){
  1009. var locCurrentTask = this._getNextTaskToPlay();
  1010. if(locCurrentTask){
  1011. //pause music
  1012. if(this.isMusicPlaying()){
  1013. this._checkFlag = false;
  1014. this.pauseMusic();
  1015. this._isPauseForList = true;
  1016. }
  1017. locCurrentTask.status = cc.PlayingTaskStatus.playing;
  1018. locCurrentTask.audio.play();
  1019. }
  1020. }
  1021. },
  1022. /**
  1023. * Stop playing sound effect.
  1024. * @param {Number} audioID The return value of function playEffect.
  1025. * @example
  1026. * //example
  1027. * cc.AudioEngine.getInstance().stopEffect(audioID);
  1028. */
  1029. stopEffect:function (audioID) {
  1030. if (audioID == null) return;
  1031. if (this._audioIDList[audioID]) {
  1032. var au = this._audioIDList[audioID];
  1033. if (!au.ended) {
  1034. au.removeEventListener('ended', this._audioEndedCallbackBound, false);
  1035. au.loop = false;
  1036. au.duration && (au.currentTime = au.duration);
  1037. }
  1038. }
  1039. this._deletePlayingTaskFromList(audioID);
  1040. },
  1041. /**
  1042. * Stop all playing sound effects.
  1043. * @example
  1044. * //example
  1045. * cc.AudioEngine.getInstance().stopAllEffects();
  1046. */
  1047. stopAllEffects:function () {
  1048. var tmpArr, au, locEffectList = this._effectList;
  1049. for (var i in locEffectList) {
  1050. tmpArr = locEffectList[i];
  1051. for (var j = 0; j < tmpArr.length; j++) {
  1052. au = tmpArr[j];
  1053. if (!au.ended) {
  1054. au.removeEventListener('ended', this._audioEndedCallbackBound, false);
  1055. au.loop = false;
  1056. au.duration && (au.currentTime = au.duration);
  1057. }
  1058. }
  1059. }
  1060. this._playingList.length = 0;
  1061. this._currentTask = null;
  1062. if(this._isPauseForList){
  1063. this._isPauseForList = false;
  1064. this.resumeMusic();
  1065. }
  1066. },
  1067. _pausePlaying: function(){
  1068. var locPausedPlayings = this._pausedPlayings, locSoundList = this._soundList, au;
  1069. if (!this._musicIsStopped && locSoundList[this._playingMusic]) {
  1070. au = locSoundList[this._playingMusic].audio;
  1071. if (!au.paused) {
  1072. au.pause();
  1073. cc.AudioEngine.isMusicPlaying = false;
  1074. locPausedPlayings.push(au);
  1075. }
  1076. }
  1077. this.stopAllEffects();
  1078. },
  1079. _resumePlaying: function(){
  1080. var locPausedPlayings = this._pausedPlayings, locSoundList = this._soundList, au;
  1081. if (!this._musicIsStopped && locSoundList[this._playingMusic]) {
  1082. au = locSoundList[this._playingMusic].audio;
  1083. if (locPausedPlayings.indexOf(au) !== -1) {
  1084. au.play();
  1085. au.addEventListener("pause", this._musicListenerBound, false);
  1086. cc.AudioEngine.isMusicPlaying = true;
  1087. }
  1088. }
  1089. locPausedPlayings.length = 0;
  1090. }
  1091. });
  1092. /**
  1093. * The entity stored in cc.WebAudioEngine, representing a sound object
  1094. */
  1095. cc.WebAudioSFX = function(key, sourceNode, volumeNode, startTime, pauseTime) {
  1096. // the name of the relevant audio resource
  1097. this.key = key;
  1098. // the node used in Web Audio API in charge of the source data
  1099. this.sourceNode = sourceNode;
  1100. // the node used in Web Audio API in charge of volume
  1101. this.volumeNode = volumeNode;
  1102. /*
  1103. * when playing started from beginning, startTime is set to the current time of AudioContext.currentTime
  1104. * when paused, pauseTime is set to the current time of AudioContext.currentTime
  1105. * so how long the music has been played can be calculated
  1106. * these won't be used in other cases
  1107. */
  1108. this.startTime = startTime || 0;
  1109. this.pauseTime = pauseTime || 0;
  1110. // by only sourceNode's playbackState, it cannot distinguish finished state from paused state
  1111. this.isPaused = false;
  1112. };
  1113. /**
  1114. * The Audio Engine implementation via Web Audio API.
  1115. * @class
  1116. * @extends cc.AudioEngine
  1117. */
  1118. cc.WebAudioEngine = cc.AudioEngine.extend(/** @lends cc.WebAudioEngine# */{
  1119. // the Web Audio Context
  1120. _ctx: null,
  1121. // containing all binary buffers of loaded audio resources
  1122. _audioData: null,
  1123. /*
  1124. * Issue: When loading two resources with different suffixes asynchronously, the second one might start loading
  1125. * when the first one is already loading!
  1126. * To avoid this duplication, loading synchronously somehow doesn't work. _ctx.decodeAudioData() would throw an
  1127. * exception "DOM exception 12", it should be a bug of the browser.
  1128. * So just add something to mark some audios as LOADING so as to avoid duplication.
  1129. */
  1130. _audiosLoading: null,
  1131. // the volume applied to the music
  1132. _musicVolume: 1,
  1133. // the effects being played: { key => [cc.WebAudioSFX] }, many effects of the same resource may be played simultaneously
  1134. _effects: null,
  1135. /*
  1136. * _canPlay is a property in cc.SimpleAudioEngine, but not used in cc.WebAudioEngine.
  1137. * Only those which support Web Audio API will be using this cc.WebAudioEngine, so no need to add an extra check.
  1138. */
  1139. // _canPlay: true,
  1140. /*
  1141. * _maxAudioInstance is also a property in cc.SimpleAudioEngine, but not used here
  1142. */
  1143. // _maxAudioInstance: 10,
  1144. /**
  1145. * Constructor
  1146. */
  1147. ctor: function() {
  1148. cc.AudioEngine.prototype.ctor.call(this);
  1149. this._audioData = {};
  1150. this._audiosLoading = {};
  1151. this._effects = {};
  1152. },
  1153. /**
  1154. * Initialization
  1155. * @return {Boolean}
  1156. */
  1157. init: function() {
  1158. /*
  1159. * browser has proved to support Web Audio API in miniFramework.js
  1160. * only in that case will cc.WebAudioEngine be chosen to run, thus the following is guaranteed to work
  1161. */
  1162. this._ctx = new (window.AudioContext || window.webkitAudioContext || window.mozAudioContext)();
  1163. // gather capabilities information, enable sound if any of the audio format is supported
  1164. var capabilities = {};
  1165. this._checkCanPlay(capabilities);
  1166. var formats = ["ogg", "mp3", "wav", "mp4", "m4a"], locSupportedFormat = this._supportedFormat;
  1167. for (var idx in formats) {
  1168. var name = formats[idx];
  1169. if (capabilities[name])
  1170. locSupportedFormat.push(name);
  1171. }
  1172. this._soundSupported = locSupportedFormat.length > 0;
  1173. return this._soundSupported;
  1174. },
  1175. /**
  1176. * Using XMLHttpRequest to retrieve the resource data from server.
  1177. * Not using cc.FileUtils.getByteArrayFromFile() because it is synchronous,
  1178. * so doing the retrieving here is more handful.
  1179. * @param {String} url The url to retrieve data
  1180. * @param {Object} onSuccess The callback to run when retrieving succeeds, the binary data array is passed into it
  1181. * @param {Object} onError The callback to run when retrieving fails
  1182. * @private
  1183. */
  1184. _fetchData: function(url, onSuccess, onError) {
  1185. // currently, only the webkit browsers support Web Audio API, so it should be fine just writing like this.
  1186. var req = new window.XMLHttpRequest();
  1187. var realPath = this._resPath + url;
  1188. req.open('GET', realPath, true);
  1189. req.responseType = 'arraybuffer';
  1190. var engine = this;
  1191. req.onload = function() {
  1192. // when context decodes the array buffer successfully, call onSuccess
  1193. engine._ctx.decodeAudioData(req.response, onSuccess, onError);
  1194. };
  1195. req.onerror = onError;
  1196. req.send();
  1197. },
  1198. /**
  1199. * Preload music resource.<br />
  1200. * This method is called when cc.Loader preload resources.
  1201. * @param {String} path The path of the music file with filename extension.
  1202. * @param {Function} selector
  1203. * @param {Object} target
  1204. */
  1205. preloadSound: function(path, selector, target) {
  1206. if (!this._soundSupported)
  1207. return;
  1208. var extName = this._getExtFromFullPath(path);
  1209. var keyName = this._getPathWithoutExt(path);
  1210. // not supported, already loaded, already loading
  1211. if (this._audioData[keyName] || this._audiosLoading[keyName] || !this.isFormatSupported(extName)) {
  1212. cc.doCallback(selector, target);
  1213. return;
  1214. }
  1215. this._audiosLoading[keyName] = true;
  1216. var engine = this;
  1217. this._fetchData(path, function(buffer) {
  1218. // resource fetched, in @param buffer
  1219. engine._audioData[keyName] = buffer;
  1220. delete engine._audiosLoading[keyName];
  1221. cc.doCallback(selector, target);
  1222. }, function() {
  1223. // resource fetching failed
  1224. delete engine._audiosLoading[keyName];
  1225. cc.doCallback(selector, target, path);
  1226. });
  1227. },
  1228. /**
  1229. * Init a new WebAudioSFX and play it, return this WebAudioSFX object
  1230. * assuming that key exists in this._audioData
  1231. * @param {String} key
  1232. * @param {Boolean} loop Default value is false
  1233. * @param {Number} volume 0.0 - 1.0, default value is 1.0
  1234. * @param {Number} [offset] Where to start playing (in seconds)
  1235. * @private
  1236. */
  1237. _beginSound: function(key, loop, volume, offset) {
  1238. var sfxCache = new cc.WebAudioSFX();
  1239. loop = loop == null ? false : loop;
  1240. volume = volume == null ? 1 : volume;
  1241. offset = offset || 0;
  1242. var locCtx = this._ctx;
  1243. sfxCache.key = key;
  1244. sfxCache.sourceNode = this._ctx.createBufferSource();
  1245. sfxCache.sourceNode.buffer = this._audioData[key];
  1246. sfxCache.sourceNode.loop = loop;
  1247. if(locCtx.createGain)
  1248. sfxCache.volumeNode = this._ctx.createGain();
  1249. else
  1250. sfxCache.volumeNode = this._ctx.createGainNode();
  1251. sfxCache.volumeNode.gain.value = volume;
  1252. sfxCache.sourceNode.connect(sfxCache.volumeNode);
  1253. sfxCache.volumeNode.connect(this._ctx.destination);
  1254. /*
  1255. * Safari on iOS 6 only supports noteOn(), noteGrainOn(), and noteOff() now.(iOS 6.1.3)
  1256. * The latest version of chrome has supported start() and stop()
  1257. * start() & stop() are specified in the latest specification (written on 04/26/2013)
  1258. * Reference: https://dvcs.w3.org/hg/audio/raw-file/tip/webaudio/specification.html
  1259. * noteOn(), noteGrainOn(), and noteOff() are specified in Draft 13 version (03/13/2012)
  1260. * Reference: http://www.w3.org/2011/audio/drafts/2WD/Overview.html
  1261. */
  1262. if (sfxCache.sourceNode.start) {
  1263. // starting from offset means resuming from where it paused last time
  1264. sfxCache.sourceNode.start(0, offset);
  1265. } else if (sfxCache.sourceNode.noteGrainOn) {
  1266. var duration = sfxCache.sourceNode.buffer.duration;
  1267. if (loop) {
  1268. /*
  1269. * On Safari on iOS 6, if loop == true, the passed in @param duration will be the duration from now on.
  1270. * In other words, the sound will keep playing the rest of the music all the time.
  1271. * On latest chrome desktop version, the passed in duration will only be the duration in this cycle.
  1272. * Now that latest chrome would have start() method, it is prepared for iOS here.
  1273. */
  1274. sfxCache.sourceNode.noteGrainOn(0, offset, duration);
  1275. } else {
  1276. sfxCache.sourceNode.noteGrainOn(0, offset, duration - offset);
  1277. }
  1278. } else {
  1279. // if only noteOn() is supported, resuming sound will NOT work
  1280. sfxCache.sourceNode.noteOn(0);
  1281. }
  1282. // currentTime - offset is necessary for pausing multiple times!
  1283. sfxCache.startTime = this._ctx.currentTime - offset;
  1284. sfxCache.pauseTime = sfxCache.startTime;
  1285. sfxCache.isPaused = false;
  1286. return sfxCache;
  1287. },
  1288. /**
  1289. * <p>
  1290. * According to the spec: dvcs.w3.org/hg/audio/raw-file/tip/webaudio/specification.html <br/>
  1291. * const unsigned short UNSCHEDULED_STATE = 0; <br/>
  1292. * const unsigned short SCHEDULED_STATE = 1; <br/>
  1293. * const unsigned short PLAYING_STATE = 2; // this means it is playing <br/>
  1294. * const unsigned short FINISHED_STATE = 3; <br/>
  1295. * However, the older specification doesn't include this property, such as this one: http://www.w3.org/2011/audio/drafts/2WD/Overview.html
  1296. * </p>
  1297. * @param {Object} sfxCache Assuming not null
  1298. * @returns {Boolean} Whether sfxCache is playing or not
  1299. * @private
  1300. */
  1301. _isSoundPlaying: function(sfxCache) {
  1302. return sfxCache.sourceNode.playbackState == 2;
  1303. },
  1304. /**
  1305. * To distinguish 3 kinds of status for each sound (PLAYING, PAUSED, FINISHED), _isSoundPlaying() is not enough
  1306. * @param {Object} sfxCache Assuming not null
  1307. * @returns {Boolean}
  1308. * @private
  1309. */
  1310. _isSoundPaused: function(sfxCache) {
  1311. // checking _isSoundPlaying() won't hurt
  1312. return this._isSoundPlaying(sfxCache) ? false : sfxCache.isPaused;
  1313. },
  1314. /**
  1315. * Whether it is playing any music
  1316. * @return {Boolean} If is playing return true,or return false.
  1317. * @example
  1318. * //example
  1319. * if (cc.AudioEngine.getInstance().isMusicPlaying()) {
  1320. * cc.log("music is playing");
  1321. * }
  1322. * else {
  1323. * cc.log("music is not playing");
  1324. * }
  1325. */
  1326. isMusicPlaying: function () {
  1327. /*
  1328. * cc.AudioEngine.isMusicPlaying property is not going to be used here in cc.WebAudioEngine
  1329. * that is only used in cc.SimpleAudioEngine
  1330. * WebAudioEngine uses Web Audio API which contains a playbackState property in AudioBufferSourceNode
  1331. * So there is also no need to be any method like setMusicPlaying(), it is done automatically
  1332. */
  1333. return this._playingMusic ? this._isSoundPlaying(this._playingMusic) : false;
  1334. },
  1335. /**
  1336. * Play music.
  1337. * @param {String} path The path of the music file without filename extension.
  1338. * @param {Boolean} loop Whether the music loop or not.
  1339. * @example
  1340. * //example
  1341. * cc.AudioEngine.getInstance().playMusic(path, false);
  1342. */
  1343. playMusic: function (path, loop) {
  1344. var keyName = this._getPathWithoutExt(path);
  1345. var extName = this._getExtFromFullPath(path);
  1346. loop = loop || false;
  1347. if (this._playingMusic) {
  1348. // there is a music being played currently, stop it (may be paused)
  1349. this.stopMusic();
  1350. }
  1351. if (this._audioData[keyName]) {
  1352. // already loaded, just play it
  1353. this._playingMusic = this._beginSound(keyName, loop, this._musicVolume);
  1354. } else if (!this._audiosLoading[keyName] && this.isFormatSupported(extName)) {
  1355. // load now only if the type is supported and it is not being loaded currently
  1356. this._audiosLoading[keyName] = true;
  1357. var engine = this;
  1358. this._fetchData(path, function(buffer) {
  1359. // resource fetched, save it and call playMusic() again, this time it should be alright
  1360. engine._audioData[keyName] = buffer;
  1361. delete engine._audiosLoading[keyName];
  1362. engine.playMusic(path, loop);
  1363. }, function() {
  1364. // resource fetching failed, doing nothing here
  1365. delete engine._audiosLoading[keyName];
  1366. /*
  1367. * Potential Bug: if fetching data fails every time, loading will be tried again and again.
  1368. * Preloading would prevent this issue: if it fails to fetch, preloading procedure will not achieve 100%.
  1369. */
  1370. });
  1371. }
  1372. },
  1373. /**
  1374. * Ends a sound, call stop() or noteOff() accordingly
  1375. * @param {Object} sfxCache Assuming not null
  1376. * @private
  1377. */
  1378. _endSound: function(sfxCache) {
  1379. if (sfxCache.sourceNode.playbackState && sfxCache.sourceNode.playbackState == 3)
  1380. return;
  1381. if (sfxCache.sourceNode.stop) {
  1382. sfxCache.sourceNode.stop(0);
  1383. } else {
  1384. sfxCache.sourceNode.noteOff(0);
  1385. }
  1386. // Do not call disconnect()! Otherwise the sourceNode's playbackState may not be updated correctly
  1387. // sfxCache.sourceNode.disconnect();
  1388. // sfxCache.volumeNode.disconnect();
  1389. },
  1390. /**
  1391. * Stop playing music.
  1392. * @param {Boolean} [releaseData] If release the music data or not.As default value is false.
  1393. * @example
  1394. * //example
  1395. * cc.AudioEngine.getInstance().stopMusic();
  1396. */
  1397. stopMusic: function(releaseData) {
  1398. // can stop when it's playing/paused
  1399. var locMusic = this._playingMusic;
  1400. if (!locMusic)
  1401. return;
  1402. var key = locMusic.key;
  1403. this._endSound(locMusic);
  1404. this._playingMusic = null;
  1405. if (releaseData)
  1406. delete this._audioData[key];
  1407. },
  1408. /**
  1409. * Used in pauseMusic() & pauseEffect() & pauseAllEffects()
  1410. * @param {Object} sfxCache Assuming not null
  1411. * @private
  1412. */
  1413. _pauseSound: function(sfxCache) {
  1414. sfxCache.pauseTime = this._ctx.currentTime;
  1415. sfxCache.isPaused = true;
  1416. this._endSound(sfxCache);
  1417. },
  1418. /**
  1419. * Pause playing music.
  1420. * @example
  1421. * //example
  1422. * cc.AudioEngine.getInstance().pauseMusic();
  1423. */
  1424. pauseMusic: function() {
  1425. // can pause only when it's playing
  1426. if (!this.isMusicPlaying())
  1427. return;
  1428. this._pauseSound(this._playingMusic);
  1429. },
  1430. /**
  1431. * Used in resumeMusic() & resumeEffect() & resumeAllEffects()
  1432. * @param {Object} paused The paused WebAudioSFX, assuming not null
  1433. * @param {Number} volume Can be getMusicVolume() or getEffectsVolume()
  1434. * @returns {Object} A new WebAudioSFX object representing the resumed sound
  1435. * @private
  1436. */
  1437. _resumeSound: function(paused, volume) {
  1438. var key = paused.key;
  1439. var loop = paused.sourceNode.loop;
  1440. // the paused sound may have been playing several loops, (pauseTime - startTime) may be too large
  1441. var offset = (paused.pauseTime - paused.startTime) % paused.sourceNode.buffer.duration;
  1442. return this._beginSound(key, loop, volume, offset);
  1443. },
  1444. /**
  1445. * Resume playing music.
  1446. * @example
  1447. * //example
  1448. * cc.AudioEngine.getInstance().resumeMusic();
  1449. */
  1450. resumeMusic: function() {
  1451. var locMusic = this._playingMusic;
  1452. // can resume only when it's paused
  1453. if (!locMusic || !this._isSoundPaused(locMusic)) {
  1454. return;
  1455. }
  1456. this._playingMusic = this._resumeSound(locMusic, this.getMusicVolume());
  1457. },
  1458. /**
  1459. * Rewind playing music.
  1460. * @example
  1461. * //example
  1462. * cc.AudioEngine.getInstance().rewindMusic();
  1463. */
  1464. rewindMusic: function() {
  1465. var locMusic = this._playingMusic;
  1466. // can rewind when it's playing or paused
  1467. if (!locMusic)
  1468. return;
  1469. var key = locMusic.key;
  1470. var loop = locMusic.sourceNode.loop;
  1471. var volume = this.getMusicVolume();
  1472. this._endSound(locMusic);
  1473. this._playingMusic = this._beginSound(key, loop, volume);
  1474. },
  1475. /**
  1476. * The volume of the music max value is 1.0,the min value is 0.0 .
  1477. * @return {Number}
  1478. * @example
  1479. * //example
  1480. * var volume = cc.AudioEngine.getInstance().getMusicVolume();
  1481. */
  1482. getMusicVolume: function() {
  1483. return this._musicVolume;
  1484. },
  1485. /**
  1486. * update volume, used in setMusicVolume() or setEffectsVolume()
  1487. * @param {Object} sfxCache Assuming not null
  1488. * @param {Number} volume
  1489. * @private
  1490. */
  1491. _setSoundVolume: function(sfxCache, volume) {
  1492. sfxCache.volumeNode.gain.value = volume;
  1493. },
  1494. /**
  1495. * Set the volume of music.
  1496. * @param {Number} volume Volume must be in 0.0~1.0 .
  1497. * @example
  1498. * //example
  1499. * cc.AudioEngine.getInstance().setMusicVolume(0.5);
  1500. */
  1501. setMusicVolume: function(volume) {
  1502. if (volume > 1)
  1503. volume = 1;
  1504. else if (volume < 0)
  1505. volume = 0;
  1506. if (this.getMusicVolume() == volume) // it is the same, no need to update
  1507. return;
  1508. this._musicVolume = volume;
  1509. if (this._playingMusic)
  1510. this._setSoundVolume(this._playingMusic, volume);
  1511. },
  1512. /**
  1513. * Play sound effect.
  1514. * @param {String} path The path of the sound effect with filename extension.
  1515. * @param {Boolean} loop Whether to loop the effect playing, default value is false
  1516. * @return {Number|null}
  1517. * @example
  1518. * //example
  1519. * cc.AudioEngine.getInstance().playEffect(path);
  1520. */
  1521. playEffect: function(path, loop) {
  1522. var keyName = this._getPathWithoutExt(path), extName = this._getExtFromFullPath(path), audioID;
  1523. loop = loop || false;
  1524. if (this._audioData[keyName]) {
  1525. // the resource has been loaded, just play it
  1526. var locEffects = this._effects;
  1527. if (!locEffects[keyName]) {
  1528. locEffects[keyName] = [];
  1529. }
  1530. // a list of sound objects from the same resource
  1531. var effectList = locEffects[keyName];
  1532. for (var idx = 0, len = effectList.length; idx < len; idx++) {
  1533. var sfxCache = effectList[idx];
  1534. if (!this._isSoundPlaying(sfxCache) && !this._isSoundPaused(sfxCache)) {
  1535. // not playing && not paused => it is finished, this position can be reused
  1536. effectList[idx] = this._beginSound(keyName, loop, this.getEffectsVolume());
  1537. audioID = this._audioID++;
  1538. this._audioIDList[audioID] = effectList[idx];
  1539. return audioID;
  1540. }
  1541. }
  1542. // no new sound was created to replace an old one in the list, then just append one
  1543. var addSFX = this._beginSound(keyName, loop, this.getEffectsVolume());
  1544. effectList.push(addSFX);
  1545. audioID = this._audioID++;
  1546. this._audioIDList[audioID] = addSFX;
  1547. return audioID;
  1548. } else if (!this._audiosLoading[keyName] && this.isFormatSupported(extName)) {
  1549. // load now only if the type is supported and it is not being loaded currently
  1550. this._audiosLoading[keyName] = true;
  1551. var engine = this;
  1552. audioID = this._audioID++;
  1553. this._audioIDList[audioID] = null;
  1554. this._fetchData(path, function(buffer) {
  1555. // resource fetched, save it and call playEffect() again, this time it should be alright
  1556. engine._audioData[keyName] = buffer;
  1557. delete engine._audiosLoading[keyName];
  1558. var asynSFX = engine._beginSound(keyName, loop, engine.getEffectsVolume());
  1559. engine._audioIDList[audioID] = asynSFX;
  1560. var locEffects = engine._effects;
  1561. if (!locEffects[keyName])
  1562. locEffects[keyName] = [];
  1563. locEffects[keyName].push(asynSFX);
  1564. }, function() {
  1565. // resource fetching failed, doing nothing here
  1566. delete engine._audiosLoading[keyName];
  1567. delete engine._audioIDList[audioID];
  1568. /*
  1569. * Potential Bug: if fetching data fails every time, loading will be tried again and again.
  1570. * Preloading would prevent this issue: if it fails to fetch, preloading procedure will not achieve 100%.
  1571. */
  1572. });
  1573. return audioID;
  1574. }
  1575. return null;
  1576. },
  1577. /**
  1578. * Set the volume of sound effects.
  1579. * @param {Number} volume Volume must be in 0.0~1.0 .
  1580. * @example
  1581. * //example
  1582. * cc.AudioEngine.getInstance().setEffectsVolume(0.5);
  1583. */
  1584. setEffectsVolume: function(volume) {
  1585. if (volume > 1)
  1586. volume = 1;
  1587. else if (volume < 0)
  1588. volume = 0;
  1589. if (this._effectsVolume == volume) {
  1590. // it is the same, no need to update
  1591. return;
  1592. }
  1593. this._effectsVolume = volume;
  1594. var locEffects = this._effects;
  1595. for (var key in locEffects) {
  1596. var effectList = locEffects[key];
  1597. for (var idx = 0, len = effectList.length; idx < len; idx++)
  1598. this._setSoundVolume(effectList[idx], volume);
  1599. }
  1600. },
  1601. /**
  1602. * Used in pauseEffect() and pauseAllEffects()
  1603. * @param {Array} effectList A list of sounds, each sound may be playing/paused/finished
  1604. * @private
  1605. */
  1606. _pauseSoundList: function(effectList) {
  1607. for (var idx = 0, len = effectList.length; idx < len; idx++) {
  1608. var sfxCache = effectList[idx];
  1609. if (sfxCache && this._isSoundPlaying(sfxCache))
  1610. this._pauseSound(sfxCache);
  1611. }
  1612. },
  1613. /**
  1614. * Pause playing sound effect.
  1615. * @param {Number} audioID The return value of function playEffect.
  1616. * @example
  1617. * //example
  1618. * cc.AudioEngine.getInstance().pauseEffect(audioID);
  1619. */
  1620. pauseEffect: function(audioID) {
  1621. if (audioID == null)
  1622. return;
  1623. if (this._audioIDList[audioID]){
  1624. var sfxCache = this._audioIDList[audioID];
  1625. if (sfxCache && this._isSoundPlaying(sfxCache))
  1626. this._pauseSound(sfxCache);
  1627. }
  1628. },
  1629. /**
  1630. * Pause all playing sound effect.
  1631. * @example
  1632. * //example
  1633. * cc.AudioEngine.getInstance().pauseAllEffects();
  1634. */
  1635. pauseAllEffects: function() {
  1636. for (var key in this._effects) {
  1637. this._pauseSoundList(this._effects[key]);
  1638. }
  1639. },
  1640. /**
  1641. * Used in resumeEffect() and resumeAllEffects()
  1642. * @param {Array} effectList A list of sounds, each sound may be playing/paused/finished
  1643. * @param {Number} volume
  1644. * @private
  1645. */
  1646. _resumeSoundList: function(effectList, volume) {
  1647. for (var idx = 0, len = effectList.length; idx < len; idx++) {
  1648. var sfxCache = effectList[idx];
  1649. if (this._isSoundPaused(sfxCache)) {
  1650. effectList[idx] = this._resumeSound(sfxCache, volume);
  1651. this._updateEffectsList(sfxCache, effectList[idx]);
  1652. }
  1653. }
  1654. },
  1655. /**
  1656. * Resume playing sound effect.
  1657. * @param {Number} audioID The return value of function playEffect.
  1658. * @example
  1659. * //example
  1660. * cc.AudioEngine.getInstance().resumeEffect(audioID);
  1661. */
  1662. resumeEffect: function(audioID) {
  1663. if (audioID == null)
  1664. return;
  1665. if (this._audioIDList[audioID]){
  1666. var sfxCache = this._audioIDList[audioID];
  1667. if (sfxCache && this._isSoundPaused(sfxCache)){
  1668. this._audioIDList[audioID] = this._resumeSound(sfxCache, this.getEffectsVolume());
  1669. this._updateEffectsList(sfxCache, this._audioIDList[audioID]);
  1670. }
  1671. }
  1672. },
  1673. _updateEffectsList:function(oldSFX, newSFX){
  1674. var locEffects = this._effects, locEffectList;
  1675. for(var eKey in locEffects){
  1676. locEffectList = locEffects[eKey];
  1677. for(var i = 0; i< locEffectList.length; i++){
  1678. if(locEffectList[i] == oldSFX)
  1679. locEffectList[i] = newSFX;
  1680. }
  1681. }
  1682. },
  1683. /**
  1684. * Resume all playing sound effect
  1685. * @example
  1686. * //example
  1687. * cc.AudioEngine.getInstance().resumeAllEffects();
  1688. */
  1689. resumeAllEffects: function() {
  1690. var locEffects = this._effects;
  1691. for (var key in locEffects)
  1692. this._resumeSoundList(locEffects[key], this.getEffectsVolume());
  1693. },
  1694. /**
  1695. * Stop playing sound effect.
  1696. * @param {Number} audioID The return value of function playEffect.
  1697. * @example
  1698. * //example
  1699. * cc.AudioEngine.getInstance().stopEffect(audioID);
  1700. */
  1701. stopEffect: function(audioID) {
  1702. if (audioID == null)
  1703. return;
  1704. var locAudioIDList = this._audioIDList;
  1705. if (locAudioIDList[audioID])
  1706. this._endSound(locAudioIDList[audioID]);
  1707. },
  1708. /**
  1709. * Stop all playing sound effects.
  1710. * @example
  1711. * //example
  1712. * cc.AudioEngine.getInstance().stopAllEffects();
  1713. */
  1714. stopAllEffects: function() {
  1715. var locEffects = this._effects;
  1716. for (var key in locEffects) {
  1717. var effectList = locEffects[key];
  1718. for (var idx = 0, len = effectList.length; idx < len; idx++)
  1719. this._endSound(effectList[idx]);
  1720. /*
  1721. * Another way is to set this._effects = {} outside this for loop.
  1722. * However, the cc.Class.extend() put all properties in the prototype.
  1723. * If I reassign a new {} to it, that will be appear in the instance.
  1724. * In other words, the dict in prototype won't release its children.
  1725. */
  1726. delete locEffects[key];
  1727. }
  1728. },
  1729. /**
  1730. * Unload the preloaded effect from internal buffer
  1731. * @param {String} path
  1732. * @example
  1733. * //example
  1734. * cc.AudioEngine.getInstance().unloadEffect(EFFECT_FILE);
  1735. */
  1736. unloadEffect: function(path) {
  1737. if (!path)
  1738. return;
  1739. var keyName = this._getPathWithoutExt(path);
  1740. if (this._effects[keyName]){
  1741. var locEffect = this._effects[keyName];
  1742. delete this._effects[keyName];
  1743. var locAudioIDList = this._audioIDList;
  1744. for(var auID in locAudioIDList){
  1745. if(locEffect.indexOf(locAudioIDList[auID]) > -1){
  1746. this.stopEffect(auID);
  1747. delete locAudioIDList[auID];
  1748. }
  1749. }
  1750. }
  1751. if (this._audioData[keyName])
  1752. delete this._audioData[keyName];
  1753. },
  1754. _pausePlaying: function(){
  1755. var locPausedPlayings = this._pausedPlayings;
  1756. if (this.isMusicPlaying()){
  1757. locPausedPlayings.push(this._playingMusic);
  1758. this._pauseSound(this._playingMusic);
  1759. }
  1760. var locEffects = this._effects;
  1761. for (var selKey in locEffects) {
  1762. var selEffectList = locEffects[selKey];
  1763. for (var idx = 0, len = selEffectList.length; idx < len; idx++) {
  1764. var sfxCache = selEffectList[idx];
  1765. if (sfxCache && this._isSoundPlaying(sfxCache)) {
  1766. locPausedPlayings.push(sfxCache);
  1767. this._pauseSound(sfxCache);
  1768. }
  1769. }
  1770. }
  1771. },
  1772. _resumePlaying: function(){
  1773. var locPausedPlayings = this._pausedPlayings, locVolume = this.getMusicVolume();
  1774. var locMusic = this._playingMusic;
  1775. // can resume only when it's paused
  1776. if (locMusic && this._isSoundPaused(locMusic) && locPausedPlayings.indexOf(locMusic) != -1)
  1777. this._playingMusic = this._resumeSound(locMusic, locVolume);
  1778. var locEffects = this._effects;
  1779. for (var selKey in locEffects){
  1780. var selEffects = locEffects[selKey];
  1781. for (var idx = 0, len = selEffects.length; idx < len; idx++) {
  1782. var sfxCache = selEffects[idx];
  1783. if (this._isSoundPaused(sfxCache) &&locPausedPlayings.indexOf(sfxCache) != -1) {
  1784. selEffects[idx] = this._resumeSound(sfxCache, locVolume);
  1785. this._updateEffectsList(sfxCache, selEffects[idx]);
  1786. }
  1787. }
  1788. }
  1789. locPausedPlayings.length = 0;
  1790. }
  1791. });
  1792. cc.AudioEngine._instance = null;
  1793. cc.AudioEngine.isMusicPlaying = false;
  1794. /**
  1795. * Get the shared Engine object, it will new one when first time be called.
  1796. * @return {cc.AudioEngine}
  1797. */
  1798. cc.AudioEngine.getInstance = function () {
  1799. if (!this._instance) {
  1800. if (cc.Browser.supportWebAudio) {
  1801. this._instance = new cc.WebAudioEngine();
  1802. } else {
  1803. if (cc.Browser.multipleAudioWhiteList.indexOf(cc.Browser.type) !== -1)
  1804. this._instance = new cc.SimpleAudioEngine();
  1805. else
  1806. this._instance = new cc.SimpleAudioEngineForMobile();
  1807. }
  1808. this._instance.init();
  1809. }
  1810. return this._instance;
  1811. };
  1812. /**
  1813. * Stop all music and sound effects
  1814. * @example
  1815. * //example
  1816. * cc.AudioEngine.end();
  1817. */
  1818. cc.AudioEngine.end = function () {
  1819. if (this._instance) {
  1820. this._instance.stopMusic();
  1821. this._instance.stopAllEffects();
  1822. }
  1823. this._instance = null;
  1824. };