New feature: XAudio2.

This commit is contained in:
Nomango 2018-02-01 22:07:44 +08:00
parent 71b39a8149
commit 342b8e050a
15 changed files with 803 additions and 532 deletions

View File

@ -20,35 +20,65 @@ bool e2d::EGame::init(LPCTSTR sTitle, UINT32 nWidth, UINT32 nHeight, LPCTSTR pIc
return false;
}
do
{
// 初始化 COM 组件
if (SUCCEEDED(CoInitialize(NULL)))
if (FAILED(CoInitializeEx(NULL, COINIT_MULTITHREADED)))
{
WARN_IF(true, "CoInitializeEx Failed!");
break;
}
// 创建设备无关资源
if (ERenderer::__createDeviceIndependentResources())
if (!ERenderer::__createDeviceIndependentResources())
{
WARN_IF(true, "ERenderer::__createDeviceIndependentResources Failed!");
break;
}
// 初始化窗口
if (EWindow::__init(sTitle, nWidth, nHeight, pIconID))
if (!EWindow::__init(sTitle, nWidth, nHeight, pIconID))
{
WARN_IF(true, "EWindow::__init Failed!");
break;
}
// 创建设备相关资源
if (ERenderer::__createDeviceResources())
if (!ERenderer::__createDeviceResources())
{
WARN_IF(true, "ERenderer::__createDeviceResources Failed!");
break;
}
// 初始化计时
if (!ETime::__init())
{
WARN_IF(true, "ETime::__init Failed!");
break;
}
// 初始化 DirectInput
if (!EInput::__init())
{
WARN_IF(true, "EInput::__init Failed!");
break;
}
// 初始化播放器
if (!EMusicManager::__init())
{
WARN_IF(true, "EMusicManager::__init Failed!");
break;
}
// 重设 Client 大小
EWindow::setSize(nWidth, nHeight);
// 设置 AppName
if (sAppname)
{
s_sAppName = sAppname;
}
else
{
s_sAppName = EWindow::getTitle();
}
s_sAppName = (sAppname != nullptr) ? sAppname : EWindow::getTitle();
// 标志初始化成功
s_bInitialized = true;
}
}
}
}
} while (0);
return s_bInitialized;
}
@ -61,12 +91,8 @@ int e2d::EGame::run()
return -1;
}
// 初始化 DirectInput
EInput::__init();
// 初始化计时操作
ETime::__init();
// 进入第一个场景
ESceneManager::__enterNextScene();
// 初始化场景管理器
ESceneManager::__init();
// 显示窗口
::ShowWindow(EWindow::getHWnd(), SW_SHOWNORMAL);
// 刷新窗口内容
@ -93,8 +119,8 @@ int e2d::EGame::run()
}
else
{
// 挂起线程
ETime::__sleep();
EObjectManager::__flush(); // 刷新内存池
ETime::__sleep(); // 挂起线程
}
}
@ -131,16 +157,24 @@ void e2d::EGame::quit()
void e2d::EGame::uninit()
{
// 删除所有场景
ESceneManager::__uninit();
// 重置窗口属性
EWindow::__uninit();
// 关闭输入
EInput::__uninit();
// 关闭播放器
EMusicManager::__uninit();
// 恢复计时操作
ETime::__uninit();
// 删除渲染相关资源
ERenderer::__discardResources();
// 刷新内存池
EObjectManager::__flush();
CoUninitialize();
s_bInitialized = false;
}
void e2d::EGame::__update()

View File

@ -28,7 +28,7 @@ void EInput::__uninit()
SafeReleaseInterface(&s_pDirectInput);
}
HRESULT EInput::__init()
bool EInput::__init()
{
ZeroMemory(s_KeyBuffer, sizeof(s_KeyBuffer));
ZeroMemory(s_KeySecBuffer, sizeof(s_KeySecBuffer));
@ -41,7 +41,7 @@ HRESULT EInput::__init()
DIRECTINPUT_VERSION,
IID_IDirectInput8,
(void**)&s_pDirectInput,
NULL
nullptr
);
if (SUCCEEDED(hr))
@ -50,7 +50,7 @@ HRESULT EInput::__init()
hr = s_pDirectInput->CreateDevice(
GUID_SysKeyboard,
&s_KeyboardDevice,
NULL
nullptr
);
if (SUCCEEDED(hr))
@ -77,7 +77,7 @@ HRESULT EInput::__init()
if (SUCCEEDED(hr))
{
// 初始化鼠标设备
hr = s_pDirectInput->CreateDevice(GUID_SysMouse, &s_MouseDevice, NULL);
hr = s_pDirectInput->CreateDevice(GUID_SysMouse, &s_MouseDevice, nullptr);
if (SUCCEEDED(hr))
{

View File

@ -33,9 +33,10 @@ static steady_clock::time_point s_tNow;
static steady_clock::time_point s_tLast;
void e2d::ETime::__init()
bool e2d::ETime::__init()
{
s_tStart = s_tLast = s_tNow = steady_clock::now();
return true;
}
void e2d::ETime::__uninit()
@ -83,12 +84,23 @@ static LARGE_INTEGER s_tLast;
void e2d::ETime::__init()
bool e2d::ETime::__init()
{
::timeBeginPeriod(1); // 修改时间精度
::QueryPerformanceFrequency(&s_tFreq); // 获取时钟频率
::QueryPerformanceCounter(&s_tNow); // 刷新当前时间
bool bRet = false;
if (::timeBeginPeriod(1))
{
// 修改时间精度
if (::QueryPerformanceFrequency(&s_tFreq)) // 获取时钟频率
{
if (::QueryPerformanceCounter(&s_tNow)) // 刷新当前时间
{
s_tStart = s_tLast = s_tNow;
bRet = true;
}
}
}
return bRet;
}
void e2d::ETime::__uninit()

View File

@ -59,7 +59,7 @@ bool e2d::EWindow::__init(LPCTSTR sTitle, UINT32 nWidth, UINT32 nHeight, LPCTSTR
s_HWnd = CreateWindow(
L"Easy2DApp",
sTitle,
WS_OVERLAPPED | WS_SYSMENU,
WS_OVERLAPPED | WS_SYSMENU | WS_MINIMIZEBOX,
0, 0, nWidth, nHeight,
NULL,
NULL,
@ -193,7 +193,7 @@ void e2d::EWindow::showConsole(bool show /* = true */)
FILE * stdoutStream, * stdinStream, * stderrStream;
freopen_s(&stdoutStream, "conout$", "w+t", stdout);
freopen_s(&stdinStream, "conin$", "r+t", stdin);
freopen_s(&stderrStream, "conerr$", "w+t", stderr);
freopen_s(&stderrStream, "conout$", "w+t", stderr);
// 禁用控制台关闭按钮
HMENU hmenu = ::GetSystemMenu(hwnd, FALSE);
::RemoveMenu(hmenu, SC_CLOSE, MF_BYCOMMAND);

View File

@ -211,7 +211,6 @@ bool e2d::EImage::preload(LPCTSTR resourceName, LPCTSTR resourceType)
IWICBitmapFrameDecode *pSource = nullptr;
IWICStream *pStream = nullptr;
IWICFormatConverter *pConverter = nullptr;
IWICBitmapScaler *pScaler = nullptr;
ID2D1Bitmap *pBitmap = nullptr;
HRSRC imageResHandle = nullptr;
@ -329,7 +328,6 @@ bool e2d::EImage::preload(LPCTSTR resourceName, LPCTSTR resourceType)
SafeReleaseInterface(&pSource);
SafeReleaseInterface(&pStream);
SafeReleaseInterface(&pConverter);
SafeReleaseInterface(&pScaler);
return SUCCEEDED(hr);
}

View File

@ -0,0 +1,127 @@
#include "..\emanagers.h"
#include "..\etools.h"
#include <map>
static IXAudio2 * s_pXAudio2 = nullptr;
static IXAudio2MasteringVoice * s_pMasteringVoice = nullptr;
typedef std::pair<UINT, e2d::EMusic *> MusicPair;
typedef std::map<UINT, e2d::EMusic *> MusicList;
static MusicList& getMusicList()
{
static MusicList s_List;
return s_List;
}
e2d::EMusic * e2d::EMusicManager::add(const EString & strFilePath)
{
EMusic * pPlayer = get(strFilePath);
if (pPlayer)
{
return pPlayer;
}
else
{
UINT nRet = strFilePath.hash();
getMusicList().insert(MusicPair(nRet, new EMusic()));
pPlayer = getMusicList()[nRet];
if (pPlayer->_open(strFilePath))
{
return pPlayer;
}
else
{
delete pPlayer;
getMusicList().erase(nRet);
return nullptr;
}
}
}
e2d::EMusic * e2d::EMusicManager::get(const EString & strFilePath)
{
if (strFilePath.isEmpty())
return nullptr;
UINT nRet = strFilePath.hash();
if (getMusicList().end() != getMusicList().find(nRet))
return getMusicList()[nRet];
return nullptr;
}
void e2d::EMusicManager::pauseAllMusics()
{
for (auto iter = getMusicList().begin(); iter != getMusicList().end(); iter++)
{
(*iter).second->pause();
}
}
void e2d::EMusicManager::resumeAllMusics()
{
for (auto iter = getMusicList().begin(); iter != getMusicList().end(); iter++)
{
(*iter).second->resume();
}
}
void e2d::EMusicManager::stopAllMusics()
{
for (auto iter = getMusicList().begin(); iter != getMusicList().end(); iter++)
{
(*iter).second->stop();
}
}
IXAudio2 * e2d::EMusicManager::getIXAudio2()
{
return s_pXAudio2;
}
IXAudio2MasteringVoice * e2d::EMusicManager::getIXAudio2MasteringVoice()
{
return s_pMasteringVoice;
}
bool e2d::EMusicManager::__init()
{
HRESULT hr;
if (FAILED(hr = XAudio2Create(&s_pXAudio2, 0)))
{
WARN_IF(true, "Failed to init XAudio2 engine");
return false;
}
if (FAILED(hr = s_pXAudio2->CreateMasteringVoice(&s_pMasteringVoice)))
{
WARN_IF(true, "Failed creating mastering voice");
SafeReleaseInterface(&s_pXAudio2);
return false;
}
return true;
}
void e2d::EMusicManager::__uninit()
{
for (auto iter = getMusicList().begin(); iter != getMusicList().end(); iter++)
{
(*iter).second->_close();
(*iter).second->release();
}
getMusicList().clear();
if (s_pMasteringVoice)
{
s_pMasteringVoice->DestroyVoice();
}
SafeReleaseInterface(&s_pXAudio2);
}

View File

@ -94,8 +94,24 @@ void e2d::ESceneManager::__update()
// 下一场景指针不为空时,切换场景
if (s_pNextScene)
{
// 进入下一场景
__enterNextScene();
// 若要保存当前场景,把它放入栈中
if (s_pCurrentScene->m_bWillSave)
{
s_SceneStack.push(s_pCurrentScene);
}
else
{
SafeRelease(&s_pCurrentScene);
}
// 执行当前场景的 onExit 函数
s_pCurrentScene->onExit();
// 执行下一场景的 onEnter 函数
s_pNextScene->onEnter();
s_pCurrentScene = s_pNextScene; // 切换场景
s_pNextScene = nullptr; // 下一场景置空
}
// 断言当前场景非空
@ -119,30 +135,21 @@ void e2d::ESceneManager::__render()
}
}
void e2d::ESceneManager::__enterNextScene()
bool e2d::ESceneManager::__init()
{
if (s_pNextScene == nullptr)
return;
return false;
// 执行当前场景的 onCloseWindow 函数
if (s_pCurrentScene)
{
s_pCurrentScene->onExit();
if (s_pCurrentScene->m_bWillSave)
{
// 若要保存当前场景,把它放入栈中
s_SceneStack.push(s_pCurrentScene);
}
else
{
SafeRelease(&s_pCurrentScene);
}
}
// 执行下一场景的 onEnter 函数
s_pNextScene->onEnter();
s_pCurrentScene = s_pNextScene; // 切换场景
s_pNextScene = nullptr; // 下一场景置空
s_pCurrentScene = s_pNextScene;
s_pCurrentScene->onEnter();
s_pNextScene = nullptr;
return true;
}
void e2d::ESceneManager::__uninit()
{
SafeRelease(&s_pCurrentScene);
SafeRelease(&s_pNextScene);
SafeRelease(&s_pTransition);
ESceneManager::clearScene();
}

View File

@ -1,443 +1,469 @@
#include "..\etools.h"
#include <map>
#include <mmsystem.h>
#pragma comment(lib , "winmm.lib")
#include "..\emanagers.h"
#define WIN_CLASS_NAME L"MciPlayerCallbackWnd"
using namespace e2d;
static HINSTANCE s_hInstance = nullptr;
#ifndef SAFE_DELETE
#define SAFE_DELETE(p) { if (p) { delete (p); (p)=nullptr; } }
#endif
#ifndef SAFE_DELETE_ARRAY
#define SAFE_DELETE_ARRAY(p) { if (p) { delete[] (p); (p)=nullptr; } }
#endif
LRESULT WINAPI _MciPlayerProc(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam);
static bool ExtractResource(LPCTSTR strDstFile, LPCTSTR strResType, LPCTSTR strResName);
class MciPlayer
inline bool TraceError(LPCTSTR sPrompt)
{
public:
MciPlayer();
~MciPlayer();
void close();
bool open(const e2d::EString & pFileName, UINT uId);
bool open(const e2d::EString & pResouceName, const e2d::EString & pResouceType, const e2d::EString & musicExtension, UINT uId);
void play(int repeatTimes);
void pause();
void resume();
void stop();
void rewind();
bool isPlaying();
UINT getMusicID();
private:
friend LRESULT WINAPI _MciPlayerProc(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam);
void _sendCommand(int nCommand, DWORD_PTR param1 = 0, DWORD_PTR parma2 = 0);
MCIDEVICEID m_dev;
HWND m_wnd;
UINT m_nMusicID;
bool m_bPlaying;
int m_nRepeatTimes;
};
MciPlayer::MciPlayer()
: m_wnd(NULL)
, m_dev(0L)
, m_nMusicID(0)
, m_bPlaying(false)
, m_nRepeatTimes(0)
{
if (!s_hInstance)
{
s_hInstance = HINST_THISCOMPONENT;
WNDCLASS wc;
wc.style = 0;
wc.lpfnWndProc = _MciPlayerProc;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = s_hInstance;
wc.hIcon = 0;
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.hbrBackground = NULL;
wc.lpszMenuName = NULL;
wc.lpszClassName = WIN_CLASS_NAME;
if (!RegisterClass(&wc) && 1410 != GetLastError())
{
return;
}
}
m_wnd = CreateWindowEx(
WS_EX_APPWINDOW,
WIN_CLASS_NAME,
NULL,
WS_POPUPWINDOW,
0, 0, 0, 0,
NULL,
NULL,
s_hInstance,
NULL);
if (m_wnd)
{
SetWindowLongPtr(m_wnd, GWLP_USERDATA, (LONG_PTR)this);
}
}
MciPlayer::~MciPlayer()
{
close();
DestroyWindow(m_wnd);
}
bool MciPlayer::open(const e2d::EString & pFileName, UINT uId)
{
if (pFileName.isEmpty())
WARN_IF(true, "EMusic error: %s failed!", sPrompt);
return false;
}
close();
inline bool TraceError(LPCTSTR sPrompt, HRESULT hr)
{
WARN_IF(true, "EMusic error: %s (%#X)", sPrompt, hr);
return false;
}
MCI_OPEN_PARMS mciOpen = { 0 };
mciOpen.lpstrDeviceType = 0;
mciOpen.lpstrElementName = pFileName;
MCIERROR mciError;
mciError = mciSendCommand(
0,
MCI_OPEN,
MCI_OPEN_ELEMENT,
reinterpret_cast<DWORD_PTR>(&mciOpen)
);
EMusic::EMusic()
: m_bOpened(false)
, m_bPlaying(false)
, m_pwfx(nullptr)
, m_hmmio(nullptr)
, m_pResourceBuffer(nullptr)
, m_pbWaveData(nullptr)
, m_dwSize(0)
, m_pSourceVoice(nullptr)
{
}
if (mciError == 0)
EMusic::~EMusic()
{
_close();
}
bool EMusic::_open(LPWSTR strFileName)
{
if (m_bOpened)
{
m_dev = mciOpen.wDeviceID;
m_nMusicID = uId;
WARN_IF(true, L"EMusic can be opened only once!");
return false;
}
if (strFileName == nullptr)
{
WARN_IF(true, L"EMusic::_open Invalid file name.");
return false;
}
IXAudio2 * pXAudio2 = EMusicManager::getIXAudio2();
if (!pXAudio2)
{
WARN_IF(true, L"IXAudio2 nullptr pointer error!");
return false;
}
// 定位 wave 文件
wchar_t strFilePath[MAX_PATH];
if (!_findMediaFileCch(strFilePath, MAX_PATH, strFileName))
{
WARN_IF(true, L"Failed to find media file: %s", strFileName);
return false;
}
m_hmmio = mmioOpen(strFilePath, nullptr, MMIO_ALLOCBUF | MMIO_READ);
if (nullptr == m_hmmio)
{
return TraceError(L"mmioOpen");
}
if (!_readMMIO())
{
// 读取非 wave 文件时 ReadMMIO 调用失败
mmioClose(m_hmmio, 0);
return TraceError(L"_readMMIO");
}
if (!_resetFile())
return TraceError(L"_resetFile");
// 重置文件后wave 文件的大小是 m_ck.cksize
m_dwSize = m_ck.cksize;
// 将样本数据读取到内存中
m_pbWaveData = new BYTE[m_dwSize];
if (!_read(m_pbWaveData, m_dwSize))
{
TraceError(L"Failed to read WAV data");
SAFE_DELETE_ARRAY(m_pbWaveData);
return false;
}
// 创建音源
HRESULT hr;
if (FAILED(hr = pXAudio2->CreateSourceVoice(&m_pSourceVoice, m_pwfx)))
{
TraceError(L"Error %#X creating source voice", hr);
SAFE_DELETE_ARRAY(m_pbWaveData);
return false;
}
m_bOpened = true;
m_bPlaying = false;
return true;
}
return false;
}
bool MciPlayer::open(const e2d::EString & pResouceName, const e2d::EString & pResouceType, const e2d::EString & musicExtension, UINT uId)
bool EMusic::play(int nLoopCount)
{
// 忽略不存在的文件
if (pResouceName.isEmpty() || pResouceType.isEmpty() || musicExtension.isEmpty()) return false;
HRESULT hr;
// 获取临时文件目录
e2d::EString tempFileName = e2d::EFile::getTempPath();
// 产生临时文件的文件名
tempFileName = tempFileName + L"\\" + uId + L"." + musicExtension;
// 导出资源为临时文件
if (ExtractResource(tempFileName, pResouceType, pResouceName))
{
return open(tempFileName, uId);
}
return false;
}
void MciPlayer::play(int repeatTimes)
{
if (!m_dev)
{
return;
}
MCI_PLAY_PARMS mciPlay = { 0 };
mciPlay.dwCallback = reinterpret_cast<DWORD_PTR>(m_wnd);
// 播放声音
MCIERROR mciError = mciSendCommand(
m_dev,
MCI_PLAY,
MCI_FROM | MCI_NOTIFY,
reinterpret_cast<DWORD_PTR>(&mciPlay)
);
if (!mciError)
{
m_bPlaying = true;
m_nRepeatTimes = repeatTimes;
}
}
void MciPlayer::close()
{
if (m_bPlaying)
{
stop();
}
if (m_dev)
// 提交 wave 样本数据
XAUDIO2_BUFFER buffer = { 0 };
buffer.pAudioData = m_pbWaveData;
buffer.Flags = XAUDIO2_END_OF_STREAM;
buffer.AudioBytes = m_dwSize;
buffer.LoopCount = nLoopCount;
if (FAILED(hr = m_pSourceVoice->SubmitSourceBuffer(&buffer)))
{
_sendCommand(MCI_CLOSE);
}
m_dev = 0;
m_bPlaying = false;
}
void MciPlayer::pause()
{
_sendCommand(MCI_PAUSE);
m_bPlaying = false;
}
void MciPlayer::resume()
{
_sendCommand(MCI_RESUME);
m_bPlaying = true;
}
void MciPlayer::stop()
{
_sendCommand(MCI_STOP);
m_bPlaying = false;
}
void MciPlayer::rewind()
{
if (!m_dev)
{
return;
}
mciSendCommand(m_dev, MCI_SEEK, MCI_SEEK_TO_START, 0);
MCI_PLAY_PARMS mciPlay = { 0 };
mciPlay.dwCallback = reinterpret_cast<DWORD_PTR>(m_wnd);
MCIERROR mciError = mciSendCommand(
m_dev,
MCI_PLAY,
MCI_NOTIFY,
reinterpret_cast<DWORD_PTR>(&mciPlay)
);
m_bPlaying = mciError ? false : true;
}
bool MciPlayer::isPlaying()
{
return m_bPlaying;
}
UINT MciPlayer::getMusicID()
{
return m_nMusicID;
}
void MciPlayer::_sendCommand(int nCommand, DWORD_PTR param1, DWORD_PTR parma2)
{
// 空设备时忽略这次操作
if (!m_dev)
{
return;
}
// 向当前设备发送操作
mciSendCommand(m_dev, nCommand, param1, parma2);
}
bool ExtractResource(LPCTSTR strDstFile, LPCTSTR strResType, LPCTSTR strResName)
{
// 创建文件
HANDLE hFile = ::CreateFile(strDstFile, GENERIC_WRITE, NULL, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_TEMPORARY, NULL);
if (hFile == INVALID_HANDLE_VALUE)
TraceError(L"Error %#X submitting source buffer", hr);
m_pSourceVoice->DestroyVoice();
SAFE_DELETE_ARRAY(m_pbWaveData);
return false;
// 查找资源文件中、加载资源到内存、得到资源大小
HRSRC hRes = ::FindResource(NULL, strResName, strResType);
HGLOBAL hMem = ::LoadResource(NULL, hRes);
DWORD dwSize = ::SizeofResource(NULL, hRes);
// 写入文件
DWORD dwWrite = 0; // 返回写入字节
::WriteFile(hFile, hMem, dwSize, &dwWrite, NULL);
::CloseHandle(hFile);
return true;
}
LRESULT WINAPI _MciPlayerProc(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam)
{
MciPlayer * pPlayer = NULL;
if (Msg == MM_MCINOTIFY
&& wParam == MCI_NOTIFY_SUCCESSFUL
&& (pPlayer = (MciPlayer *)GetWindowLongPtr(hWnd, GWLP_USERDATA)))
{
if (pPlayer->m_nRepeatTimes > 0)
{
pPlayer->m_nRepeatTimes--;
}
if (pPlayer->m_nRepeatTimes)
if (SUCCEEDED(hr = m_pSourceVoice->Start(0)))
{
mciSendCommand(static_cast<MCIDEVICEID>(lParam), MCI_SEEK, MCI_SEEK_TO_START, 0);
m_bPlaying = true;
}
MCI_PLAY_PARMS mciPlay = { 0 };
mciPlay.dwCallback = reinterpret_cast<DWORD_PTR>(hWnd);
mciSendCommand(static_cast<MCIDEVICEID>(lParam), MCI_PLAY, MCI_NOTIFY, reinterpret_cast<DWORD_PTR>(&mciPlay));
return SUCCEEDED(hr);
}
bool EMusic::pause()
{
if (m_pSourceVoice)
{
if (SUCCEEDED(m_pSourceVoice->Stop()))
{
m_bPlaying = false;
return true;
}
}
return false;
}
bool EMusic::resume()
{
if (m_pSourceVoice)
{
if (SUCCEEDED(m_pSourceVoice->Start()))
{
m_bPlaying = true;
return true;
}
}
return false;
}
bool EMusic::stop()
{
if (m_pSourceVoice)
{
if (SUCCEEDED(m_pSourceVoice->Stop()))
{
m_pSourceVoice->ExitLoop();
m_pSourceVoice->FlushSourceBuffers();
m_bPlaying = false;
return true;
}
}
return false;
}
bool EMusic::isPlaying()
{
if (m_pSourceVoice)
{
XAUDIO2_VOICE_STATE state;
m_pSourceVoice->GetState(&state);
if (state.BuffersQueued == 0)
{
m_bPlaying = false;
}
return m_bPlaying;
}
else
{
pPlayer->m_bPlaying = false;
return 0;
return false;
}
}
return DefWindowProc(hWnd, Msg, wParam, lParam);
}
typedef std::pair<UINT, MciPlayer *> MusicPair;
typedef std::map<UINT, MciPlayer *> MusicList;
static MusicList& getMciPlayerList()
float e2d::EMusic::getVolume() const
{
static MusicList s_List;
return s_List;
}
UINT e2d::EMusic::play(const EString & musicFilePath, int repeatTimes)
{
UINT nRet = preload(musicFilePath);
if (nRet)
float fVolume = 0.0f;
if (m_pSourceVoice)
{
getMciPlayerList()[nRet]->play(repeatTimes);
m_pSourceVoice->GetVolume(&fVolume);
}
return nRet;
return fVolume;
}
UINT e2d::EMusic::play(const EString & musicResourceName, const EString & musicResourceType, const EString & musicExtension, int repeatTimes)
bool e2d::EMusic::setVolume(float fVolume)
{
UINT nRet = preload(musicResourceName, musicResourceType, musicExtension);
if (nRet)
if (m_pSourceVoice)
{
getMciPlayerList()[nRet]->play(repeatTimes);
}
return nRet;
}
UINT e2d::EMusic::preload(const EString & musicFilePath)
{
if (musicFilePath.isEmpty())
return 0;
UINT nRet = musicFilePath.hash();
if (getMciPlayerList().end() != getMciPlayerList().find(nRet))
return nRet;
getMciPlayerList().insert(MusicPair(nRet, new MciPlayer()));
MciPlayer * pPlayer = getMciPlayerList()[nRet];
pPlayer->open(musicFilePath, nRet);
if (nRet == pPlayer->getMusicID()) return nRet;
delete pPlayer;
getMciPlayerList().erase(nRet);
return 0;
}
UINT e2d::EMusic::preload(const EString & musicResourceName, const EString & musicResourceType, const EString & musicExtension)
{
if (musicResourceName.isEmpty() || musicResourceType.isEmpty())
return 0;
UINT nRet = musicResourceName.hash();
if (getMciPlayerList().end() != getMciPlayerList().find(nRet))
return nRet;
getMciPlayerList().insert(MusicPair(nRet, new MciPlayer()));
MciPlayer * pPlayer = getMciPlayerList()[nRet];
pPlayer->open(musicResourceName, musicResourceType, musicExtension, nRet);
if (nRet == pPlayer->getMusicID()) return nRet;
delete pPlayer;
getMciPlayerList().erase(nRet);
return 0;
}
bool e2d::EMusic::resume(UINT musicId)
{
MusicList::iterator p = getMciPlayerList().find(musicId);
if (p != getMciPlayerList().end())
{
p->second->resume();
return true;
return SUCCEEDED(m_pSourceVoice->SetVolume(min(max(fVolume, -224), 224)));
}
return false;
}
bool e2d::EMusic::resume(const EString & musicName)
float e2d::EMusic::getFrequencyRatio() const
{
return resume(musicName.hash());;
float fFrequencyRatio = 0.0f;
if (m_pSourceVoice)
{
m_pSourceVoice->GetFrequencyRatio(&fFrequencyRatio);
}
return fFrequencyRatio;
}
bool e2d::EMusic::pause(UINT musicId)
bool e2d::EMusic::setFrequencyRatio(float fFrequencyRatio)
{
MusicList::iterator p = getMciPlayerList().find(musicId);
if (p != getMciPlayerList().end())
if (m_pSourceVoice)
{
p->second->pause();
return true;
fFrequencyRatio = min(max(fFrequencyRatio, XAUDIO2_MIN_FREQ_RATIO), XAUDIO2_MAX_FREQ_RATIO);
return SUCCEEDED(m_pSourceVoice->SetFrequencyRatio(fFrequencyRatio));
}
return false;
}
bool e2d::EMusic::pause(const EString & musicName)
IXAudio2SourceVoice * e2d::EMusic::getIXAudio2SourceVoice() const
{
return pause(musicName.hash());
return m_pSourceVoice;
}
bool e2d::EMusic::stop(UINT musicId)
void EMusic::_close()
{
MusicList::iterator p = getMciPlayerList().find(musicId);
if (p != getMciPlayerList().end())
if (m_pSourceVoice)
{
p->second->stop();
return true;
m_pSourceVoice->DestroyVoice();
m_pSourceVoice = nullptr;
}
if (m_hmmio != nullptr)
{
mmioClose(m_hmmio, 0);
m_hmmio = nullptr;
}
SAFE_DELETE_ARRAY(m_pResourceBuffer);
SAFE_DELETE_ARRAY(m_pbWaveData);
SAFE_DELETE_ARRAY(m_pwfx);
m_bOpened = false;
m_bPlaying = false;
}
bool EMusic::_readMMIO()
{
MMCKINFO ckIn;
PCMWAVEFORMAT pcmWaveFormat;
memset(&ckIn, 0, sizeof(ckIn));
m_pwfx = nullptr;
if ((0 != mmioDescend(m_hmmio, &m_ckRiff, nullptr, 0)))
return TraceError(L"mmioDescend");
// 确认文件是一个合法的 wave 文件
if ((m_ckRiff.ckid != FOURCC_RIFF) ||
(m_ckRiff.fccType != mmioFOURCC('W', 'A', 'V', 'E')))
return TraceError(L"mmioFOURCC");
// 在输入文件中查找 'fmt' 块
ckIn.ckid = mmioFOURCC('f', 'm', 't', ' ');
if (0 != mmioDescend(m_hmmio, &ckIn, &m_ckRiff, MMIO_FINDCHUNK))
return TraceError(L"mmioDescend");
// 'fmt' 块至少应和 PCMWAVEFORMAT 一样大
if (ckIn.cksize < (LONG)sizeof(PCMWAVEFORMAT))
return TraceError(L"sizeof(PCMWAVEFORMAT)");
// 将 'fmt' 块读取到 pcmWaveFormat 中
if (mmioRead(m_hmmio, (HPSTR)&pcmWaveFormat,
sizeof(pcmWaveFormat)) != sizeof(pcmWaveFormat))
return TraceError(L"mmioRead");
// 分配 WAVEFORMATEX但如果它不是 PCM 格式,再读取一个 WORD 大小
// 的数据,这个数据就是额外分配的大小
if (pcmWaveFormat.wf.wFormatTag == WAVE_FORMAT_PCM)
{
m_pwfx = (WAVEFORMATEX*)new CHAR[sizeof(WAVEFORMATEX)];
// 拷贝数据
memcpy(m_pwfx, &pcmWaveFormat, sizeof(pcmWaveFormat));
m_pwfx->cbSize = 0;
}
else
{
// 读取额外数据的大小
WORD cbExtraBytes = 0L;
if (mmioRead(m_hmmio, (CHAR*)&cbExtraBytes, sizeof(WORD)) != sizeof(WORD))
return TraceError(L"mmioRead");
m_pwfx = (WAVEFORMATEX*)new CHAR[sizeof(WAVEFORMATEX) + cbExtraBytes];
// 拷贝数据
memcpy(m_pwfx, &pcmWaveFormat, sizeof(pcmWaveFormat));
m_pwfx->cbSize = cbExtraBytes;
// 读取额外数据
if (mmioRead(m_hmmio, (CHAR*)(((BYTE*)&(m_pwfx->cbSize)) + sizeof(WORD)),
cbExtraBytes) != cbExtraBytes)
{
SAFE_DELETE(m_pwfx);
return TraceError(L"mmioRead");
}
}
if (0 != mmioAscend(m_hmmio, &ckIn, 0))
{
SAFE_DELETE(m_pwfx);
return TraceError(L"mmioAscend");
}
return true;
}
bool EMusic::_resetFile()
{
// Seek to the data
if (-1 == mmioSeek(m_hmmio, m_ckRiff.dwDataOffset + sizeof(FOURCC),
SEEK_SET))
return TraceError(L"mmioSeek");
// Search the input file for the 'data' chunk.
m_ck.ckid = mmioFOURCC('d', 'a', 't', 'a');
if (0 != mmioDescend(m_hmmio, &m_ck, &m_ckRiff, MMIO_FINDCHUNK))
return TraceError(L"mmioDescend");
return true;
}
bool EMusic::_read(BYTE* pBuffer, DWORD dwSizeToRead)
{
MMIOINFO mmioinfoIn; // current status of m_hmmio
if (0 != mmioGetInfo(m_hmmio, &mmioinfoIn, 0))
return TraceError(L"mmioGetInfo");
UINT cbDataIn = dwSizeToRead;
if (cbDataIn > m_ck.cksize)
cbDataIn = m_ck.cksize;
m_ck.cksize -= cbDataIn;
for (DWORD cT = 0; cT < cbDataIn; cT++)
{
// Copy the bytes from the io to the buffer.
if (mmioinfoIn.pchNext == mmioinfoIn.pchEndRead)
{
if (0 != mmioAdvance(m_hmmio, &mmioinfoIn, MMIO_READ))
return TraceError(L"mmioAdvance");
if (mmioinfoIn.pchNext == mmioinfoIn.pchEndRead)
return TraceError(L"mmioinfoIn.pchNext");
}
// Actual copy.
*((BYTE*)pBuffer + cT) = *((BYTE*)mmioinfoIn.pchNext);
mmioinfoIn.pchNext++;
}
if (0 != mmioSetInfo(m_hmmio, &mmioinfoIn, 0))
return TraceError(L"mmioSetInfo");
return true;
}
bool EMusic::_findMediaFileCch(WCHAR* strDestPath, int cchDest, LPCWSTR strFilename)
{
bool bFound = false;
if (nullptr == strFilename || strFilename[0] == 0 || nullptr == strDestPath || cchDest < 10)
return false;
// Get the exe name, and exe path
WCHAR strExePath[MAX_PATH] = { 0 };
WCHAR strExeName[MAX_PATH] = { 0 };
WCHAR* strLastSlash = nullptr;
GetModuleFileName(HINST_THISCOMPONENT, strExePath, MAX_PATH);
strExePath[MAX_PATH - 1] = 0;
strLastSlash = wcsrchr(strExePath, TEXT('\\'));
if (strLastSlash)
{
wcscpy_s(strExeName, MAX_PATH, &strLastSlash[1]);
// Chop the exe name from the exe path
*strLastSlash = 0;
// Chop the .exe from the exe name
strLastSlash = wcsrchr(strExeName, TEXT('.'));
if (strLastSlash)
*strLastSlash = 0;
}
wcscpy_s(strDestPath, cchDest, strFilename);
if (GetFileAttributes(strDestPath) != 0xFFFFFFFF)
return true;
// Search all parent directories starting at .\ and using strFilename as the leaf name
WCHAR strLeafName[MAX_PATH] = { 0 };
wcscpy_s(strLeafName, MAX_PATH, strFilename);
WCHAR strFullPath[MAX_PATH] = { 0 };
WCHAR strFullFileName[MAX_PATH] = { 0 };
WCHAR strSearch[MAX_PATH] = { 0 };
WCHAR* strFilePart = nullptr;
GetFullPathName(L".", MAX_PATH, strFullPath, &strFilePart);
if (strFilePart == nullptr)
return false;
while (strFilePart != nullptr && *strFilePart != '\0')
{
swprintf_s(strFullFileName, MAX_PATH, L"%s\\%s", strFullPath, strLeafName);
if (GetFileAttributes(strFullFileName) != 0xFFFFFFFF)
{
wcscpy_s(strDestPath, cchDest, strFullFileName);
bFound = true;
break;
}
swprintf_s(strFullFileName, MAX_PATH, L"%s\\%s\\%s", strFullPath, strExeName, strLeafName);
if (GetFileAttributes(strFullFileName) != 0xFFFFFFFF)
{
wcscpy_s(strDestPath, cchDest, strFullFileName);
bFound = true;
break;
}
swprintf_s(strSearch, MAX_PATH, L"%s\\..", strFullPath);
GetFullPathName(strSearch, MAX_PATH, strFullPath, &strFilePart);
}
if (bFound)
return true;
// 失败时,将文件作为路径返回,同时也返回错误代码
wcscpy_s(strDestPath, cchDest, strFilename);
return false;
}
bool e2d::EMusic::stop(const EString & musicName)
{
return stop(musicName.hash());;
}
void e2d::EMusic::pauseAllMusics()
{
for (auto iter = getMciPlayerList().begin(); iter != getMciPlayerList().end(); iter++)
{
(*iter).second->pause();
}
}
void e2d::EMusic::resumeAllMusics()
{
for (auto iter = getMciPlayerList().begin(); iter != getMciPlayerList().end(); iter++)
{
(*iter).second->resume();
}
}
void e2d::EMusic::stopAllMusics()
{
for (auto iter = getMciPlayerList().begin(); iter != getMciPlayerList().end(); iter++)
{
(*iter).second->stop();
}
}

View File

@ -124,7 +124,7 @@ public:
private:
// 初始化计时操作
static void __init();
static bool __init();
// 重置计时操作
static void __uninit();
@ -208,7 +208,7 @@ public:
private:
// 初始化 DirectInput 以及键盘鼠标设备
static HRESULT __init();
static bool __init();
// 获得输入信息
static void __updateDeviceState();

View File

@ -145,7 +145,7 @@ public:
bool operator > (EString const&) const;
bool operator >= (EString const&) const;
operator const wchar_t*() const { return _string; }
operator wchar_t*() const { return _string; }
operator bool() const { return _size != 0; }
friend EString operator+(const wchar_t, const EString &);

View File

@ -35,14 +35,19 @@
// C RunTime Header Files
#include <stdio.h>
#include <wchar.h>
#include <wincodec.h>
#include <mmsystem.h>
#include <d2d1.h>
#include <d2d1helper.h>
#include <dwrite.h>
#include <dinput.h>
#include <wincodec.h>
#include <xaudio2.h>
#include <d2d1helper.h>
#pragma comment(lib, "d2d1.lib")
#pragma comment(lib, "dwrite.lib")
#pragma comment(lib, "xaudio2.lib")
#pragma comment(lib, "windowscodecs.lib")
#pragma comment(lib, "winmm.lib")
#ifndef HINST_THISCOMPONENT
@ -53,7 +58,7 @@ EXTERN_C IMAGE_DOS_HEADER __ImageBase;
#ifndef ASSERT_IF
#if defined( DEBUG ) || defined( _DEBUG )
#define ASSERT(b, m) do {if (!(b)) { fprintf(stderr, "Assert: " #m "\n"); assert(b); }} while(0)
#define ASSERT(b, m, ...) do {if (!(b)) { fwprintf(stderr, L"Assert: " #m L"\n", __VA_ARGS__); assert(b); }} while(0)
#else
#define ASSERT(b, m) ((void)0)
#endif //DEBUG || _DEBUG
@ -61,7 +66,7 @@ EXTERN_C IMAGE_DOS_HEADER __ImageBase;
#ifndef WARN_IF
#if defined( DEBUG ) || defined( _DEBUG )
#define WARN_IF(b, m) do {if (b) { fprintf(stderr, "Warning: " #m "\n"); }} while(0)
#define WARN_IF(b, m, ...) do {if (b) { fwprintf(stderr, L"Warning: " #m L"\n", __VA_ARGS__); }} while(0)
#else
#define WARN_IF(b, m) ((void)0)
#endif //DEBUG || _DEBUG

View File

@ -12,6 +12,7 @@ class EScene;
class ENode;
class ETimer;
class EAction;
class EMusic;
class EGeometry;
class ETransition;
class EListenerPhysics;
@ -68,8 +69,11 @@ private:
// äÖȾ³¡¾°»­Ãæ
static void __render();
// 进入下一场景
static void __enterNextScene();
// 初始化场景
static bool __init();
// 回收场景资源
static void __uninit();
};
@ -199,6 +203,46 @@ private:
};
// 音乐管理工具
class EMusicManager
{
friend EGame;
public:
// 添加音乐文件
static EMusic * add(
const EString & strFilePath /* 音乐文件路径 */
);
// 获取指定音乐的 EMusic 对象
static EMusic * get(
const EString & strFilePath /* 音乐文件路径 */
);
// 暂停所有音乐
static void pauseAllMusics();
// 继续播放所有音乐
static void resumeAllMusics();
// 停止所有音乐
static void stopAllMusics();
// 获取 IXAudio2 对象
static IXAudio2 * getIXAudio2();
// 获取 IXAudio2MasteringVoice 对象
static IXAudio2MasteringVoice * getIXAudio2MasteringVoice();
private:
// 初始化 XAudio2
static bool __init();
// 回收相关资源
static void __uninit();
};
class EPhysicsManager
{
friend EGame;

View File

@ -6,7 +6,7 @@ namespace e2d
{
class ETimerManager;
class EAction;
class EMusicManager;
// 随机数产生器
class ERandom
@ -215,74 +215,88 @@ public:
};
// 稜있밗잿묏야
// 音乐播放器
class EMusic
: public EObject
{
friend EMusicManager;
public:
// 꺄렴稜있
static UINT play(
const EString & musicFilePath, /* 稜있匡숭쨌쓺 */
int repeatTimes = 1
// 播放
bool play(
int nLoopCount = 0 /* 重复播放次数 */
);
// 꺄렴稜있
static UINT play(
const EString & musicResourceName, /* 栗都츰냔 */
const EString & musicResourceType, /* 栗都잚깎 */
const EString & musicExtension, /* 寧땍栗都돨윈嵐츰 */
int repeatTimes = 1
// 暂停
bool pause();
// 继续
bool resume();
// 停止
bool stop();
// 获取音乐播放状态
bool isPlaying();
// 获取音量
float getVolume() const;
// 设置音量
bool setVolume(
float fVolume /* 音量范围为 -224 ~ 224其中 0 是静音1 是正常音量 */
);
// 董界稜있
static bool pause(
UINT musicId
// 获取频率比
float getFrequencyRatio() const;
// 设置频率比
bool setFrequencyRatio(
float fFrequencyRatio /* 频率比范围为 1/1024.0f ~ 1024.0f,其中 1.0 为正常声调 */
);
// 董界稜있
static bool pause(
const EString& musicName
// 获取 IXAudio2SourceVoice 对象
IXAudio2SourceVoice* getIXAudio2SourceVoice() const;
protected:
EMusic();
virtual ~EMusic();
// 打开音乐文件
bool _open(
LPWSTR strFileName
);
// 셨崎꺄렴稜있
static bool resume(
UINT musicId
// 关闭该播放器
void _close();
bool _readMMIO();
bool _resetFile();
bool _read(
BYTE* pBuffer,
DWORD dwSizeToRead
);
// 셨崎꺄렴稜있
static bool resume(
const EString& musicName
bool _findMediaFileCch(
WCHAR* strDestPath,
int cchDest,
LPCWSTR strFilename
);
// 界岺稜있
static bool stop(
UINT musicId
);
// 界岺稜있
static bool stop(
const EString& musicName
);
// 渡속潼稜있
static UINT preload(
const EString & musicFilePath
);
// 渡속潼稜있
static UINT preload(
const EString & musicResourceName, /* 栗都츰냔 */
const EString & musicResourceType, /* 栗都잚깎 */
const EString & musicExtension /* 寧땍栗都돨윈嵐츰 */
);
// 董界杰唐稜있
static void pauseAllMusics();
// 셨崎꺄렴杰唐稜있
static void resumeAllMusics();
// 界岺杰唐稜있
static void stopAllMusics();
protected:
bool m_bOpened;
bool m_bPlaying;
DWORD m_dwSize;
CHAR* m_pResourceBuffer;
BYTE* m_pbWaveData;
HMMIO m_hmmio;
MMCKINFO m_ck;
MMCKINFO m_ckRiff;
WAVEFORMATEX* m_pwfx;
IXAudio2SourceVoice* m_pSourceVoice;
};
}

View File

@ -101,7 +101,7 @@
<Optimization>Disabled</Optimization>
<PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<SDLCheck>true</SDLCheck>
<DebugInformationFormat>None</DebugInformationFormat>
<DebugInformationFormat>EditAndContinue</DebugInformationFormat>
<MinimalRebuild>false</MinimalRebuild>
</ClCompile>
<Link>
@ -227,6 +227,7 @@
<ClCompile Include="..\..\core\Listener\Listener.cpp" />
<ClCompile Include="..\..\core\Listener\ListenerPhysics.cpp" />
<ClCompile Include="..\..\core\Manager\ActionManager.cpp" />
<ClCompile Include="..\..\core\Manager\MusicManager.cpp" />
<ClCompile Include="..\..\core\Manager\ObjectManager.cpp" />
<ClCompile Include="..\..\core\Manager\PhysicsManager.cpp" />
<ClCompile Include="..\..\core\Manager\SceneManager.cpp" />

View File

@ -201,6 +201,9 @@
<ClCompile Include="..\..\core\Tool\File.cpp">
<Filter>Tool</Filter>
</ClCompile>
<ClCompile Include="..\..\core\Manager\MusicManager.cpp">
<Filter>Manager</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="..\..\core\etools.h" />