mirror of https://github.com/axmolengine/axmol.git
added wp8 and winrt support files and projects
This commit is contained in:
parent
afe561cccc
commit
97bdaad41b
|
@ -0,0 +1,170 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup Label="ProjectConfigurations">
|
||||
<ProjectConfiguration Include="Debug|Win32">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>Win32</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Debug|ARM">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>ARM</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release|Win32">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>Win32</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release|ARM">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>ARM</Platform>
|
||||
</ProjectConfiguration>
|
||||
</ItemGroup>
|
||||
<PropertyGroup Label="Globals">
|
||||
<ProjectGuid>{DF125891-EEE9-4466-B903-F828FD272158}</ProjectGuid>
|
||||
<RootNamespace>CocosDenshion.win32</RootNamespace>
|
||||
<DefaultLanguage>en-US</DefaultLanguage>
|
||||
<MinimumVisualStudioVersion>11.0</MinimumVisualStudioVersion>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
|
||||
<ConfigurationType>StaticLibrary</ConfigurationType>
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
<PlatformToolset>v110_wp80</PlatformToolset>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|ARM'" Label="Configuration">
|
||||
<ConfigurationType>StaticLibrary</ConfigurationType>
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
<PlatformToolset>v110_wp80</PlatformToolset>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
|
||||
<ConfigurationType>StaticLibrary</ConfigurationType>
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||
<PlatformToolset>v110_wp80</PlatformToolset>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|ARM'" Label="Configuration">
|
||||
<ConfigurationType>StaticLibrary</ConfigurationType>
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||
<PlatformToolset>v110_wp80</PlatformToolset>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
|
||||
<ImportGroup Label="PropertySheets">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<Import Project="..\..\2d\cocos2d_wp8_headers.props" />
|
||||
<Import Project="..\..\2d\cocos2d_winrt.props" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|ARM'">
|
||||
<Import Project="..\..\2d\cocos2d_wp8_headers.props" />
|
||||
<Import Project="..\..\2d\cocos2d_winrt.props" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<Import Project="..\..\2d\cocos2d_wp8_headers.props" />
|
||||
<Import Project="..\..\2d\cocos2d_winrt.props" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|ARM'">
|
||||
<Import Project="..\..\2d\cocos2d_wp8_headers.props" />
|
||||
<Import Project="..\..\2d\cocos2d_winrt.props" />
|
||||
</ImportGroup>
|
||||
<PropertyGroup Label="UserMacros" />
|
||||
<PropertyGroup>
|
||||
<GenerateManifest>false</GenerateManifest>
|
||||
</PropertyGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<ClCompile>
|
||||
<PreprocessorDefinitions>_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<PrecompiledHeader>NotUsing</PrecompiledHeader>
|
||||
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
|
||||
<AdditionalUsingDirectories>$(WindowsSDK_MetadataPath);$(AdditionalUsingDirectories)</AdditionalUsingDirectories>
|
||||
<AdditionalIncludeDirectories>..\Include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<PreprocessorDefinitions>WP8;_DEBUG;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<DisableSpecificWarnings>4251</DisableSpecificWarnings>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<IgnoreAllDefaultLibraries>false</IgnoreAllDefaultLibraries>
|
||||
</Link>
|
||||
<Lib>
|
||||
<AdditionalOptions>/IGNORE:4264 %(AdditionalOptions)</AdditionalOptions>
|
||||
</Lib>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<ClCompile>
|
||||
<PreprocessorDefinitions>_LIB;NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<PrecompiledHeader>NotUsing</PrecompiledHeader>
|
||||
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
|
||||
<AdditionalUsingDirectories>$(WindowsSDK_MetadataPath);$(AdditionalUsingDirectories)</AdditionalUsingDirectories>
|
||||
<AdditionalIncludeDirectories>..\Include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<PreprocessorDefinitions>WP8;NDEBUG;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<DisableSpecificWarnings>4251</DisableSpecificWarnings>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<IgnoreAllDefaultLibraries>false</IgnoreAllDefaultLibraries>
|
||||
</Link>
|
||||
<Lib>
|
||||
<AdditionalOptions>/IGNORE:4264 %(AdditionalOptions)</AdditionalOptions>
|
||||
</Lib>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|ARM'">
|
||||
<ClCompile>
|
||||
<PreprocessorDefinitions>_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<PrecompiledHeader>NotUsing</PrecompiledHeader>
|
||||
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
|
||||
<AdditionalUsingDirectories>$(WindowsSDK_MetadataPath);$(AdditionalUsingDirectories)</AdditionalUsingDirectories>
|
||||
<AdditionalIncludeDirectories>..\Include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<PreprocessorDefinitions>WP8;_DEBUG;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<DisableSpecificWarnings>4251</DisableSpecificWarnings>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<IgnoreAllDefaultLibraries>false</IgnoreAllDefaultLibraries>
|
||||
</Link>
|
||||
<Lib>
|
||||
<AdditionalOptions>/IGNORE:4264 %(AdditionalOptions)</AdditionalOptions>
|
||||
</Lib>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|ARM'">
|
||||
<ClCompile>
|
||||
<PreprocessorDefinitions>_LIB;NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<PrecompiledHeader>NotUsing</PrecompiledHeader>
|
||||
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
|
||||
<AdditionalUsingDirectories>$(WindowsSDK_MetadataPath);$(AdditionalUsingDirectories)</AdditionalUsingDirectories>
|
||||
<AdditionalIncludeDirectories>..\Include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<PreprocessorDefinitions>WP8;NDEBUG;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<DisableSpecificWarnings>4251</DisableSpecificWarnings>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<IgnoreAllDefaultLibraries>false</IgnoreAllDefaultLibraries>
|
||||
</Link>
|
||||
<Lib>
|
||||
<AdditionalOptions>/IGNORE:4264 %(AdditionalOptions)</AdditionalOptions>
|
||||
</Lib>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="Windows">
|
||||
<IsWinMDFile>true</IsWinMDFile>
|
||||
</Reference>
|
||||
<Reference Include="platform.winmd">
|
||||
<IsWinMDFile>true</IsWinMDFile>
|
||||
<Private>false</Private>
|
||||
</Reference>
|
||||
</ItemGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
<Import Project="$(MSBuildExtensionsPath)\Microsoft\WindowsPhone\v$(TargetPlatformVersion)\Microsoft.Cpp.WindowsPhone.$(TargetPlatformVersion).targets" />
|
||||
<ImportGroup Label="ExtensionTargets">
|
||||
</ImportGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="..\include\Export.h" />
|
||||
<ClInclude Include="..\include\SimpleAudioEngine.h" />
|
||||
<ClInclude Include="..\wp8\Audio.h" />
|
||||
<ClInclude Include="..\wp8\MediaStreamer.h" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="..\wp8\Audio.cpp" />
|
||||
<ClCompile Include="..\wp8\MediaStreamer.cpp" />
|
||||
<ClCompile Include="..\wp8\SimpleAudioEngine.cpp" />
|
||||
</ItemGroup>
|
||||
</Project>
|
|
@ -0,0 +1,42 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup>
|
||||
<Filter Include="Header Files">
|
||||
<UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
|
||||
<Extensions>h;hpp;hxx;hm;inl;inc;xsd</Extensions>
|
||||
</Filter>
|
||||
<Filter Include="Resource Files">
|
||||
<UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
|
||||
<Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx</Extensions>
|
||||
</Filter>
|
||||
<Filter Include="Source Files">
|
||||
<UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
|
||||
<Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
|
||||
</Filter>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="..\include\Export.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\include\SimpleAudioEngine.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\wp8\Audio.h">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\wp8\MediaStreamer.h">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="..\wp8\Audio.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\wp8\MediaStreamer.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\wp8\SimpleAudioEngine.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
</Project>
|
|
@ -0,0 +1,6 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<PropertyGroup>
|
||||
<FullDeploy>true</FullDeploy>
|
||||
</PropertyGroup>
|
||||
</Project>
|
|
@ -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);
|
||||
}
|
|
@ -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 <map>
|
||||
|
||||
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<unsigned int, SoundEffectData> 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);
|
||||
};
|
|
@ -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<IMFMediaType> outputMediaType;
|
||||
Microsoft::WRL::ComPtr<IMFMediaType> 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<double>(10000000)); // duration is in 100ns units, convert to seconds
|
||||
m_maxStreamLengthInBytes = static_cast<unsigned int>(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<IMFSample> sample;
|
||||
Microsoft::WRL::ComPtr<IMFMediaBuffer> 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)
|
||||
);
|
||||
}
|
|
@ -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<IMFSourceReader> m_reader;
|
||||
Microsoft::WRL::ComPtr<IMFMediaType> 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();
|
||||
};
|
|
@ -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 <map>
|
||||
|
||||
//#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
|
|
@ -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);
|
||||
}
|
|
@ -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 <wrl.h>
|
||||
#include <d3d11_1.h>
|
||||
#include <agile.h>
|
||||
#include <DirectXMath.h>
|
||||
#include <memory>
|
||||
|
||||
#define XAUDIO2_HELPER_FUNCTIONS 1
|
||||
#include <xaudio2.h>
|
||||
#include <map>
|
||||
|
||||
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<unsigned int, SoundEffectData> EffectList;
|
||||
typedef std::pair<unsigned int, SoundEffectData> 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);
|
||||
};
|
|
@ -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 <wrl\wrappers\corewrappers.h>
|
||||
#include <ppltasks.h>
|
||||
|
||||
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<byte>^ 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<byte>^ fileData = ref new Platform::Array<byte>(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<byte>^ 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<const DWORD *>(&dataPtr[offset]);
|
||||
DWORD chunkSize = *reinterpret_cast<const DWORD *>(&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<const DWORD *>(&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;
|
||||
}
|
|
@ -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 <vector>
|
||||
#include <xaudio2.h>
|
||||
|
||||
ref class MediaStreamer
|
||||
{
|
||||
private:
|
||||
WAVEFORMATEX m_waveFormat;
|
||||
uint32 m_maxStreamLengthInBytes;
|
||||
std::vector<byte> m_data;
|
||||
UINT32 m_offset;
|
||||
Platform::Array<byte>^ 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();
|
||||
};
|
|
@ -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 <map>
|
||||
//#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
|
Loading…
Reference in New Issue