Audio.cpp 16 KB

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