Audio.cpp 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601
  1. /*
  2. * cocos2d-x http://www.cocos2d-x.org
  3. *
  4. * Copyright (c) 2010-2011 - cocos2d-x community
  5. *
  6. * Portions Copyright (c) Microsoft Open Technologies, Inc.
  7. * All Rights Reserved
  8. *
  9. * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License.
  10. * You may obtain a copy of the License at
  11. *
  12. * http://www.apache.org/licenses/LICENSE-2.0
  13. *
  14. * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an
  15. * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  16. * See the License for the specific language governing permissions and limitations under the License.
  17. */
  18. #include "audio/winrt/Audio.h"
  19. #include "platform/CCCommon.h"
  20. #include "audio/winrt/AudioSourceReader.h"
  21. inline void ThrowIfFailed(HRESULT hr)
  22. {
  23. if (FAILED(hr))
  24. {
  25. // Set a breakpoint on this line to catch DX API errors.
  26. throw Platform::Exception::CreateException(hr);
  27. }
  28. }
  29. void AudioEngineCallbacks::Initialize(Audio *audio)
  30. {
  31. m_audio = audio;
  32. }
  33. // Called in the event of a critical system error which requires XAudio2
  34. // to be closed down and restarted. The error code is given in error.
  35. void _stdcall AudioEngineCallbacks::OnCriticalError(HRESULT Error)
  36. {
  37. UNUSED_PARAM(Error);
  38. m_audio->SetEngineExperiencedCriticalError();
  39. };
  40. Audio::Audio() :
  41. m_backgroundID(0),
  42. m_soundEffctVolume(1.0f),
  43. m_backgroundMusicVolume(1.0f)
  44. {
  45. }
  46. void Audio::Initialize()
  47. {
  48. m_engineExperiencedCriticalError = false;
  49. m_musicEngine = nullptr;
  50. m_soundEffectEngine = nullptr;
  51. m_musicMasteringVoice = nullptr;
  52. m_soundEffectMasteringVoice = nullptr;
  53. }
  54. void Audio::CreateResources()
  55. {
  56. do
  57. {
  58. if (FAILED(XAudio2Create(&m_musicEngine)))
  59. {
  60. m_engineExperiencedCriticalError = true;
  61. break;
  62. }
  63. #if defined(_DEBUG)
  64. XAUDIO2_DEBUG_CONFIGURATION debugConfig = {0};
  65. debugConfig.BreakMask = XAUDIO2_LOG_ERRORS;
  66. debugConfig.TraceMask = XAUDIO2_LOG_ERRORS;
  67. m_musicEngine->SetDebugConfiguration(&debugConfig);
  68. #endif
  69. m_musicEngineCallback.Initialize(this);
  70. m_musicEngine->RegisterForCallbacks(&m_musicEngineCallback);
  71. // This sample plays the equivalent of background music, which we tag on the mastering voice as AudioCategory_GameMedia.
  72. // In ordinary usage, if we were playing the music track with no effects, we could route it entirely through
  73. // Media Foundation. Here we are using XAudio2 to apply a reverb effect to the music, so we use Media Foundation to
  74. // decode the data then we feed it through the XAudio2 pipeline as a separate Mastering Voice, so that we can tag it
  75. // as Game Media.
  76. // We default the mastering voice to 2 channels to simplify the reverb logic.
  77. if(FAILED(m_musicEngine->CreateMasteringVoice(&m_musicMasteringVoice, XAUDIO2_DEFAULT_CHANNELS, XAUDIO2_DEFAULT_SAMPLERATE, 0, nullptr, nullptr, AudioCategory_GameMedia)))
  78. {
  79. m_engineExperiencedCriticalError = true;
  80. break;
  81. }
  82. // Create a separate engine and mastering voice for sound effects in the sample
  83. // Games will use many voices in a complex graph for audio, mixing all effects down to a
  84. // single mastering voice.
  85. // We are creating an entirely new engine instance and mastering voice in order to tag
  86. // our sound effects with the audio category AudioCategory_GameEffects.
  87. if(FAILED(XAudio2Create(&m_soundEffectEngine)))
  88. {
  89. m_engineExperiencedCriticalError = true;
  90. break;
  91. }
  92. m_soundEffectEngineCallback.Initialize(this);
  93. m_soundEffectEngine->RegisterForCallbacks(&m_soundEffectEngineCallback);
  94. // We default the mastering voice to 2 channels to simplify the reverb logic.
  95. if(FAILED(m_soundEffectEngine->CreateMasteringVoice(&m_soundEffectMasteringVoice, XAUDIO2_DEFAULT_CHANNELS, XAUDIO2_DEFAULT_SAMPLERATE, 0, nullptr, nullptr, AudioCategory_GameEffects)))
  96. {
  97. m_engineExperiencedCriticalError = true;
  98. break;
  99. }
  100. } while (false);
  101. }
  102. unsigned int Audio::Hash(const char *key)
  103. {
  104. unsigned int len = static_cast<unsigned int>(strlen(key));
  105. const char *end=key+len;
  106. unsigned int hash;
  107. for (hash = 0; key < end; key++)
  108. {
  109. hash *= 16777619;
  110. hash ^= (unsigned int) (unsigned char) toupper(*key);
  111. }
  112. return (hash);
  113. }
  114. void Audio::ReleaseResources()
  115. {
  116. if (m_musicMasteringVoice != nullptr)
  117. {
  118. m_musicMasteringVoice->DestroyVoice();
  119. m_musicMasteringVoice = nullptr;
  120. }
  121. if (m_soundEffectMasteringVoice != nullptr)
  122. {
  123. m_soundEffectMasteringVoice->DestroyVoice();
  124. m_soundEffectMasteringVoice = nullptr;
  125. }
  126. for (auto& EffectIter : m_soundEffects)
  127. {
  128. if (EffectIter.second.m_soundEffectSourceVoice != nullptr)
  129. {
  130. EffectIter.second.m_soundEffectSourceVoice->DestroyVoice();
  131. EffectIter.second.m_soundEffectSourceVoice = nullptr;
  132. }
  133. }
  134. m_soundEffects.clear();
  135. m_musicEngine = nullptr;
  136. m_soundEffectEngine = nullptr;
  137. }
  138. void Audio::Start()
  139. {
  140. if (m_engineExperiencedCriticalError)
  141. {
  142. return;
  143. }
  144. if (! m_backgroundFile.empty())
  145. PlayBackgroundMusic(m_backgroundFile.c_str(), m_backgroundLoop);
  146. }
  147. // This sample processes audio buffers during the render cycle of the application.
  148. // As long as the sample maintains a high-enough frame rate, this approach should
  149. // not glitch audio. In game code, it is best for audio buffers to be processed
  150. // on a separate thread that is not synced to the main render loop of the game.
  151. void Audio::Render()
  152. {
  153. if (m_engineExperiencedCriticalError)
  154. {
  155. ReleaseResources();
  156. Initialize();
  157. CreateResources();
  158. Start();
  159. if (m_engineExperiencedCriticalError)
  160. {
  161. return;
  162. }
  163. }
  164. }
  165. void Audio::PlayBackgroundMusic(const char* pszFilePath, bool bLoop)
  166. {
  167. m_backgroundFile = pszFilePath;
  168. m_backgroundLoop = bLoop;
  169. if (m_engineExperiencedCriticalError) {
  170. return;
  171. }
  172. StopBackgroundMusic(true);
  173. PlaySoundEffect(pszFilePath, bLoop, m_backgroundID, true);
  174. }
  175. void Audio::StopBackgroundMusic(bool bReleaseData)
  176. {
  177. if (m_engineExperiencedCriticalError) {
  178. return;
  179. }
  180. StopSoundEffect(m_backgroundID);
  181. if (bReleaseData){
  182. UnloadSoundEffect(m_backgroundID);
  183. RemoveFromList(m_backgroundID);
  184. }
  185. }
  186. void Audio::PauseBackgroundMusic()
  187. {
  188. if (m_engineExperiencedCriticalError) {
  189. return;
  190. }
  191. PauseSoundEffect(m_backgroundID);
  192. }
  193. void Audio::ResumeBackgroundMusic()
  194. {
  195. if (m_engineExperiencedCriticalError) {
  196. return;
  197. }
  198. ResumeSoundEffect(m_backgroundID);
  199. }
  200. void Audio::RewindBackgroundMusic()
  201. {
  202. if (m_engineExperiencedCriticalError) {
  203. return;
  204. }
  205. RewindSoundEffect(m_backgroundID);
  206. }
  207. bool Audio::IsBackgroundMusicPlaying()
  208. {
  209. return IsSoundEffectStarted(m_backgroundID) && !IsSoundEffectPaused(m_backgroundID);
  210. }
  211. void Audio::SetBackgroundVolume(float volume)
  212. {
  213. m_backgroundMusicVolume = volume;
  214. if (m_engineExperiencedCriticalError) {
  215. return;
  216. }
  217. if (m_soundEffects.end() != m_soundEffects.find(m_backgroundID))
  218. {
  219. m_soundEffects[m_backgroundID].m_soundEffectSourceVoice->SetVolume(volume);
  220. }
  221. }
  222. float Audio::GetBackgroundVolume()
  223. {
  224. return m_backgroundMusicVolume;
  225. }
  226. void Audio::SetSoundEffectVolume(float volume)
  227. {
  228. m_soundEffctVolume = volume;
  229. if (m_engineExperiencedCriticalError) {
  230. return;
  231. }
  232. for (auto& iter : m_soundEffects)
  233. {
  234. if (iter.first != m_backgroundID)
  235. iter.second.m_soundEffectSourceVoice->SetVolume(m_soundEffctVolume);
  236. }
  237. }
  238. float Audio::GetSoundEffectVolume()
  239. {
  240. return m_soundEffctVolume;
  241. }
  242. void Audio::PlaySoundEffect(const char* pszFilePath, bool bLoop, unsigned int& sound, bool isMusic)
  243. {
  244. sound = Hash(pszFilePath);
  245. if (m_soundEffects.end() == m_soundEffects.find(sound))
  246. {
  247. PreloadSoundEffect(pszFilePath, isMusic);
  248. }
  249. if (m_soundEffects.end() == m_soundEffects.find(sound))
  250. return;
  251. m_soundEffects[sound].m_audioBuffer.LoopCount = bLoop ? XAUDIO2_LOOP_INFINITE : 0;
  252. PlaySoundEffect(sound);
  253. }
  254. void Audio::PlaySoundEffect(unsigned int sound)
  255. {
  256. if (m_engineExperiencedCriticalError) {
  257. return;
  258. }
  259. if (m_soundEffects.end() == m_soundEffects.find(sound))
  260. return;
  261. StopSoundEffect(sound);
  262. if (FAILED(m_soundEffects[sound].m_soundEffectSourceVoice->SubmitSourceBuffer(&m_soundEffects[sound].m_audioBuffer)))
  263. {
  264. m_engineExperiencedCriticalError = true;
  265. }
  266. if (m_engineExperiencedCriticalError) {
  267. // If there's an error, then we'll recreate the engine on the next render pass
  268. return;
  269. }
  270. SoundEffectData* soundEffect = &m_soundEffects[sound];
  271. HRESULT hr = soundEffect->m_soundEffectSourceVoice->Start();
  272. if FAILED(hr)
  273. {
  274. m_engineExperiencedCriticalError = true;
  275. return;
  276. }
  277. m_soundEffects[sound].m_soundEffectStarted = true;
  278. m_soundEffects[sound].m_soundEffectPaused = false;
  279. }
  280. void Audio::StopSoundEffect(unsigned int sound)
  281. {
  282. if (m_engineExperiencedCriticalError) {
  283. return;
  284. }
  285. if (m_soundEffects.end() == m_soundEffects.find(sound))
  286. return;
  287. HRESULT hr = m_soundEffects[sound].m_soundEffectSourceVoice->Stop();
  288. HRESULT hr1 = m_soundEffects[sound].m_soundEffectSourceVoice->FlushSourceBuffers();
  289. if (FAILED(hr) || FAILED(hr1))
  290. {
  291. // If there's an error, then we'll recreate the engine on the next render pass
  292. m_engineExperiencedCriticalError = true;
  293. return;
  294. }
  295. m_soundEffects[sound].m_soundEffectStarted = false;
  296. m_soundEffects[sound].m_soundEffectPaused = false;
  297. }
  298. void Audio::PauseSoundEffect(unsigned int sound)
  299. {
  300. if (m_engineExperiencedCriticalError) {
  301. return;
  302. }
  303. if (m_soundEffects.end() == m_soundEffects.find(sound))
  304. return;
  305. HRESULT hr = m_soundEffects[sound].m_soundEffectSourceVoice->Stop();
  306. if FAILED(hr)
  307. {
  308. // If there's an error, then we'll recreate the engine on the next render pass
  309. m_engineExperiencedCriticalError = true;
  310. return;
  311. }
  312. m_soundEffects[sound].m_soundEffectPaused = true;
  313. }
  314. void Audio::ResumeSoundEffect(unsigned int sound)
  315. {
  316. if (m_engineExperiencedCriticalError) {
  317. return;
  318. }
  319. if (m_soundEffects.end() == m_soundEffects.find(sound))
  320. return;
  321. HRESULT hr = m_soundEffects[sound].m_soundEffectSourceVoice->Start();
  322. if FAILED(hr)
  323. {
  324. // If there's an error, then we'll recreate the engine on the next render pass
  325. m_engineExperiencedCriticalError = true;
  326. return;
  327. }
  328. m_soundEffects[sound].m_soundEffectPaused = false;
  329. }
  330. void Audio::RewindSoundEffect(unsigned int sound)
  331. {
  332. if (m_engineExperiencedCriticalError) {
  333. return;
  334. }
  335. if (m_soundEffects.end() == m_soundEffects.find(sound))
  336. return;
  337. StopSoundEffect(sound);
  338. PlaySoundEffect(sound);
  339. }
  340. void Audio::PauseAllSoundEffects()
  341. {
  342. if (m_engineExperiencedCriticalError) {
  343. return;
  344. }
  345. for (auto& iter : m_soundEffects)
  346. {
  347. if (iter.first != m_backgroundID)
  348. PauseSoundEffect(iter.first);
  349. }
  350. }
  351. void Audio::ResumeAllSoundEffects()
  352. {
  353. if (m_engineExperiencedCriticalError) {
  354. return;
  355. }
  356. for (auto& iter : m_soundEffects)
  357. {
  358. if (iter.first != m_backgroundID)
  359. ResumeSoundEffect(iter.first);
  360. }
  361. }
  362. void Audio::StopAllSoundEffects(bool bReleaseData)
  363. {
  364. if (m_engineExperiencedCriticalError) {
  365. return;
  366. }
  367. EffectList::iterator iter;
  368. for (iter = m_soundEffects.begin(); iter != m_soundEffects.end(); iter++)
  369. {
  370. if (iter->first != m_backgroundID){
  371. StopSoundEffect(iter->first);
  372. if (bReleaseData)
  373. {
  374. UnloadSoundEffect(iter->first);
  375. }
  376. }
  377. }
  378. if (bReleaseData)
  379. {
  380. for (iter = m_soundEffects.begin(); iter != m_soundEffects.end();)
  381. {
  382. if (iter->first != m_backgroundID){
  383. m_soundEffects.erase(iter++);
  384. }
  385. else
  386. {
  387. iter++;
  388. }
  389. }
  390. }
  391. }
  392. bool Audio::IsSoundEffectStarted(unsigned int sound)
  393. {
  394. if (m_soundEffects.end() == m_soundEffects.find(sound))
  395. return false;
  396. return m_soundEffects[sound].m_soundEffectStarted;
  397. }
  398. bool Audio::IsSoundEffectPaused(unsigned int sound)
  399. {
  400. if (m_soundEffects.end() == m_soundEffects.find(sound))
  401. return false;
  402. return m_soundEffects[sound].m_soundEffectPaused;
  403. }
  404. void Audio::PreloadSoundEffect(const char* pszFilePath, bool isMusic)
  405. {
  406. if (m_engineExperiencedCriticalError) {
  407. return;
  408. }
  409. int sound = Hash(pszFilePath);
  410. std::unique_ptr<cocos2d::experimental::AudioSourceReader> reader = std::make_unique<cocos2d::experimental::MP3Reader>();
  411. if (!reader) {
  412. return;
  413. }
  414. static_cast<cocos2d::experimental::MP3Reader*>(reader.get())->doLargeFileSupport(false);
  415. if (!reader->initialize(pszFilePath)) {
  416. return;
  417. }
  418. m_soundEffects[sound].m_soundID = sound;
  419. size_t bufferLength = reader->getTotalAudioBytes();
  420. WAVEFORMATEX wfx = reader->getWaveFormatInfo();
  421. cocos2d::experimental::AudioDataChunk chunk;
  422. if (!reader->consumeChunk(chunk)) {
  423. return;
  424. }
  425. m_soundEffects[sound].m_soundEffectBufferData = new (std::nothrow) BYTE[chunk._dataSize];
  426. if (nullptr == m_soundEffects[sound].m_soundEffectBufferData) {
  427. return;
  428. }
  429. m_soundEffects[sound].m_soundEffectBufferLength = chunk._dataSize;
  430. CopyMemory(m_soundEffects[sound].m_soundEffectBufferData, chunk._data->data(), chunk._dataSize);
  431. if (isMusic)
  432. {
  433. XAUDIO2_SEND_DESCRIPTOR descriptors[1];
  434. descriptors[0].pOutputVoice = m_musicMasteringVoice;
  435. descriptors[0].Flags = 0;
  436. XAUDIO2_VOICE_SENDS sends = {0};
  437. sends.SendCount = 1;
  438. sends.pSends = descriptors;
  439. if (FAILED(m_musicEngine->CreateSourceVoice(&m_soundEffects[sound].m_soundEffectSourceVoice,
  440. &wfx, 0, 1.0f, &m_voiceContext, &sends)))
  441. {
  442. m_engineExperiencedCriticalError = true;
  443. }
  444. //fix bug: set a initial volume
  445. m_soundEffects[sound].m_soundEffectSourceVoice->SetVolume(m_backgroundMusicVolume);
  446. } else
  447. {
  448. XAUDIO2_SEND_DESCRIPTOR descriptors[1];
  449. descriptors[0].pOutputVoice = m_soundEffectMasteringVoice;
  450. descriptors[0].Flags = 0;
  451. XAUDIO2_VOICE_SENDS sends = {0};
  452. sends.SendCount = 1;
  453. sends.pSends = descriptors;
  454. if(FAILED(m_soundEffectEngine->CreateSourceVoice(&m_soundEffects[sound].m_soundEffectSourceVoice,
  455. &wfx, 0, 1.0f, &m_voiceContext, &sends, nullptr)))
  456. {
  457. m_engineExperiencedCriticalError = true;
  458. }
  459. //fix bug: set a initial volume
  460. m_soundEffects[sound].m_soundEffectSourceVoice->SetVolume(m_soundEffctVolume);
  461. }
  462. m_soundEffects[sound].m_soundEffectSampleRate = wfx.nSamplesPerSec;
  463. // Queue in-memory buffer for playback
  464. ZeroMemory(&m_soundEffects[sound].m_audioBuffer, sizeof(m_soundEffects[sound].m_audioBuffer));
  465. m_soundEffects[sound].m_audioBuffer.AudioBytes = static_cast<UINT32>(m_soundEffects[sound].m_soundEffectBufferLength);
  466. m_soundEffects[sound].m_audioBuffer.pAudioData = m_soundEffects[sound].m_soundEffectBufferData;
  467. m_soundEffects[sound].m_audioBuffer.pContext = &m_soundEffects[sound];
  468. m_soundEffects[sound].m_audioBuffer.Flags = XAUDIO2_END_OF_STREAM;
  469. m_soundEffects[sound].m_audioBuffer.LoopCount = 0;
  470. }
  471. void Audio::UnloadSoundEffect(const char* pszFilePath)
  472. {
  473. int sound = Hash(pszFilePath);
  474. UnloadSoundEffect(sound);
  475. RemoveFromList(sound);
  476. }
  477. void Audio::UnloadSoundEffect(unsigned int sound)
  478. {
  479. if (m_engineExperiencedCriticalError) {
  480. return;
  481. }
  482. if (m_soundEffects.end() == m_soundEffects.find(sound))
  483. return;
  484. m_soundEffects[sound].m_soundEffectSourceVoice->DestroyVoice();
  485. if(m_soundEffects[sound].m_soundEffectBufferData)
  486. delete [] m_soundEffects[sound].m_soundEffectBufferData;
  487. m_soundEffects[sound].m_soundEffectBufferData = nullptr;
  488. m_soundEffects[sound].m_soundEffectSourceVoice = nullptr;
  489. m_soundEffects[sound].m_soundEffectStarted = false;
  490. m_soundEffects[sound].m_soundEffectPaused = false;
  491. ZeroMemory(&m_soundEffects[sound].m_audioBuffer, sizeof(m_soundEffects[sound].m_audioBuffer));
  492. }
  493. void Audio::RemoveFromList( unsigned int sound )
  494. {
  495. m_soundEffects.erase(sound);
  496. }