123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602 |
- /*
- * cocos2d-x http://www.cocos2d-x.org
- *
- * Copyright (c) 2010-2011 - cocos2d-x community
- * Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd.
- *
- * Portions Copyright (c) Microsoft Open Technologies, Inc.
- * All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and limitations under the License.
- */
- #include "audio/winrt/Audio.h"
- #include "platform/CCCommon.h"
- #include "audio/winrt/AudioSourceReader.h"
- inline void ThrowIfFailed(HRESULT hr)
- {
- if (FAILED(hr))
- {
- // Set a breakpoint on this line to catch DX API errors.
- throw Platform::Exception::CreateException(hr);
- }
- }
- void AudioEngineCallbacks::Initialize(Audio *audio)
- {
- m_audio = audio;
- }
- // Called in the event of a critical system error which requires XAudio2
- // to be closed down and restarted. The error code is given in error.
- void _stdcall AudioEngineCallbacks::OnCriticalError(HRESULT Error)
- {
- UNUSED_PARAM(Error);
- m_audio->SetEngineExperiencedCriticalError();
- };
- Audio::Audio() :
- m_backgroundID(0),
- m_soundEffctVolume(1.0f),
- m_backgroundMusicVolume(1.0f)
- {
- }
- void Audio::Initialize()
- {
- m_engineExperiencedCriticalError = false;
- m_musicEngine = nullptr;
- m_soundEffectEngine = nullptr;
- m_musicMasteringVoice = nullptr;
- m_soundEffectMasteringVoice = nullptr;
- }
- void Audio::CreateResources()
- {
- do
- {
- if (FAILED(XAudio2Create(&m_musicEngine)))
- {
- m_engineExperiencedCriticalError = true;
- break;
- }
- #if defined(_DEBUG)
- XAUDIO2_DEBUG_CONFIGURATION debugConfig = {0};
- debugConfig.BreakMask = XAUDIO2_LOG_ERRORS;
- debugConfig.TraceMask = XAUDIO2_LOG_ERRORS;
- m_musicEngine->SetDebugConfiguration(&debugConfig);
- #endif
- m_musicEngineCallback.Initialize(this);
- m_musicEngine->RegisterForCallbacks(&m_musicEngineCallback);
- // This sample plays the equivalent of background music, which we tag on the mastering voice as AudioCategory_GameMedia.
- // In ordinary usage, if we were playing the music track with no effects, we could route it entirely through
- // Media Foundation. Here we are using XAudio2 to apply a reverb effect to the music, so we use Media Foundation to
- // decode the data then we feed it through the XAudio2 pipeline as a separate Mastering Voice, so that we can tag it
- // as Game Media.
- // We default the mastering voice to 2 channels to simplify the reverb logic.
- if(FAILED(m_musicEngine->CreateMasteringVoice(&m_musicMasteringVoice, XAUDIO2_DEFAULT_CHANNELS, XAUDIO2_DEFAULT_SAMPLERATE, 0, nullptr, nullptr, AudioCategory_GameMedia)))
- {
- m_engineExperiencedCriticalError = true;
- break;
- }
- // Create a separate engine and mastering voice for sound effects in the sample
- // Games will use many voices in a complex graph for audio, mixing all effects down to a
- // single mastering voice.
- // We are creating an entirely new engine instance and mastering voice in order to tag
- // our sound effects with the audio category AudioCategory_GameEffects.
- if(FAILED(XAudio2Create(&m_soundEffectEngine)))
- {
- m_engineExperiencedCriticalError = true;
- break;
- }
-
- m_soundEffectEngineCallback.Initialize(this);
- m_soundEffectEngine->RegisterForCallbacks(&m_soundEffectEngineCallback);
- // We default the mastering voice to 2 channels to simplify the reverb logic.
- if(FAILED(m_soundEffectEngine->CreateMasteringVoice(&m_soundEffectMasteringVoice, XAUDIO2_DEFAULT_CHANNELS, XAUDIO2_DEFAULT_SAMPLERATE, 0, nullptr, nullptr, AudioCategory_GameEffects)))
- {
- m_engineExperiencedCriticalError = true;
- break;
- }
- } while (false);
- }
- unsigned int Audio::Hash(const char *key)
- {
- unsigned int len = static_cast<unsigned int>(strlen(key));
- const char *end=key+len;
- unsigned int hash;
- for (hash = 0; key < end; key++)
- {
- hash *= 16777619;
- hash ^= (unsigned int) (unsigned char) toupper(*key);
- }
- return (hash);
- }
- void Audio::ReleaseResources()
- {
- if (m_musicMasteringVoice != nullptr)
- {
- m_musicMasteringVoice->DestroyVoice();
- m_musicMasteringVoice = nullptr;
- }
- if (m_soundEffectMasteringVoice != nullptr)
- {
- m_soundEffectMasteringVoice->DestroyVoice();
- m_soundEffectMasteringVoice = nullptr;
- }
- for (auto& EffectIter : m_soundEffects)
- {
- if (EffectIter.second.m_soundEffectSourceVoice != nullptr)
- {
- EffectIter.second.m_soundEffectSourceVoice->DestroyVoice();
- EffectIter.second.m_soundEffectSourceVoice = nullptr;
- }
- }
- m_soundEffects.clear();
- m_musicEngine = nullptr;
- m_soundEffectEngine = nullptr;
- }
- void Audio::Start()
- {
- if (m_engineExperiencedCriticalError)
- {
- return;
- }
- if (! m_backgroundFile.empty())
- PlayBackgroundMusic(m_backgroundFile.c_str(), m_backgroundLoop);
- }
- // This sample processes audio buffers during the render cycle of the application.
- // As long as the sample maintains a high-enough frame rate, this approach should
- // not glitch audio. In game code, it is best for audio buffers to be processed
- // on a separate thread that is not synced to the main render loop of the game.
- void Audio::Render()
- {
- if (m_engineExperiencedCriticalError)
- {
- ReleaseResources();
- Initialize();
- CreateResources();
- Start();
- if (m_engineExperiencedCriticalError)
- {
- return;
- }
- }
- }
- void Audio::PlayBackgroundMusic(const char* pszFilePath, bool bLoop)
- {
- m_backgroundFile = pszFilePath;
- m_backgroundLoop = bLoop;
- if (m_engineExperiencedCriticalError) {
- return;
- }
- StopBackgroundMusic(true);
- PlaySoundEffect(pszFilePath, bLoop, m_backgroundID, true);
- }
- void Audio::StopBackgroundMusic(bool bReleaseData)
- {
- if (m_engineExperiencedCriticalError) {
- return;
- }
- StopSoundEffect(m_backgroundID);
- if (bReleaseData){
- UnloadSoundEffect(m_backgroundID);
- RemoveFromList(m_backgroundID);
- }
- }
- void Audio::PauseBackgroundMusic()
- {
- if (m_engineExperiencedCriticalError) {
- return;
- }
- PauseSoundEffect(m_backgroundID);
- }
- void Audio::ResumeBackgroundMusic()
- {
- if (m_engineExperiencedCriticalError) {
- return;
- }
- ResumeSoundEffect(m_backgroundID);
- }
- void Audio::RewindBackgroundMusic()
- {
- if (m_engineExperiencedCriticalError) {
- return;
- }
- RewindSoundEffect(m_backgroundID);
- }
- bool Audio::IsBackgroundMusicPlaying()
- {
- return IsSoundEffectStarted(m_backgroundID) && !IsSoundEffectPaused(m_backgroundID);
- }
- void Audio::SetBackgroundVolume(float volume)
- {
- m_backgroundMusicVolume = volume;
- if (m_engineExperiencedCriticalError) {
- return;
- }
- if (m_soundEffects.end() != m_soundEffects.find(m_backgroundID))
- {
- m_soundEffects[m_backgroundID].m_soundEffectSourceVoice->SetVolume(volume);
- }
- }
- float Audio::GetBackgroundVolume()
- {
- return m_backgroundMusicVolume;
- }
- void Audio::SetSoundEffectVolume(float volume)
- {
- m_soundEffctVolume = volume;
- if (m_engineExperiencedCriticalError) {
- return;
- }
- for (auto& iter : m_soundEffects)
- {
- if (iter.first != m_backgroundID)
- iter.second.m_soundEffectSourceVoice->SetVolume(m_soundEffctVolume);
- }
- }
- float Audio::GetSoundEffectVolume()
- {
- return m_soundEffctVolume;
- }
- void Audio::PlaySoundEffect(const char* pszFilePath, bool bLoop, unsigned int& sound, bool isMusic)
- {
- sound = Hash(pszFilePath);
- if (m_soundEffects.end() == m_soundEffects.find(sound))
- {
- PreloadSoundEffect(pszFilePath, isMusic);
- }
- if (m_soundEffects.end() == m_soundEffects.find(sound))
- return;
- m_soundEffects[sound].m_audioBuffer.LoopCount = bLoop ? XAUDIO2_LOOP_INFINITE : 0;
- PlaySoundEffect(sound);
- }
- void Audio::PlaySoundEffect(unsigned int sound)
- {
- if (m_engineExperiencedCriticalError) {
- return;
- }
- if (m_soundEffects.end() == m_soundEffects.find(sound))
- return;
- StopSoundEffect(sound);
- if (FAILED(m_soundEffects[sound].m_soundEffectSourceVoice->SubmitSourceBuffer(&m_soundEffects[sound].m_audioBuffer)))
- {
- m_engineExperiencedCriticalError = true;
- }
- if (m_engineExperiencedCriticalError) {
- // If there's an error, then we'll recreate the engine on the next render pass
- return;
- }
- SoundEffectData* soundEffect = &m_soundEffects[sound];
- HRESULT hr = soundEffect->m_soundEffectSourceVoice->Start();
- if FAILED(hr)
- {
- m_engineExperiencedCriticalError = true;
- return;
- }
- m_soundEffects[sound].m_soundEffectStarted = true;
- m_soundEffects[sound].m_soundEffectPaused = false;
- }
- void Audio::StopSoundEffect(unsigned int sound)
- {
- if (m_engineExperiencedCriticalError) {
- return;
- }
- if (m_soundEffects.end() == m_soundEffects.find(sound))
- return;
- HRESULT hr = m_soundEffects[sound].m_soundEffectSourceVoice->Stop();
- HRESULT hr1 = m_soundEffects[sound].m_soundEffectSourceVoice->FlushSourceBuffers();
- if (FAILED(hr) || FAILED(hr1))
- {
- // If there's an error, then we'll recreate the engine on the next render pass
- m_engineExperiencedCriticalError = true;
- return;
- }
- m_soundEffects[sound].m_soundEffectStarted = false;
- m_soundEffects[sound].m_soundEffectPaused = false;
- }
- void Audio::PauseSoundEffect(unsigned int sound)
- {
- if (m_engineExperiencedCriticalError) {
- return;
- }
- if (m_soundEffects.end() == m_soundEffects.find(sound))
- return;
- HRESULT hr = m_soundEffects[sound].m_soundEffectSourceVoice->Stop();
- if FAILED(hr)
- {
- // If there's an error, then we'll recreate the engine on the next render pass
- m_engineExperiencedCriticalError = true;
- return;
- }
- m_soundEffects[sound].m_soundEffectPaused = true;
- }
- void Audio::ResumeSoundEffect(unsigned int sound)
- {
- if (m_engineExperiencedCriticalError) {
- return;
- }
- if (m_soundEffects.end() == m_soundEffects.find(sound))
- return;
- HRESULT hr = m_soundEffects[sound].m_soundEffectSourceVoice->Start();
- if FAILED(hr)
- {
- // If there's an error, then we'll recreate the engine on the next render pass
- m_engineExperiencedCriticalError = true;
- return;
- }
- m_soundEffects[sound].m_soundEffectPaused = false;
- }
- void Audio::RewindSoundEffect(unsigned int sound)
- {
- if (m_engineExperiencedCriticalError) {
- return;
- }
- if (m_soundEffects.end() == m_soundEffects.find(sound))
- return;
- StopSoundEffect(sound);
- PlaySoundEffect(sound);
- }
- void Audio::PauseAllSoundEffects()
- {
- if (m_engineExperiencedCriticalError) {
- return;
- }
- for (auto& iter : m_soundEffects)
- {
- if (iter.first != m_backgroundID)
- PauseSoundEffect(iter.first);
- }
- }
- void Audio::ResumeAllSoundEffects()
- {
- if (m_engineExperiencedCriticalError) {
- return;
- }
- for (auto& iter : m_soundEffects)
- {
- if (iter.first != m_backgroundID)
- ResumeSoundEffect(iter.first);
- }
- }
- void Audio::StopAllSoundEffects(bool bReleaseData)
- {
- if (m_engineExperiencedCriticalError) {
- return;
- }
- EffectList::iterator iter;
- for (iter = m_soundEffects.begin(); iter != m_soundEffects.end(); iter++)
- {
- if (iter->first != m_backgroundID){
- StopSoundEffect(iter->first);
- if (bReleaseData)
- {
- UnloadSoundEffect(iter->first);
- }
- }
- }
- if (bReleaseData)
- {
- for (iter = m_soundEffects.begin(); iter != m_soundEffects.end();)
- {
- if (iter->first != m_backgroundID){
- m_soundEffects.erase(iter++);
- }
- else
- {
- iter++;
- }
- }
- }
- }
- bool Audio::IsSoundEffectStarted(unsigned int sound)
- {
- if (m_soundEffects.end() == m_soundEffects.find(sound))
- return false;
- return m_soundEffects[sound].m_soundEffectStarted;
- }
- bool Audio::IsSoundEffectPaused(unsigned int sound)
- {
- if (m_soundEffects.end() == m_soundEffects.find(sound))
- return false;
- return m_soundEffects[sound].m_soundEffectPaused;
- }
- void Audio::PreloadSoundEffect(const char* pszFilePath, bool isMusic)
- {
- if (m_engineExperiencedCriticalError) {
- return;
- }
- int sound = Hash(pszFilePath);
- std::unique_ptr<cocos2d::experimental::AudioSourceReader> reader = std::make_unique<cocos2d::experimental::MP3Reader>();
- if (!reader) {
- return;
- }
- static_cast<cocos2d::experimental::MP3Reader*>(reader.get())->doLargeFileSupport(false);
- if (!reader->initialize(pszFilePath)) {
- return;
- }
- m_soundEffects[sound].m_soundID = sound;
- size_t bufferLength = reader->getTotalAudioBytes();
- WAVEFORMATEX wfx = reader->getWaveFormatInfo();
- cocos2d::experimental::AudioDataChunk chunk;
- if (!reader->consumeChunk(chunk)) {
- return;
- }
- m_soundEffects[sound].m_soundEffectBufferData = new (std::nothrow) BYTE[chunk._dataSize];
- if (nullptr == m_soundEffects[sound].m_soundEffectBufferData) {
- return;
- }
- m_soundEffects[sound].m_soundEffectBufferLength = chunk._dataSize;
- CopyMemory(m_soundEffects[sound].m_soundEffectBufferData, chunk._data->data(), chunk._dataSize);
- if (isMusic)
- {
- XAUDIO2_SEND_DESCRIPTOR descriptors[1];
- descriptors[0].pOutputVoice = m_musicMasteringVoice;
- descriptors[0].Flags = 0;
- XAUDIO2_VOICE_SENDS sends = {0};
- sends.SendCount = 1;
- sends.pSends = descriptors;
- if (FAILED(m_musicEngine->CreateSourceVoice(&m_soundEffects[sound].m_soundEffectSourceVoice,
- &wfx, 0, 1.0f, &m_voiceContext, &sends)))
- {
- m_engineExperiencedCriticalError = true;
- }
- //fix bug: set a initial volume
- m_soundEffects[sound].m_soundEffectSourceVoice->SetVolume(m_backgroundMusicVolume);
- } else
- {
- XAUDIO2_SEND_DESCRIPTOR descriptors[1];
- descriptors[0].pOutputVoice = m_soundEffectMasteringVoice;
- descriptors[0].Flags = 0;
- XAUDIO2_VOICE_SENDS sends = {0};
- sends.SendCount = 1;
- sends.pSends = descriptors;
- if(FAILED(m_soundEffectEngine->CreateSourceVoice(&m_soundEffects[sound].m_soundEffectSourceVoice,
- &wfx, 0, 1.0f, &m_voiceContext, &sends, nullptr)))
- {
- m_engineExperiencedCriticalError = true;
- }
- //fix bug: set a initial volume
- m_soundEffects[sound].m_soundEffectSourceVoice->SetVolume(m_soundEffctVolume);
- }
- m_soundEffects[sound].m_soundEffectSampleRate = wfx.nSamplesPerSec;
- // Queue in-memory buffer for playback
- ZeroMemory(&m_soundEffects[sound].m_audioBuffer, sizeof(m_soundEffects[sound].m_audioBuffer));
- m_soundEffects[sound].m_audioBuffer.AudioBytes = static_cast<UINT32>(m_soundEffects[sound].m_soundEffectBufferLength);
- m_soundEffects[sound].m_audioBuffer.pAudioData = m_soundEffects[sound].m_soundEffectBufferData;
- m_soundEffects[sound].m_audioBuffer.pContext = &m_soundEffects[sound];
- m_soundEffects[sound].m_audioBuffer.Flags = XAUDIO2_END_OF_STREAM;
- m_soundEffects[sound].m_audioBuffer.LoopCount = 0;
- }
- void Audio::UnloadSoundEffect(const char* pszFilePath)
- {
- int sound = Hash(pszFilePath);
- UnloadSoundEffect(sound);
- RemoveFromList(sound);
- }
- void Audio::UnloadSoundEffect(unsigned int sound)
- {
- if (m_engineExperiencedCriticalError) {
- return;
- }
- if (m_soundEffects.end() == m_soundEffects.find(sound))
- return;
- m_soundEffects[sound].m_soundEffectSourceVoice->DestroyVoice();
- if(m_soundEffects[sound].m_soundEffectBufferData)
- delete [] m_soundEffects[sound].m_soundEffectBufferData;
- m_soundEffects[sound].m_soundEffectBufferData = nullptr;
- m_soundEffects[sound].m_soundEffectSourceVoice = nullptr;
- m_soundEffects[sound].m_soundEffectStarted = false;
- m_soundEffects[sound].m_soundEffectPaused = false;
- ZeroMemory(&m_soundEffects[sound].m_audioBuffer, sizeof(m_soundEffects[sound].m_audioBuffer));
- }
- void Audio::RemoveFromList( unsigned int sound )
- {
- m_soundEffects.erase(sound);
- }
|