2014-10-15 05:13:14 +08:00
|
|
|
/*
|
|
|
|
* cocos2d-x http://www.cocos2d-x.org
|
|
|
|
*
|
|
|
|
* Copyright (c) 2010-2011 - cocos2d-x community
|
|
|
|
*
|
|
|
|
* 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.
|
|
|
|
*/
|
|
|
|
|
2014-03-29 03:53:35 +08:00
|
|
|
#include "Audio.h"
|
2015-05-28 01:57:26 +08:00
|
|
|
#include "CCCommon.h"
|
|
|
|
#include "AudioSourceReader.h"
|
2014-03-29 03:53:35 +08:00
|
|
|
|
2014-10-15 05:13:14 +08:00
|
|
|
inline void ThrowIfFailed(HRESULT hr)
|
2014-03-29 03:53:35 +08:00
|
|
|
{
|
|
|
|
if (FAILED(hr))
|
|
|
|
{
|
2014-10-15 05:13:14 +08:00
|
|
|
// Set a breakpoint on this line to catch DX API errors.
|
2014-03-29 03:53:35 +08:00
|
|
|
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)
|
|
|
|
{
|
2014-10-15 05:13:14 +08:00
|
|
|
UNUSED_PARAM(Error);
|
2014-03-29 03:53:35 +08:00
|
|
|
m_audio->SetEngineExperiencedCriticalError();
|
|
|
|
};
|
|
|
|
|
|
|
|
Audio::Audio() :
|
|
|
|
m_backgroundID(0),
|
2014-10-15 05:13:14 +08:00
|
|
|
m_soundEffctVolume(1.0f),
|
2014-03-29 03:53:35 +08:00
|
|
|
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()
|
|
|
|
{
|
2015-06-20 15:20:15 +08:00
|
|
|
do
|
2014-03-29 03:53:35 +08:00
|
|
|
{
|
2015-06-20 15:20:15 +08:00
|
|
|
if (FAILED(XAudio2Create(&m_musicEngine)))
|
|
|
|
{
|
|
|
|
m_engineExperiencedCriticalError = true;
|
|
|
|
break;
|
|
|
|
}
|
2014-03-29 03:53:35 +08:00
|
|
|
|
|
|
|
#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.
|
2015-06-23 20:08:00 +08:00
|
|
|
if(FAILED(m_musicEngine->CreateMasteringVoice(&m_musicMasteringVoice, XAUDIO2_DEFAULT_CHANNELS, XAUDIO2_DEFAULT_SAMPLERATE, 0, nullptr, nullptr, AudioCategory_GameMedia)))
|
2015-06-20 15:20:15 +08:00
|
|
|
{
|
|
|
|
m_engineExperiencedCriticalError = true;
|
|
|
|
break;
|
|
|
|
}
|
2014-03-29 03:53:35 +08:00
|
|
|
|
|
|
|
// 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.
|
2015-06-23 20:08:00 +08:00
|
|
|
if(FAILED(XAudio2Create(&m_soundEffectEngine)))
|
2015-06-20 15:20:15 +08:00
|
|
|
{
|
|
|
|
m_engineExperiencedCriticalError = true;
|
|
|
|
break;
|
|
|
|
}
|
2014-03-29 03:53:35 +08:00
|
|
|
|
|
|
|
m_soundEffectEngineCallback.Initialize(this);
|
|
|
|
m_soundEffectEngine->RegisterForCallbacks(&m_soundEffectEngineCallback);
|
|
|
|
|
|
|
|
// We default the mastering voice to 2 channels to simplify the reverb logic.
|
2015-06-23 20:08:00 +08:00
|
|
|
if(FAILED(m_soundEffectEngine->CreateMasteringVoice(&m_soundEffectMasteringVoice, XAUDIO2_DEFAULT_CHANNELS, XAUDIO2_DEFAULT_SAMPLERATE, 0, nullptr, nullptr, AudioCategory_GameEffects)))
|
2015-06-20 15:20:15 +08:00
|
|
|
{
|
|
|
|
m_engineExperiencedCriticalError = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
} while (false);
|
2014-03-29 03:53:35 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
unsigned int Audio::Hash(const char *key)
|
|
|
|
{
|
|
|
|
unsigned int len = 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;
|
|
|
|
}
|
|
|
|
|
|
|
|
EffectList::iterator EffectIter = m_soundEffects.begin();
|
|
|
|
for (; EffectIter != m_soundEffects.end(); EffectIter++)
|
|
|
|
{
|
|
|
|
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);
|
|
|
|
|
2014-10-15 05:13:14 +08:00
|
|
|
if (bReleaseData){
|
2014-03-29 03:53:35 +08:00
|
|
|
UnloadSoundEffect(m_backgroundID);
|
2014-10-15 05:13:14 +08:00
|
|
|
RemoveFromList(m_backgroundID);
|
|
|
|
}
|
2014-03-29 03:53:35 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
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()
|
|
|
|
{
|
2015-08-22 00:59:46 +08:00
|
|
|
return IsSoundEffectStarted(m_backgroundID) && !IsSoundEffectPaused(m_backgroundID);
|
2014-03-29 03:53:35 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
|
|
|
EffectList::iterator iter;
|
|
|
|
for (iter = m_soundEffects.begin(); iter != m_soundEffects.end(); iter++)
|
|
|
|
{
|
|
|
|
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);
|
|
|
|
|
2015-06-20 15:20:15 +08:00
|
|
|
if (FAILED(m_soundEffects[sound].m_soundEffectSourceVoice->SubmitSourceBuffer(&m_soundEffects[sound].m_audioBuffer)))
|
|
|
|
{
|
|
|
|
m_engineExperiencedCriticalError = true;
|
|
|
|
}
|
2014-03-29 03:53:35 +08:00
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2015-08-22 00:59:46 +08:00
|
|
|
m_soundEffects[sound].m_soundEffectStarted = true;
|
|
|
|
m_soundEffects[sound].m_soundEffectPaused = false;
|
2014-03-29 03:53:35 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
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;
|
2015-08-22 00:59:46 +08:00
|
|
|
m_soundEffects[sound].m_soundEffectPaused = false;
|
2014-03-29 03:53:35 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
2015-08-22 00:59:46 +08:00
|
|
|
m_soundEffects[sound].m_soundEffectPaused = true;
|
2014-03-29 03:53:35 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
2015-08-22 00:59:46 +08:00
|
|
|
m_soundEffects[sound].m_soundEffectPaused = false;
|
2014-03-29 03:53:35 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
|
|
|
EffectList::iterator iter;
|
|
|
|
for (iter = m_soundEffects.begin(); iter != m_soundEffects.end(); iter++)
|
|
|
|
{
|
2014-10-15 05:13:14 +08:00
|
|
|
if (iter->first != m_backgroundID)
|
|
|
|
PauseSoundEffect(iter->first);
|
2014-03-29 03:53:35 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void Audio::ResumeAllSoundEffects()
|
|
|
|
{
|
|
|
|
if (m_engineExperiencedCriticalError) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
EffectList::iterator iter;
|
|
|
|
for (iter = m_soundEffects.begin(); iter != m_soundEffects.end(); iter++)
|
|
|
|
{
|
2014-10-15 05:13:14 +08:00
|
|
|
if (iter->first != m_backgroundID)
|
|
|
|
ResumeSoundEffect(iter->first);
|
2014-03-29 03:53:35 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-10-15 05:13:14 +08:00
|
|
|
void Audio::StopAllSoundEffects(bool bReleaseData)
|
2014-03-29 03:53:35 +08:00
|
|
|
{
|
|
|
|
if (m_engineExperiencedCriticalError) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
EffectList::iterator iter;
|
|
|
|
for (iter = m_soundEffects.begin(); iter != m_soundEffects.end(); iter++)
|
|
|
|
{
|
2014-10-15 05:13:14 +08:00
|
|
|
if (iter->first != m_backgroundID){
|
|
|
|
StopSoundEffect(iter->first);
|
|
|
|
if (bReleaseData)
|
|
|
|
{
|
|
|
|
UnloadSoundEffect(iter->first);
|
|
|
|
}
|
|
|
|
}
|
2014-03-29 03:53:35 +08:00
|
|
|
}
|
2014-10-15 05:13:14 +08:00
|
|
|
if (bReleaseData)
|
|
|
|
{
|
|
|
|
for (iter = m_soundEffects.begin(); iter != m_soundEffects.end();)
|
|
|
|
{
|
|
|
|
if (iter->first != m_backgroundID){
|
|
|
|
m_soundEffects.erase(iter++);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
iter++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2014-03-29 03:53:35 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
bool Audio::IsSoundEffectStarted(unsigned int sound)
|
|
|
|
{
|
|
|
|
if (m_soundEffects.end() == m_soundEffects.find(sound))
|
|
|
|
return false;
|
|
|
|
|
|
|
|
return m_soundEffects[sound].m_soundEffectStarted;
|
|
|
|
}
|
|
|
|
|
2015-08-22 00:59:46 +08:00
|
|
|
bool Audio::IsSoundEffectPaused(unsigned int sound)
|
|
|
|
{
|
|
|
|
if (m_soundEffects.end() == m_soundEffects.find(sound))
|
|
|
|
return false;
|
|
|
|
|
|
|
|
return m_soundEffects[sound].m_soundEffectPaused;
|
|
|
|
}
|
|
|
|
|
2014-10-15 05:13:14 +08:00
|
|
|
void Audio::PreloadSoundEffect(const char* pszFilePath, bool isMusic)
|
|
|
|
{
|
2014-03-29 03:53:35 +08:00
|
|
|
if (m_engineExperiencedCriticalError) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
int sound = Hash(pszFilePath);
|
|
|
|
|
2015-05-28 01:57:26 +08:00
|
|
|
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;
|
|
|
|
uint32 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);
|
2014-03-29 03:53:35 +08:00
|
|
|
|
|
|
|
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;
|
|
|
|
|
2015-06-20 15:20:15 +08:00
|
|
|
if (FAILED(m_musicEngine->CreateSourceVoice(&m_soundEffects[sound].m_soundEffectSourceVoice,
|
|
|
|
&wfx, 0, 1.0f, &m_voiceContext, &sends)))
|
|
|
|
{
|
|
|
|
m_engineExperiencedCriticalError = true;
|
|
|
|
}
|
2014-03-29 03:53:35 +08:00
|
|
|
//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;
|
|
|
|
|
2015-06-20 15:20:15 +08:00
|
|
|
if(FAILED(m_soundEffectEngine->CreateSourceVoice(&m_soundEffects[sound].m_soundEffectSourceVoice,
|
|
|
|
&wfx, 0, 1.0f, &m_voiceContext, &sends, nullptr)))
|
|
|
|
{
|
|
|
|
m_engineExperiencedCriticalError = true;
|
|
|
|
}
|
2014-03-29 03:53:35 +08:00
|
|
|
//fix bug: set a initial volume
|
|
|
|
m_soundEffects[sound].m_soundEffectSourceVoice->SetVolume(m_soundEffctVolume);
|
|
|
|
}
|
|
|
|
|
2015-05-28 01:57:26 +08:00
|
|
|
m_soundEffects[sound].m_soundEffectSampleRate = wfx.nSamplesPerSec;
|
2014-03-29 03:53:35 +08:00
|
|
|
|
|
|
|
// Queue in-memory buffer for playback
|
|
|
|
ZeroMemory(&m_soundEffects[sound].m_audioBuffer, sizeof(m_soundEffects[sound].m_audioBuffer));
|
|
|
|
|
|
|
|
m_soundEffects[sound].m_audioBuffer.AudioBytes = 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);
|
2014-10-15 05:13:14 +08:00
|
|
|
|
|
|
|
RemoveFromList(sound);
|
2014-03-29 03:53:35 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
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();
|
|
|
|
|
2014-10-15 05:13:14 +08:00
|
|
|
if(m_soundEffects[sound].m_soundEffectBufferData)
|
|
|
|
delete [] m_soundEffects[sound].m_soundEffectBufferData;
|
2014-03-29 03:53:35 +08:00
|
|
|
|
2014-10-15 05:13:14 +08:00
|
|
|
m_soundEffects[sound].m_soundEffectBufferData = nullptr;
|
2014-03-29 03:53:35 +08:00
|
|
|
m_soundEffects[sound].m_soundEffectSourceVoice = nullptr;
|
2014-10-15 05:13:14 +08:00
|
|
|
m_soundEffects[sound].m_soundEffectStarted = false;
|
2015-08-22 00:59:46 +08:00
|
|
|
m_soundEffects[sound].m_soundEffectPaused = false;
|
2014-10-15 05:13:14 +08:00
|
|
|
ZeroMemory(&m_soundEffects[sound].m_audioBuffer, sizeof(m_soundEffects[sound].m_audioBuffer));
|
|
|
|
}
|
2014-03-29 03:53:35 +08:00
|
|
|
|
2014-10-15 05:13:14 +08:00
|
|
|
void Audio::RemoveFromList( unsigned int sound )
|
|
|
|
{
|
2014-03-29 03:53:35 +08:00
|
|
|
m_soundEffects.erase(sound);
|
|
|
|
}
|
2014-10-15 05:13:14 +08:00
|
|
|
|
|
|
|
|