diff --git a/cocos/audio/proj.wp8/CocosDenshion.vcxproj b/cocos/audio/proj.wp8/CocosDenshion.vcxproj new file mode 100644 index 0000000000..7838be9ddf --- /dev/null +++ b/cocos/audio/proj.wp8/CocosDenshion.vcxproj @@ -0,0 +1,170 @@ + + + + + Debug + Win32 + + + Debug + ARM + + + Release + Win32 + + + Release + ARM + + + + {DF125891-EEE9-4466-B903-F828FD272158} + CocosDenshion.win32 + en-US + 11.0 + + + + StaticLibrary + true + v110_wp80 + + + StaticLibrary + true + v110_wp80 + + + StaticLibrary + false + true + v110_wp80 + + + StaticLibrary + false + true + v110_wp80 + + + + + + + + + + + + + + + + + + + + + + + + false + + + + _LIB;%(PreprocessorDefinitions) + NotUsing + pch.h + $(WindowsSDK_MetadataPath);$(AdditionalUsingDirectories) + ..\Include;%(AdditionalIncludeDirectories) + WP8;_DEBUG;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) + 4251 + + + Console + false + + + /IGNORE:4264 %(AdditionalOptions) + + + + + _LIB;NDEBUG;%(PreprocessorDefinitions) + NotUsing + pch.h + $(WindowsSDK_MetadataPath);$(AdditionalUsingDirectories) + ..\Include;%(AdditionalIncludeDirectories) + WP8;NDEBUG;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) + 4251 + + + Console + false + + + /IGNORE:4264 %(AdditionalOptions) + + + + + _LIB;%(PreprocessorDefinitions) + NotUsing + pch.h + $(WindowsSDK_MetadataPath);$(AdditionalUsingDirectories) + ..\Include;%(AdditionalIncludeDirectories) + WP8;_DEBUG;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) + 4251 + + + Console + false + + + /IGNORE:4264 %(AdditionalOptions) + + + + + _LIB;NDEBUG;%(PreprocessorDefinitions) + NotUsing + pch.h + $(WindowsSDK_MetadataPath);$(AdditionalUsingDirectories) + ..\Include;%(AdditionalIncludeDirectories) + WP8;NDEBUG;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) + 4251 + + + Console + false + + + /IGNORE:4264 %(AdditionalOptions) + + + + + true + + + true + false + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/cocos/audio/proj.wp8/CocosDenshion.vcxproj.filters b/cocos/audio/proj.wp8/CocosDenshion.vcxproj.filters new file mode 100644 index 0000000000..cbc9b5dc4e --- /dev/null +++ b/cocos/audio/proj.wp8/CocosDenshion.vcxproj.filters @@ -0,0 +1,42 @@ + + + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hpp;hxx;hm;inl;inc;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + + + Header Files + + + Header Files + + + Source Files + + + Source Files + + + + + Source Files + + + Source Files + + + Source Files + + + \ No newline at end of file diff --git a/cocos/audio/proj.wp8/CocosDenshion.vcxproj.user b/cocos/audio/proj.wp8/CocosDenshion.vcxproj.user new file mode 100644 index 0000000000..17f726ed50 --- /dev/null +++ b/cocos/audio/proj.wp8/CocosDenshion.vcxproj.user @@ -0,0 +1,6 @@ + + + + true + + \ No newline at end of file diff --git a/cocos/audio/winrt/Audio.cpp b/cocos/audio/winrt/Audio.cpp new file mode 100644 index 0000000000..76b3e10bcf --- /dev/null +++ b/cocos/audio/winrt/Audio.cpp @@ -0,0 +1,576 @@ +//// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF +//// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO +//// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A +//// PARTICULAR PURPOSE. +//// +//// Copyright (c) Microsoft Corporation. All rights reserved + +#include "pch.h" +#include "Audio.h" +#include "MediaStreamer.h" + +static std::wstring CCUtf8ToUnicode(const char * pszUtf8Str, unsigned len/* = -1*/) +{ + std::wstring ret; + do + { + if (! pszUtf8Str) break; + // get UTF8 string length + if (-1 == len) + { + len = strlen(pszUtf8Str); + } + if (len <= 0) break; + + // get UTF16 string length + int wLen = MultiByteToWideChar(CP_UTF8, 0, pszUtf8Str, len, 0, 0); + if (0 == wLen || 0xFFFD == wLen) break; + + // convert string + wchar_t * pwszStr = new wchar_t[wLen + 1]; + if (! pwszStr) break; + pwszStr[wLen] = 0; + MultiByteToWideChar(CP_UTF8, 0, pszUtf8Str, len, pwszStr, wLen + 1); + ret = pwszStr; + if(pwszStr) { delete[] (pwszStr);}; + } while (0); + return ret; +} + +static inline void ThrowIfFailed(HRESULT hr) +{ + if (FAILED(hr)) + { + // Set a breakpoint on this line to catch DirectX 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) +{ + m_audio->SetEngineExperiencedCriticalError(); +}; + +Audio::Audio() : + m_backgroundID(0), + m_soundEffctVolume(1.0), + 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() +{ + try + { + ThrowIfFailed( + XAudio2Create(&m_musicEngine) + ); + +#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. + ThrowIfFailed( + m_musicEngine->CreateMasteringVoice(&m_musicMasteringVoice, XAUDIO2_DEFAULT_CHANNELS, XAUDIO2_DEFAULT_SAMPLERATE, 0, nullptr, nullptr, AudioCategory_GameMedia) + ); + + // 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. + ThrowIfFailed( + XAudio2Create(&m_soundEffectEngine) + ); + + m_soundEffectEngineCallback.Initialize(this); + m_soundEffectEngine->RegisterForCallbacks(&m_soundEffectEngineCallback); + + // We default the mastering voice to 2 channels to simplify the reverb logic. + ThrowIfFailed( + m_soundEffectEngine->CreateMasteringVoice(&m_soundEffectMasteringVoice, XAUDIO2_DEFAULT_CHANNELS, XAUDIO2_DEFAULT_SAMPLERATE, 0, nullptr, nullptr, AudioCategory_GameEffects) + ); + } + catch (...) + { + m_engineExperiencedCriticalError = true; + } +} + +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); + + if (bReleaseData) + UnloadSoundEffect(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); +} + +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); + + ThrowIfFailed( + m_soundEffects[sound].m_soundEffectSourceVoice->SubmitSourceBuffer(&m_soundEffects[sound].m_audioBuffer) + ); + + XAUDIO2_BUFFER buf = {0}; + XAUDIO2_VOICE_STATE state = {0}; + + 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; +} + +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; +} + +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; + } +} + +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; + } +} + +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++) + { + PauseSoundEffect(iter->first); + } +} + +void Audio::ResumeAllSoundEffects() +{ + if (m_engineExperiencedCriticalError) { + return; + } + + EffectList::iterator iter; + for (iter = m_soundEffects.begin(); iter != m_soundEffects.end(); iter++) + { + ResumeSoundEffect(iter->first); + } +} + +void Audio::StopAllSoundEffects() +{ + if (m_engineExperiencedCriticalError) { + return; + } + + EffectList::iterator iter; + for (iter = m_soundEffects.begin(); iter != m_soundEffects.end(); iter++) + { + StopSoundEffect(iter->first); + } +} + +bool Audio::IsSoundEffectStarted(unsigned int sound) +{ + if (m_soundEffects.end() == m_soundEffects.find(sound)) + return false; + + return m_soundEffects[sound].m_soundEffectStarted; +} + +void Audio::PreloadSoundEffect(const char* pszFilePath, bool isMusic) +{ + + if (m_engineExperiencedCriticalError) { + return; + } + + int sound = Hash(pszFilePath); + + if (m_soundEffects.end() != m_soundEffects.find(sound)) + { + return; + } + + MediaStreamer mediaStreamer; + mediaStreamer.Initialize(CCUtf8ToUnicode(pszFilePath, -1).c_str()); + m_soundEffects[sound].m_soundID = sound; + + uint32 bufferLength = mediaStreamer.GetMaxStreamLengthInBytes(); + + if (m_soundEffects.find(sound) != m_soundEffects.end()) + { + if (m_soundEffects[sound].m_soundEffectBufferData) + { + delete[] m_soundEffects[sound].m_soundEffectBufferData; + m_soundEffects[sound].m_soundEffectBufferData = NULL; + } + } + else + { + m_soundEffects[sound].m_soundEffectBufferData = NULL; + } + + m_soundEffects[sound].m_soundEffectBufferData = new byte[bufferLength]; + mediaStreamer.ReadAll(m_soundEffects[sound].m_soundEffectBufferData, bufferLength, &m_soundEffects[sound].m_soundEffectBufferLength); + + 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; + + ThrowIfFailed( + m_musicEngine->CreateSourceVoice(&m_soundEffects[sound].m_soundEffectSourceVoice, + &(mediaStreamer.GetOutputWaveFormatEx()), 0, 1.0f, &m_voiceContext, &sends) + ); + //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; + + ThrowIfFailed( + m_soundEffectEngine->CreateSourceVoice(&m_soundEffects[sound].m_soundEffectSourceVoice, + &(mediaStreamer.GetOutputWaveFormatEx()), 0, 1.0f, &m_voiceContext, &sends, nullptr) + ); + //fix bug: set a initial volume + m_soundEffects[sound].m_soundEffectSourceVoice->SetVolume(m_soundEffctVolume); + } + + m_soundEffects[sound].m_soundEffectSampleRate = mediaStreamer.GetOutputWaveFormatEx().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 = 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); +} + +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 = NULL; + } + + m_soundEffects[sound].m_soundEffectSourceVoice = nullptr; + m_soundEffects[sound].m_soundEffectStarted = false;// + ZeroMemory(&m_soundEffects[sound].m_audioBuffer, sizeof(m_soundEffects[sound].m_audioBuffer)); + + m_soundEffects.erase(sound); +} diff --git a/cocos/audio/winrt/Audio.h b/cocos/audio/winrt/Audio.h new file mode 100644 index 0000000000..ae58dc7402 --- /dev/null +++ b/cocos/audio/winrt/Audio.h @@ -0,0 +1,152 @@ +//// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF +//// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO +//// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A +//// PARTICULAR PURPOSE. +//// +//// Copyright (c) Microsoft Corporation. All rights reserved + +#pragma once + +#include "pch.h" +#include + +static const int STREAMING_BUFFER_SIZE = 65536; +static const int MAX_BUFFER_COUNT = 3; + +struct SoundEffectData +{ + unsigned int m_soundID; + IXAudio2SourceVoice* m_soundEffectSourceVoice; + XAUDIO2_BUFFER m_audioBuffer; + byte* m_soundEffectBufferData; + uint32 m_soundEffectBufferLength; + uint32 m_soundEffectSampleRate; + bool m_soundEffectStarted; +}; + +class Audio; +class AudioEngineCallbacks: public IXAudio2EngineCallback +{ +private: + Audio *m_audio; + +public : + AudioEngineCallbacks(){}; + void Initialize(Audio* audio); + + // Called by XAudio2 just before an audio processing pass begins. + void _stdcall OnProcessingPassStart(){}; + + // Called just after an audio processing pass ends. + void _stdcall OnProcessingPassEnd(){}; + + // 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 OnCriticalError(HRESULT Error); +}; + +struct StreamingVoiceContext : public IXAudio2VoiceCallback +{ + STDMETHOD_(void, OnVoiceProcessingPassStart)(UINT32){} + STDMETHOD_(void, OnVoiceProcessingPassEnd)(){} + STDMETHOD_(void, OnStreamEnd)(){} + STDMETHOD_(void, OnBufferStart)(void*) + { + ResetEvent(hBufferEndEvent); + } + STDMETHOD_(void, OnBufferEnd)(void* pContext) + { + //Trigger the event for the music stream. + if (pContext == 0) { + SetEvent(hBufferEndEvent); + } + } + STDMETHOD_(void, OnLoopEnd)(void*){} + STDMETHOD_(void, OnVoiceError)(void*, HRESULT){} + + HANDLE hBufferEndEvent; + StreamingVoiceContext() : hBufferEndEvent(CreateEventEx(NULL, FALSE, FALSE, NULL)) + { + } + virtual ~StreamingVoiceContext() + { + CloseHandle(hBufferEndEvent); + } +}; + +class Audio +{ +private: + IXAudio2* m_musicEngine; + IXAudio2* m_soundEffectEngine; + IXAudio2MasteringVoice* m_musicMasteringVoice; + IXAudio2MasteringVoice* m_soundEffectMasteringVoice; + + StreamingVoiceContext m_voiceContext; + + typedef std::map EffectList; + EffectList m_soundEffects; + + unsigned int m_backgroundID; + std::string m_backgroundFile; + bool m_backgroundLoop; + + float m_soundEffctVolume; + float m_backgroundMusicVolume; + + bool m_engineExperiencedCriticalError; + AudioEngineCallbacks m_musicEngineCallback; + AudioEngineCallbacks m_soundEffectEngineCallback; + + unsigned int Hash(const char* key); + +public: + Audio(); + + void Initialize(); + void CreateResources(); + void ReleaseResources(); + void Start(); + void Render(); + + // This flag can be used to tell when the audio system is experiencing critial errors. + // XAudio2 gives a critical error when the user unplugs their headphones, and a new + // speaker configuration is generated. + void SetEngineExperiencedCriticalError() + { + m_engineExperiencedCriticalError = true; + } + + bool HasEngineExperiencedCriticalError() + { + return m_engineExperiencedCriticalError; + } + + void PlayBackgroundMusic(const char* pszFilePath, bool bLoop); + void StopBackgroundMusic(bool bReleaseData); + void PauseBackgroundMusic(); + void ResumeBackgroundMusic(); + void RewindBackgroundMusic(); + bool IsBackgroundMusicPlaying(); + + void SetBackgroundVolume(float volume); + float GetBackgroundVolume(); + void SetSoundEffectVolume(float volume); + float GetSoundEffectVolume(); + + void PlaySoundEffect(const char* pszFilePath, bool bLoop, unsigned int& sound, bool isMusic = false); + void PlaySoundEffect(unsigned int sound); + bool IsSoundEffectStarted(unsigned int sound); + void StopSoundEffect(unsigned int sound); + void PauseSoundEffect(unsigned int sound); + void ResumeSoundEffect(unsigned int sound); + void RewindSoundEffect(unsigned int sound); + + void PauseAllSoundEffects(); + void ResumeAllSoundEffects(); + void StopAllSoundEffects(); + + void PreloadSoundEffect(const char* pszFilePath, bool isMusic = false); + void UnloadSoundEffect(const char* pszFilePath); + void UnloadSoundEffect(unsigned int sound); +}; diff --git a/cocos/audio/winrt/MediaStreamer.cpp b/cocos/audio/winrt/MediaStreamer.cpp new file mode 100644 index 0000000000..62fcf7a9b1 --- /dev/null +++ b/cocos/audio/winrt/MediaStreamer.cpp @@ -0,0 +1,215 @@ +//// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF +//// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO +//// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A +//// PARTICULAR PURPOSE. +//// +//// Copyright (c) Microsoft Corporation. All rights reserved + +#include "pch.h" +#include "MediaStreamer.h" + +using namespace Windows::ApplicationModel; + +static inline void ThrowIfFailed(HRESULT hr) +{ + if (FAILED(hr)) + { + // Set a breakpoint on this line to catch DirectX API errors + throw Platform::Exception::CreateException(hr); + } +} + + +MediaStreamer::MediaStreamer() +{ + m_reader = nullptr; + m_audioType = nullptr; + ZeroMemory(&m_waveFormat, sizeof(m_waveFormat)); + + m_installedLocation = Package::Current->InstalledLocation; + m_installedLocationPath = Platform::String::Concat(m_installedLocation->Path, "\\Assets\\Resources\\"); +} + +MediaStreamer::~MediaStreamer() +{ +} + +void MediaStreamer::Initialize(__in const WCHAR* url) +{ + Microsoft::WRL::ComPtr outputMediaType; + Microsoft::WRL::ComPtr mediaType; + + ThrowIfFailed( + MFStartup(MF_VERSION) + ); + + WCHAR filePath[MAX_PATH] = {0}; + if ((wcslen(url) > 1 && url[1] == ':')) + { + // path start with "x:", is absolute path + wcscat_s(filePath, url); + } + else if (wcslen(url) > 0 + && (L'/' == url[0] || L'\\' == url[0])) + { + // path start with '/' or '\', is absolute path without driver name + wcscat_s(filePath, m_installedLocationPath->Data()); + // remove '/' or '\\' + wcscat_s(filePath, (const WCHAR*)url[1]); + }else + { + wcscat_s(filePath, m_installedLocationPath->Data()); + wcscat_s(filePath, url); + } + + ThrowIfFailed( + MFCreateSourceReaderFromURL(filePath, nullptr, &m_reader) + ); + + // Set the decoded output format as PCM + // XAudio2 on Windows can process PCM and ADPCM-encoded buffers. + // When using MF, this sample always decodes into PCM. + + ThrowIfFailed( + MFCreateMediaType(&mediaType) + ); + + ThrowIfFailed( + mediaType->SetGUID(MF_MT_MAJOR_TYPE, MFMediaType_Audio) + ); + + ThrowIfFailed( + mediaType->SetGUID(MF_MT_SUBTYPE, MFAudioFormat_PCM) + ); + + ThrowIfFailed( + m_reader->SetCurrentMediaType(MF_SOURCE_READER_FIRST_AUDIO_STREAM, 0, mediaType.Get()) + ); + + // Get the complete WAVEFORMAT from the Media Type + ThrowIfFailed( + m_reader->GetCurrentMediaType(MF_SOURCE_READER_FIRST_AUDIO_STREAM, &outputMediaType) + ); + + uint32 formatSize = 0; + WAVEFORMATEX* waveFormat; + ThrowIfFailed( + MFCreateWaveFormatExFromMFMediaType(outputMediaType.Get(), &waveFormat, &formatSize) + ); + CopyMemory(&m_waveFormat, waveFormat, sizeof(m_waveFormat)); + CoTaskMemFree(waveFormat); + + // Get the total length of the stream in bytes + PROPVARIANT var; + ThrowIfFailed( + m_reader->GetPresentationAttribute(MF_SOURCE_READER_MEDIASOURCE, MF_PD_DURATION, &var) + ); + LONGLONG duration = var.uhVal.QuadPart; + double durationInSeconds = (duration / static_cast(10000000)); // duration is in 100ns units, convert to seconds + m_maxStreamLengthInBytes = static_cast(durationInSeconds * m_waveFormat.nAvgBytesPerSec); + + // Round up the buffer size to the nearest four bytes + m_maxStreamLengthInBytes = (m_maxStreamLengthInBytes + 3) / 4 * 4; +} + +bool MediaStreamer::GetNextBuffer(uint8* buffer, uint32 maxBufferSize, uint32* bufferLength) +{ + Microsoft::WRL::ComPtr sample; + Microsoft::WRL::ComPtr mediaBuffer; + BYTE *audioData = nullptr; + DWORD sampleBufferLength = 0; + DWORD flags = 0; + + *bufferLength = 0; + + if (m_reader == nullptr) + { + return false; + } + + ThrowIfFailed( + m_reader->ReadSample(MF_SOURCE_READER_FIRST_AUDIO_STREAM, 0, nullptr, &flags, nullptr, &sample) + ); + + if (sample == nullptr) + { + if (flags & MF_SOURCE_READERF_ENDOFSTREAM) + { + return true; + } + else + { + return false; + } + } + + ThrowIfFailed( + sample->ConvertToContiguousBuffer(&mediaBuffer) + ); + + ThrowIfFailed( + mediaBuffer->Lock(&audioData, nullptr, &sampleBufferLength) + ); + + // If buffer isn't large enough, dump sample + if (sampleBufferLength <= maxBufferSize) + { + CopyMemory(buffer, audioData, sampleBufferLength); + *bufferLength = sampleBufferLength; + } + else + { +#if defined(COCOS2D_DEBUG) + OutputDebugString(L"Sample buffer dumped"); +#endif + } + + if (flags & MF_SOURCE_READERF_ENDOFSTREAM) + { + return true; + } + else + { + return false; + } +} + +void MediaStreamer::ReadAll(uint8* buffer, uint32 maxBufferSize, uint32* bufferLength) +{ + uint32 valuesWritten = 0; + uint32 sampleBufferLength = 0; + + if (m_reader == nullptr) + { + return; + } + + *bufferLength = 0; + // If buffer isn't large enough, return + if (maxBufferSize < m_maxStreamLengthInBytes) + { + return; + } + + while (!GetNextBuffer(buffer + valuesWritten, maxBufferSize - valuesWritten, &sampleBufferLength)) + { + valuesWritten += sampleBufferLength; + } + + *bufferLength = valuesWritten + sampleBufferLength; +} + +void MediaStreamer::Restart() +{ + if (m_reader == nullptr) + { + return; + } + + PROPVARIANT var = {0}; + var.vt = VT_I8; + + ThrowIfFailed( + m_reader->SetCurrentPosition(GUID_NULL, var) + ); +} \ No newline at end of file diff --git a/cocos/audio/winrt/MediaStreamer.h b/cocos/audio/winrt/MediaStreamer.h new file mode 100644 index 0000000000..972a186da5 --- /dev/null +++ b/cocos/audio/winrt/MediaStreamer.h @@ -0,0 +1,41 @@ +//// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF +//// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO +//// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A +//// PARTICULAR PURPOSE. +//// +//// Copyright (c) Microsoft Corporation. All rights reserved + +#pragma once +#include "pch.h" + +class MediaStreamer +{ +private: + WAVEFORMATEX m_waveFormat; + uint32 m_maxStreamLengthInBytes; + Windows::Storage::StorageFolder^ m_installedLocation; + Platform::String^ m_installedLocationPath; + +public: + Microsoft::WRL::ComPtr m_reader; + Microsoft::WRL::ComPtr m_audioType; + +public: + MediaStreamer(); + ~MediaStreamer(); + + WAVEFORMATEX& GetOutputWaveFormatEx() + { + return m_waveFormat; + } + + UINT32 GetMaxStreamLengthInBytes() + { + return m_maxStreamLengthInBytes; + } + + void Initialize(_In_ const WCHAR* url); + bool GetNextBuffer(uint8* buffer, uint32 maxBufferSize, uint32* bufferLength); + void ReadAll(uint8* buffer, uint32 maxBufferSize, uint32* bufferLength); + void Restart(); +}; \ No newline at end of file diff --git a/cocos/audio/winrt/SimpleAudioEngine.cpp b/cocos/audio/winrt/SimpleAudioEngine.cpp new file mode 100644 index 0000000000..510cd6434c --- /dev/null +++ b/cocos/audio/winrt/SimpleAudioEngine.cpp @@ -0,0 +1,212 @@ +/**************************************************************************** +Copyright (c) 2010-2013 cocos2d-x.org +Copyright (c) Microsoft Open Technologies, Inc. + +http://www.cocos2d-x.org + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +****************************************************************************/ +#include "SimpleAudioEngine.h" +#include "Audio.h" + +#include + +//#include "CCCommon.h" +using namespace std; + +namespace CocosDenshion { + + + +Audio* s_audioController = NULL; +// a flag that if the s_audioController should be re-initialiezed +// see also in SimpleAudioEngine::end() in this file +bool s_bAudioControllerNeedReInitialize = true; + +static Audio* sharedAudioController() +{ + if ((! s_audioController) || s_bAudioControllerNeedReInitialize) + { + s_audioController = new Audio; + s_audioController->Initialize(); + s_audioController->CreateResources(); + s_bAudioControllerNeedReInitialize = false; + } + + return s_audioController; +} + +SimpleAudioEngine::SimpleAudioEngine() +{ +} + +SimpleAudioEngine::~SimpleAudioEngine() +{ +} + +SimpleAudioEngine* SimpleAudioEngine::sharedEngine() +{ + static SimpleAudioEngine s_SharedEngine; + return &s_SharedEngine; +} + +void SimpleAudioEngine::end() +{ + sharedAudioController()->StopBackgroundMusic(true); + sharedAudioController()->StopAllSoundEffects(); + sharedAudioController()->ReleaseResources(); + //set here to tell the s_bAudioControllerNeedReInitialize should be re-initialized + s_bAudioControllerNeedReInitialize = true; +} + + +#if 0 +void SimpleAudioEngine::render() +{ + sharedAudioController()->Render(); +} +#endif + + +////////////////////////////////////////////////////////////////////////// +// BackgroundMusic +////////////////////////////////////////////////////////////////////////// + +void SimpleAudioEngine::playBackgroundMusic(const char* pszFilePath, bool bLoop) +{ + if (! pszFilePath) + { + return; + } + + sharedAudioController()->PlayBackgroundMusic(pszFilePath, bLoop); +} + +void SimpleAudioEngine::stopBackgroundMusic(bool bReleaseData) +{ + sharedAudioController()->StopBackgroundMusic(bReleaseData); +} + +void SimpleAudioEngine::pauseBackgroundMusic() +{ + sharedAudioController()->PauseBackgroundMusic(); +} + +void SimpleAudioEngine::resumeBackgroundMusic() +{ + sharedAudioController()->ResumeBackgroundMusic(); +} + +void SimpleAudioEngine::rewindBackgroundMusic() +{ + sharedAudioController()->RewindBackgroundMusic(); +} + +bool SimpleAudioEngine::willPlayBackgroundMusic() +{ + return false; +} + +bool SimpleAudioEngine::isBackgroundMusicPlaying() +{ + return sharedAudioController()->IsBackgroundMusicPlaying(); +} + +////////////////////////////////////////////////////////////////////////// +// effect function +////////////////////////////////////////////////////////////////////////// + +unsigned int SimpleAudioEngine::playEffect(const char* pszFilePath, bool bLoop) +{ + unsigned int sound; + sharedAudioController()->PlaySoundEffect(pszFilePath, bLoop, sound); + + return sound; +} + +void SimpleAudioEngine::stopEffect(unsigned int nSoundId) +{ + sharedAudioController()->StopSoundEffect(nSoundId); +} + +void SimpleAudioEngine::preloadEffect(const char* pszFilePath) +{ + sharedAudioController()->PreloadSoundEffect(pszFilePath); +} + +void SimpleAudioEngine::pauseEffect(unsigned int nSoundId) +{ + sharedAudioController()->PauseSoundEffect(nSoundId); +} + +void SimpleAudioEngine::resumeEffect(unsigned int nSoundId) +{ + sharedAudioController()->ResumeSoundEffect(nSoundId); +} + +void SimpleAudioEngine::pauseAllEffects() +{ + sharedAudioController()->PauseAllSoundEffects(); +} + +void SimpleAudioEngine::resumeAllEffects() +{ + sharedAudioController()->ResumeAllSoundEffects(); +} + +void SimpleAudioEngine::stopAllEffects() +{ + sharedAudioController()->StopAllSoundEffects(); +} + +void SimpleAudioEngine::preloadBackgroundMusic(const char* pszFilePath) +{ + +} + +void SimpleAudioEngine::unloadEffect(const char* pszFilePath) +{ + sharedAudioController()->UnloadSoundEffect(pszFilePath); +} + +////////////////////////////////////////////////////////////////////////// +// volume interface +////////////////////////////////////////////////////////////////////////// + +float SimpleAudioEngine::getBackgroundMusicVolume() +{ + return sharedAudioController()->GetBackgroundVolume(); +} + +void SimpleAudioEngine::setBackgroundMusicVolume(float volume) +{ + sharedAudioController()->SetBackgroundVolume((volume<=0.0f)? 0.0f : volume); +} + +float SimpleAudioEngine::getEffectsVolume() +{ + return sharedAudioController()->GetSoundEffectVolume(); +} + +void SimpleAudioEngine::setEffectsVolume(float volume) +{ + sharedAudioController()->SetSoundEffectVolume((volume<=0.0f)? 0.0f : volume); +} + +} // end of namespace CocosDenshion diff --git a/cocos/audio/wp8/Audio.cpp b/cocos/audio/wp8/Audio.cpp new file mode 100644 index 0000000000..0be32e6069 --- /dev/null +++ b/cocos/audio/wp8/Audio.cpp @@ -0,0 +1,581 @@ +/* +* 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. +*/ + +#include "Audio.h" +#include "MediaStreamer.h" +//#include "CCCommon.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() +{ + try + { + ThrowIfFailed( + XAudio2Create(&m_musicEngine) + ); + +#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. + ThrowIfFailed( + m_musicEngine->CreateMasteringVoice(&m_musicMasteringVoice, XAUDIO2_DEFAULT_CHANNELS, XAUDIO2_DEFAULT_SAMPLERATE, 0, nullptr, nullptr, AudioCategory_GameMedia) + ); + + // 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. + ThrowIfFailed( + XAudio2Create(&m_soundEffectEngine) + ); + + m_soundEffectEngineCallback.Initialize(this); + m_soundEffectEngine->RegisterForCallbacks(&m_soundEffectEngineCallback); + + // We default the mastering voice to 2 channels to simplify the reverb logic. + ThrowIfFailed( + m_soundEffectEngine->CreateMasteringVoice(&m_soundEffectMasteringVoice, XAUDIO2_DEFAULT_CHANNELS, XAUDIO2_DEFAULT_SAMPLERATE, 0, nullptr, nullptr, AudioCategory_GameEffects) + ); + } + catch (...) + { + m_engineExperiencedCriticalError = true; + } +} + +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); + + if (bReleaseData) + UnloadSoundEffect(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); +} + +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); + + ThrowIfFailed( + m_soundEffects[sound].m_soundEffectSourceVoice->SubmitSourceBuffer(&m_soundEffects[sound].m_audioBuffer) + ); + + 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; +} + +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; +} + +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; + } +} + +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; + } +} + +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++) + { + PauseSoundEffect(iter->first); + } +} + +void Audio::ResumeAllSoundEffects() +{ + if (m_engineExperiencedCriticalError) { + return; + } + + EffectList::iterator iter; + for (iter = m_soundEffects.begin(); iter != m_soundEffects.end(); iter++) + { + ResumeSoundEffect(iter->first); + } +} + +void Audio::StopAllSoundEffects() +{ + if (m_engineExperiencedCriticalError) { + return; + } + + EffectList::iterator iter; + for (iter = m_soundEffects.begin(); iter != m_soundEffects.end(); iter++) + { + StopSoundEffect(iter->first); + } +} + +bool Audio::IsSoundEffectStarted(unsigned int sound) +{ + if (m_soundEffects.end() == m_soundEffects.find(sound)) + return false; + + return m_soundEffects[sound].m_soundEffectStarted; +} + +std::wstring CCUtf8ToUnicode(const char * pszUtf8Str) +{ + std::wstring ret; + do + { + if (! pszUtf8Str) break; + size_t len = strlen(pszUtf8Str); + if (len <= 0) break; + ++len; + wchar_t * pwszStr = new wchar_t[len]; + if (! pwszStr) break; + pwszStr[len - 1] = 0; + MultiByteToWideChar(CP_UTF8, 0, pszUtf8Str, len, pwszStr, len); + ret = pwszStr; + + if(pwszStr) { + delete[] (pwszStr); + (pwszStr) = 0; + } + + + } while (0); + return ret; +} + +std::string CCUnicodeToUtf8(const wchar_t* pwszStr) +{ + std::string ret; + do + { + if(! pwszStr) break; + size_t len = wcslen(pwszStr); + if (len <= 0) break; + + char * pszUtf8Str = new char[len*3 + 1]; + WideCharToMultiByte(CP_UTF8, 0, pwszStr, len+1, pszUtf8Str, len*3 + 1, 0, 0); + ret = pszUtf8Str; + + if(pszUtf8Str) { + delete[] (pszUtf8Str); + (pszUtf8Str) = 0; + } + }while(0); + + return ret; +} + +void Audio::PreloadSoundEffect(const char* pszFilePath, bool isMusic) +{ + if (m_engineExperiencedCriticalError) { + return; + } + + int sound = Hash(pszFilePath); + + MediaStreamer mediaStreamer; + mediaStreamer.Initialize(CCUtf8ToUnicode(pszFilePath).c_str()); + m_soundEffects[sound].m_soundID = sound; + + uint32 bufferLength = mediaStreamer.GetMaxStreamLengthInBytes(); + m_soundEffects[sound].m_soundEffectBufferData = new byte[bufferLength]; + mediaStreamer.ReadAll(m_soundEffects[sound].m_soundEffectBufferData, bufferLength, &m_soundEffects[sound].m_soundEffectBufferLength); + + 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; + + ThrowIfFailed( + m_musicEngine->CreateSourceVoice(&m_soundEffects[sound].m_soundEffectSourceVoice, + &(mediaStreamer.GetOutputWaveFormatEx()), 0, 1.0f, &m_voiceContext, &sends) + ); + //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; + + ThrowIfFailed( + m_soundEffectEngine->CreateSourceVoice(&m_soundEffects[sound].m_soundEffectSourceVoice, + &(mediaStreamer.GetOutputWaveFormatEx()), 0, 1.0f, &m_voiceContext, &sends, nullptr) + ); + //fix bug: set a initial volume + m_soundEffects[sound].m_soundEffectSourceVoice->SetVolume(m_soundEffctVolume); + } + + m_soundEffects[sound].m_soundEffectSampleRate = mediaStreamer.GetOutputWaveFormatEx().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 = 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); +} + +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; + ZeroMemory(&m_soundEffects[sound].m_audioBuffer, sizeof(m_soundEffects[sound].m_audioBuffer)); + + m_soundEffects.erase(sound); +} diff --git a/cocos/audio/wp8/Audio.h b/cocos/audio/wp8/Audio.h new file mode 100644 index 0000000000..d279de50b6 --- /dev/null +++ b/cocos/audio/wp8/Audio.h @@ -0,0 +1,164 @@ +//// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF +//// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO +//// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A +//// PARTICULAR PURPOSE. +//// +//// Copyright (c) Microsoft Corporation. All rights reserved + +// For licensing information relating to this distribution please see Third Party Notices file. + +#pragma once + +#include +#include +#include +#include +#include + +#define XAUDIO2_HELPER_FUNCTIONS 1 +#include +#include + +static const int STREAMING_BUFFER_SIZE = 65536; +static const int MAX_BUFFER_COUNT = 3; + +#define UNUSED_PARAM(unusedparam) (void)unusedparam + +struct SoundEffectData +{ + unsigned int m_soundID; + IXAudio2SourceVoice* m_soundEffectSourceVoice; + XAUDIO2_BUFFER m_audioBuffer; + byte* m_soundEffectBufferData; + uint32 m_soundEffectBufferLength; + uint32 m_soundEffectSampleRate; + bool m_soundEffectStarted; +}; + +class Audio; +class AudioEngineCallbacks: public IXAudio2EngineCallback +{ +private: + Audio *m_audio; + +public : + AudioEngineCallbacks(){}; + void Initialize(Audio* audio); + + // Called by XAudio2 just before an audio processing pass begins. + void _stdcall OnProcessingPassStart(){}; + + // Called just after an audio processing pass ends. + void _stdcall OnProcessingPassEnd(){}; + + // 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 OnCriticalError(HRESULT Error); +}; + +struct StreamingVoiceContext : public IXAudio2VoiceCallback +{ + STDMETHOD_(void, OnVoiceProcessingPassStart)(UINT32){} + STDMETHOD_(void, OnVoiceProcessingPassEnd)(){} + STDMETHOD_(void, OnStreamEnd)(){} + STDMETHOD_(void, OnBufferStart)(void*) + { + ResetEvent(hBufferEndEvent); + } + STDMETHOD_(void, OnBufferEnd)(void* pContext) + { + //Trigger the event for the music stream. + if (pContext == 0) { + SetEvent(hBufferEndEvent); + } + } + STDMETHOD_(void, OnLoopEnd)(void*){} + STDMETHOD_(void, OnVoiceError)(void*, HRESULT){} + + HANDLE hBufferEndEvent; + StreamingVoiceContext() : hBufferEndEvent(CreateEventEx(NULL, FALSE, FALSE, NULL)) + { + } + virtual ~StreamingVoiceContext() + { + CloseHandle(hBufferEndEvent); + } +}; + +class Audio +{ +private: + IXAudio2* m_musicEngine; + IXAudio2* m_soundEffectEngine; + IXAudio2MasteringVoice* m_musicMasteringVoice; + IXAudio2MasteringVoice* m_soundEffectMasteringVoice; + + StreamingVoiceContext m_voiceContext; + + typedef std::map EffectList; + typedef std::pair Effect; + EffectList m_soundEffects; + + unsigned int m_backgroundID; + std::string m_backgroundFile; + bool m_backgroundLoop; + + float m_soundEffctVolume; + float m_backgroundMusicVolume; + + bool m_engineExperiencedCriticalError; + AudioEngineCallbacks m_musicEngineCallback; + AudioEngineCallbacks m_soundEffectEngineCallback; + + unsigned int Hash(const char* key); + +public: + Audio(); + + void Initialize(); + void CreateResources(); + void ReleaseResources(); + void Start(); + void Render(); + + // This flag can be used to tell when the audio system is experiencing critial errors. + // XAudio2 gives a critical error when the user unplugs their headphones, and a new + // speaker configuration is generated. + void SetEngineExperiencedCriticalError() + { + m_engineExperiencedCriticalError = true; + } + + bool HasEngineExperiencedCriticalError() + { + return m_engineExperiencedCriticalError; + } + + void PlayBackgroundMusic(const char* pszFilePath, bool bLoop); + void StopBackgroundMusic(bool bReleaseData); + void PauseBackgroundMusic(); + void ResumeBackgroundMusic(); + void RewindBackgroundMusic(); + bool IsBackgroundMusicPlaying(); + + void SetBackgroundVolume(float volume); + float GetBackgroundVolume(); + void SetSoundEffectVolume(float volume); + float GetSoundEffectVolume(); + + void PlaySoundEffect(const char* pszFilePath, bool bLoop, unsigned int& sound, bool isMusic = false); + void PlaySoundEffect(unsigned int sound); + bool IsSoundEffectStarted(unsigned int sound); + void StopSoundEffect(unsigned int sound); + void PauseSoundEffect(unsigned int sound); + void ResumeSoundEffect(unsigned int sound); + void RewindSoundEffect(unsigned int sound); + + void PauseAllSoundEffects(); + void ResumeAllSoundEffects(); + void StopAllSoundEffects(); + + void PreloadSoundEffect(const char* pszFilePath, bool isMusic = false); + void UnloadSoundEffect(const char* pszFilePath); + void UnloadSoundEffect(unsigned int sound); +}; diff --git a/cocos/audio/wp8/MediaStreamer.cpp b/cocos/audio/wp8/MediaStreamer.cpp new file mode 100644 index 0000000000..8330b33e25 --- /dev/null +++ b/cocos/audio/wp8/MediaStreamer.cpp @@ -0,0 +1,216 @@ +/* +* 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. +*/ + +#include "MediaStreamer.h" +#include +#include + +using namespace Microsoft::WRL; +using namespace Windows::Storage; +using namespace Windows::Storage::FileProperties; +using namespace Windows::Storage::Streams; +using namespace Windows::Foundation; +using namespace Windows::ApplicationModel; +using namespace Concurrency; + +#ifndef MAKEFOURCC + #define MAKEFOURCC(ch0, ch1, ch2, ch3) \ + ((uint32)(byte)(ch0) | ((uint32)(byte)(ch1) << 8) | \ + ((uint32)(byte)(ch2) << 16) | ((uint32)(byte)(ch3) << 24 )) +#endif /* defined(MAKEFOURCC) */ + +inline void ThrowIfFailed(HRESULT hr) +{ + if (FAILED(hr)) + { + // Set a breakpoint on this line to catch DX API errors. + throw Platform::Exception::CreateException(hr); + } +} + +MediaStreamer::MediaStreamer() : + m_offset(0) +{ + ZeroMemory(&m_waveFormat, sizeof(m_waveFormat)); + m_location = Package::Current->InstalledLocation; + m_locationPath = Platform::String::Concat(m_location->Path, "\\Assets\\Resources\\"); +} + +MediaStreamer::~MediaStreamer() +{ +} +Platform::Array^ MediaStreamer::ReadData( + _In_ Platform::String^ filename + ) +{ + CREATEFILE2_EXTENDED_PARAMETERS extendedParams = {0}; + extendedParams.dwSize = sizeof(CREATEFILE2_EXTENDED_PARAMETERS); + extendedParams.dwFileAttributes = FILE_ATTRIBUTE_NORMAL; + extendedParams.dwFileFlags = FILE_FLAG_SEQUENTIAL_SCAN; + extendedParams.dwSecurityQosFlags = SECURITY_ANONYMOUS; + extendedParams.lpSecurityAttributes = nullptr; + extendedParams.hTemplateFile = nullptr; + + Wrappers::FileHandle file( + CreateFile2( + filename->Data(), + GENERIC_READ, + FILE_SHARE_READ, + OPEN_EXISTING, + &extendedParams + ) + ); + if (file.Get()==INVALID_HANDLE_VALUE) + { + throw ref new Platform::FailureException(); + } + + FILE_STANDARD_INFO fileInfo = {0}; + if (!GetFileInformationByHandleEx( + file.Get(), + FileStandardInfo, + &fileInfo, + sizeof(fileInfo) + )) + { + throw ref new Platform::FailureException(); + } + + if (fileInfo.EndOfFile.HighPart != 0) + { + throw ref new Platform::OutOfMemoryException(); + } + + Platform::Array^ fileData = ref new Platform::Array(fileInfo.EndOfFile.LowPart); + + if (!ReadFile( + file.Get(), + fileData->Data, + fileData->Length, + nullptr, + nullptr + ) ) + { + throw ref new Platform::FailureException(); + } + + return fileData; +} + +void MediaStreamer::Initialize(__in const WCHAR* url) +{ + + WCHAR filePath[MAX_PATH] = {0}; + if ((wcslen(url) > 1 && url[1] == ':')) + { + // path start with "x:", is absolute path + wcscat_s(filePath, url); + } + else if (wcslen(url) > 0 + && (L'/' == url[0] || L'\\' == url[0])) + { + // path start with '/' or '\', is absolute path without driver name + wcscat_s(filePath, m_locationPath->Data()); + // remove '/' or '\\' + wcscat_s(filePath, (const WCHAR*)url[1]); + }else + { + wcscat_s(filePath, m_locationPath->Data()); + wcscat_s(filePath, url); + } + + + Platform::Array^ data = ReadData(ref new Platform::String(filePath)); + UINT32 length = data->Length; + const byte * dataPtr = data->Data; + UINT32 offset = 0; + + DWORD riffDataSize = 0; + + auto ReadChunk = [&length, &offset, &dataPtr, &riffDataSize](DWORD fourcc, DWORD& outChunkSize, DWORD& outChunkPos) -> HRESULT + { + while (true) + { + if (offset + sizeof(DWORD) * 2 >= length) + { + return E_FAIL; + } + + // Read two DWORDs. + DWORD chunkType = *reinterpret_cast(&dataPtr[offset]); + DWORD chunkSize = *reinterpret_cast(&dataPtr[offset + sizeof(DWORD)]); + offset += sizeof(DWORD) * 2; + + if (chunkType == MAKEFOURCC('R', 'I', 'F', 'F')) + { + riffDataSize = chunkSize; + chunkSize = sizeof(DWORD); + outChunkSize = sizeof(DWORD); + outChunkPos = offset; + } + else + { + outChunkSize = chunkSize; + outChunkPos = offset; + } + + offset += chunkSize; + + if (chunkType == fourcc) + { + return S_OK; + } + } + }; + + // Locate riff chunk, check the file type. + DWORD chunkSize = 0; + DWORD chunkPos = 0; + + ThrowIfFailed(ReadChunk(MAKEFOURCC('R', 'I', 'F', 'F'), chunkSize, chunkPos)); + if (*reinterpret_cast(&dataPtr[chunkPos]) != MAKEFOURCC('W', 'A', 'V', 'E')) ThrowIfFailed(E_FAIL); + + // Locate 'fmt ' chunk, copy to WAVEFORMATEXTENSIBLE. + ThrowIfFailed(ReadChunk(MAKEFOURCC('f', 'm', 't', ' '), chunkSize, chunkPos)); + ThrowIfFailed((chunkSize <= sizeof(m_waveFormat)) ? S_OK : E_FAIL); + CopyMemory(&m_waveFormat, &dataPtr[chunkPos], chunkSize); + + // Locate the 'data' chunk and copy its contents to a buffer. + ThrowIfFailed(ReadChunk(MAKEFOURCC('d', 'a', 't', 'a'), chunkSize, chunkPos)); + m_data.resize(chunkSize); + CopyMemory(m_data.data(), &dataPtr[chunkPos], chunkSize); + + m_offset = 0; +} + +void MediaStreamer::ReadAll(uint8* buffer, uint32 maxBufferSize, uint32* bufferLength) +{ + UINT32 toCopy = m_data.size() - m_offset; + if (toCopy > maxBufferSize) toCopy = maxBufferSize; + + CopyMemory(buffer, m_data.data(), toCopy); + *bufferLength = toCopy; + + m_offset += toCopy; + if (m_offset > m_data.size()) m_offset = m_data.size(); +} + +void MediaStreamer::Restart() +{ + m_offset = 0; +} \ No newline at end of file diff --git a/cocos/audio/wp8/MediaStreamer.h b/cocos/audio/wp8/MediaStreamer.h new file mode 100644 index 0000000000..8620620171 --- /dev/null +++ b/cocos/audio/wp8/MediaStreamer.h @@ -0,0 +1,58 @@ +/* +* 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. +*/ + +#pragma once + + +#include +#include + +ref class MediaStreamer +{ +private: + WAVEFORMATEX m_waveFormat; + uint32 m_maxStreamLengthInBytes; + std::vector m_data; + UINT32 m_offset; + Platform::Array^ ReadData( + _In_ Platform::String^ filename + ); +internal: + Windows::Storage::StorageFolder^ m_location; + Platform::String^ m_locationPath; + +public: + virtual ~MediaStreamer(); + +internal: + MediaStreamer(); + + WAVEFORMATEX& GetOutputWaveFormatEx() + { + return m_waveFormat; + } + + UINT32 GetMaxStreamLengthInBytes() + { + return m_data.size(); + } + + void Initialize(_In_ const WCHAR* url); + void ReadAll(uint8* buffer, uint32 maxBufferSize, uint32* bufferLength); + void Restart(); +}; \ No newline at end of file diff --git a/cocos/audio/wp8/SimpleAudioEngine.cpp b/cocos/audio/wp8/SimpleAudioEngine.cpp new file mode 100644 index 0000000000..e7dffaa3b4 --- /dev/null +++ b/cocos/audio/wp8/SimpleAudioEngine.cpp @@ -0,0 +1,198 @@ +/* +* 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. +*/ + +#include "SimpleAudioEngine.h" +#include "Audio.h" + +#include +//#include "CCCommon.h" +using namespace std; + +namespace CocosDenshion { + +Audio* s_audioController = NULL; +bool s_initialized = false; + +SimpleAudioEngine* SimpleAudioEngine::getInstance() +{ + static SimpleAudioEngine s_SharedEngine; + return &s_SharedEngine; +} + + +static Audio* sharedAudioController() +{ + if (! s_audioController || !s_initialized) + { + if(s_audioController == NULL) + { + s_audioController = new Audio; + } + s_audioController->Initialize(); + s_audioController->CreateResources(); + s_initialized = true; + } + + return s_audioController; +} + +SimpleAudioEngine::SimpleAudioEngine() +{ +} + +SimpleAudioEngine::~SimpleAudioEngine() +{ +} + + +void SimpleAudioEngine::end() +{ + sharedAudioController()->StopBackgroundMusic(true); + sharedAudioController()->StopAllSoundEffects(); + sharedAudioController()->ReleaseResources(); + s_initialized = false; +} + + + +////////////////////////////////////////////////////////////////////////// +// BackgroundMusic +////////////////////////////////////////////////////////////////////////// + +void SimpleAudioEngine::playBackgroundMusic(const char* pszFilePath, bool bLoop) +{ + if (! pszFilePath) + { + return; + } + + sharedAudioController()->PlayBackgroundMusic(pszFilePath, bLoop); +} + +void SimpleAudioEngine::stopBackgroundMusic(bool bReleaseData) +{ + sharedAudioController()->StopBackgroundMusic(bReleaseData); +} + +void SimpleAudioEngine::pauseBackgroundMusic() +{ + sharedAudioController()->PauseBackgroundMusic(); +} + +void SimpleAudioEngine::resumeBackgroundMusic() +{ + sharedAudioController()->ResumeBackgroundMusic(); +} + +void SimpleAudioEngine::rewindBackgroundMusic() +{ + sharedAudioController()->RewindBackgroundMusic(); +} + +bool SimpleAudioEngine::willPlayBackgroundMusic() +{ + return false; +} + +bool SimpleAudioEngine::isBackgroundMusicPlaying() +{ + return sharedAudioController()->IsBackgroundMusicPlaying(); +} + +////////////////////////////////////////////////////////////////////////// +// effect function +////////////////////////////////////////////////////////////////////////// + +unsigned int SimpleAudioEngine::playEffect(const char* pszFilePath, bool bLoop,float pitch, float pan, float gain) +{ + unsigned int sound; + sharedAudioController()->PlaySoundEffect(pszFilePath, bLoop, sound); + // TODO: need to support playEffect parameters + return sound; +} + +void SimpleAudioEngine::stopEffect(unsigned int nSoundId) +{ + sharedAudioController()->StopSoundEffect(nSoundId); +} + +void SimpleAudioEngine::preloadEffect(const char* pszFilePath) +{ + sharedAudioController()->PreloadSoundEffect(pszFilePath); +} + +void SimpleAudioEngine::pauseEffect(unsigned int nSoundId) +{ + sharedAudioController()->PauseSoundEffect(nSoundId); +} + +void SimpleAudioEngine::resumeEffect(unsigned int nSoundId) +{ + sharedAudioController()->ResumeSoundEffect(nSoundId); +} + +void SimpleAudioEngine::pauseAllEffects() +{ + sharedAudioController()->PauseAllSoundEffects(); +} + +void SimpleAudioEngine::resumeAllEffects() +{ + sharedAudioController()->ResumeAllSoundEffects(); +} + +void SimpleAudioEngine::stopAllEffects() +{ + sharedAudioController()->StopAllSoundEffects(); +} + +void SimpleAudioEngine::preloadBackgroundMusic(const char* pszFilePath) +{ + UNUSED_PARAM(pszFilePath); +} + +void SimpleAudioEngine::unloadEffect(const char* pszFilePath) +{ + sharedAudioController()->UnloadSoundEffect(pszFilePath); +} + +////////////////////////////////////////////////////////////////////////// +// volume interface +////////////////////////////////////////////////////////////////////////// + +float SimpleAudioEngine::getBackgroundMusicVolume() +{ + return sharedAudioController()->GetBackgroundVolume(); +} + +void SimpleAudioEngine::setBackgroundMusicVolume(float volume) +{ + sharedAudioController()->SetBackgroundVolume((volume<=0.0f)? 0.0f : volume); +} + +float SimpleAudioEngine::getEffectsVolume() +{ + return sharedAudioController()->GetSoundEffectVolume(); +} + +void SimpleAudioEngine::setEffectsVolume(float volume) +{ + sharedAudioController()->SetSoundEffectVolume((volume<=0.0f)? 0.0f : volume); +} + +} // end of namespace CocosDenshion