From 342b8e050a60227ae9e9d945c6af370e2be4adc5 Mon Sep 17 00:00:00 2001 From: Nomango <569629550@qq.com> Date: Thu, 1 Feb 2018 22:07:44 +0800 Subject: [PATCH] New feature: XAudio2. --- core/Base/Game.cpp | 104 ++-- core/Base/Input.cpp | 8 +- core/Base/Time.cpp | 24 +- core/Base/Window.cpp | 4 +- core/Common/Image.cpp | 2 - core/Manager/MusicManager.cpp | 127 ++++ core/Manager/SceneManager.cpp | 57 +- core/Tool/Music.cpp | 814 +++++++++++++------------- core/ebase.h | 4 +- core/ecommon.h | 2 +- core/emacros.h | 13 +- core/emanagers.h | 48 +- core/etools.h | 122 ++-- project/vs2017/Easy2D.vcxproj | 3 +- project/vs2017/Easy2D.vcxproj.filters | 3 + 15 files changed, 803 insertions(+), 532 deletions(-) create mode 100644 core/Manager/MusicManager.cpp diff --git a/core/Base/Game.cpp b/core/Base/Game.cpp index 83c8587a..a78bf583 100644 --- a/core/Base/Game.cpp +++ b/core/Base/Game.cpp @@ -20,36 +20,66 @@ bool e2d::EGame::init(LPCTSTR sTitle, UINT32 nWidth, UINT32 nHeight, LPCTSTR pIc return false; } - // 初始化 COM 组件 - if (SUCCEEDED(CoInitialize(NULL))) + do { - // 创建设备无关资源 - if (ERenderer::__createDeviceIndependentResources()) + // 初始化 COM 组件 + if (FAILED(CoInitializeEx(NULL, COINIT_MULTITHREADED))) { - // 初始化窗口 - if (EWindow::__init(sTitle, nWidth, nHeight, pIconID)) - { - // 创建设备相关资源 - if (ERenderer::__createDeviceResources()) - { - // 重设 Client 大小 - EWindow::setSize(nWidth, nHeight); - // 设置 AppName - if (sAppname) - { - s_sAppName = sAppname; - } - else - { - s_sAppName = EWindow::getTitle(); - } - // 标志初始化成功 - s_bInitialized = true; - } - } + WARN_IF(true, "CoInitializeEx Failed!"); + break; } - } - + + // 创建设备无关资源 + if (!ERenderer::__createDeviceIndependentResources()) + { + WARN_IF(true, "ERenderer::__createDeviceIndependentResources Failed!"); + break; + } + + // 初始化窗口 + if (!EWindow::__init(sTitle, nWidth, nHeight, pIconID)) + { + WARN_IF(true, "EWindow::__init Failed!"); + break; + } + + // 创建设备相关资源 + 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 + 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() diff --git a/core/Base/Input.cpp b/core/Base/Input.cpp index dd68bbd7..9dd3d71c 100644 --- a/core/Base/Input.cpp +++ b/core/Base/Input.cpp @@ -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)) { diff --git a/core/Base/Time.cpp b/core/Base/Time.cpp index 351932ec..267f5363 100644 --- a/core/Base/Time.cpp +++ b/core/Base/Time.cpp @@ -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); // 刷新当前时间 - s_tStart = s_tLast = 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() diff --git a/core/Base/Window.cpp b/core/Base/Window.cpp index d4f6d32d..6a5cd01e 100644 --- a/core/Base/Window.cpp +++ b/core/Base/Window.cpp @@ -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); diff --git a/core/Common/Image.cpp b/core/Common/Image.cpp index d46bb6c3..51bb2d4d 100644 --- a/core/Common/Image.cpp +++ b/core/Common/Image.cpp @@ -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); } diff --git a/core/Manager/MusicManager.cpp b/core/Manager/MusicManager.cpp new file mode 100644 index 00000000..c5251bf7 --- /dev/null +++ b/core/Manager/MusicManager.cpp @@ -0,0 +1,127 @@ +#include "..\emanagers.h" +#include "..\etools.h" +#include + +static IXAudio2 * s_pXAudio2 = nullptr; +static IXAudio2MasteringVoice * s_pMasteringVoice = nullptr; + +typedef std::pair MusicPair; +typedef std::map 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); +} \ No newline at end of file diff --git a/core/Manager/SceneManager.cpp b/core/Manager/SceneManager.cpp index 2795e91f..2e1e2969 100644 --- a/core/Manager/SceneManager.cpp +++ b/core/Manager/SceneManager.cpp @@ -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(); } diff --git a/core/Tool/Music.cpp b/core/Tool/Music.cpp index 88e87fa1..64e9a18a 100644 --- a/core/Tool/Music.cpp +++ b/core/Tool/Music.cpp @@ -1,443 +1,469 @@ #include "..\etools.h" -#include -#include -#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(); + WARN_IF(true, "EMusic error: %s failed!", sPrompt); + return false; +} - 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; -}; +inline bool TraceError(LPCTSTR sPrompt, HRESULT hr) +{ + WARN_IF(true, "EMusic error: %s (%#X)", sPrompt, hr); + return false; +} -MciPlayer::MciPlayer() - : m_wnd(NULL) - , m_dev(0L) - , m_nMusicID(0) +EMusic::EMusic() + : m_bOpened(false) , m_bPlaying(false) - , m_nRepeatTimes(0) + , m_pwfx(nullptr) + , m_hmmio(nullptr) + , m_pResourceBuffer(nullptr) + , m_pbWaveData(nullptr) + , m_dwSize(0) + , m_pSourceVoice(nullptr) { - 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() +EMusic::~EMusic() { - close(); - DestroyWindow(m_wnd); + _close(); } -bool MciPlayer::open(const e2d::EString & pFileName, UINT uId) +bool EMusic::_open(LPWSTR strFileName) { - if (pFileName.isEmpty()) + if (m_bOpened) + { + WARN_IF(true, L"EMusic can be opened only once!"); return false; - - close(); - - MCI_OPEN_PARMS mciOpen = { 0 }; - mciOpen.lpstrDeviceType = 0; - mciOpen.lpstrElementName = pFileName; - - MCIERROR mciError; - mciError = mciSendCommand( - 0, - MCI_OPEN, - MCI_OPEN_ELEMENT, - reinterpret_cast(&mciOpen) - ); - - if (mciError == 0) - { - m_dev = mciOpen.wDeviceID; - m_nMusicID = uId; - m_bPlaying = false; - return true; } - 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; } -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(m_wnd); - - // 播放声音 - MCIERROR mciError = mciSendCommand( - m_dev, - MCI_PLAY, - MCI_FROM | MCI_NOTIFY, - reinterpret_cast(&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(m_wnd); - - MCIERROR mciError = mciSendCommand( - m_dev, - MCI_PLAY, - MCI_NOTIFY, - reinterpret_cast(&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); + if (SUCCEEDED(hr = m_pSourceVoice->Start(0))) + { + m_bPlaying = true; + } + + return SUCCEEDED(hr); +} - // 写入文件 - DWORD dwWrite = 0; // 返回写入字节 - ::WriteFile(hFile, hMem, dwSize, &dwWrite, NULL); - ::CloseHandle(hFile); +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 + { + return false; + } +} + +float e2d::EMusic::getVolume() const +{ + float fVolume = 0.0f; + if (m_pSourceVoice) + { + m_pSourceVoice->GetVolume(&fVolume); + } + return fVolume; +} + +bool e2d::EMusic::setVolume(float fVolume) +{ + if (m_pSourceVoice) + { + return SUCCEEDED(m_pSourceVoice->SetVolume(min(max(fVolume, -224), 224))); + } + return false; +} + +float e2d::EMusic::getFrequencyRatio() const +{ + float fFrequencyRatio = 0.0f; + if (m_pSourceVoice) + { + m_pSourceVoice->GetFrequencyRatio(&fFrequencyRatio); + } + return fFrequencyRatio; +} + +bool e2d::EMusic::setFrequencyRatio(float fFrequencyRatio) +{ + if (m_pSourceVoice) + { + fFrequencyRatio = min(max(fFrequencyRatio, XAUDIO2_MIN_FREQ_RATIO), XAUDIO2_MAX_FREQ_RATIO); + return SUCCEEDED(m_pSourceVoice->SetFrequencyRatio(fFrequencyRatio)); + } + return false; +} + +IXAudio2SourceVoice * e2d::EMusic::getIXAudio2SourceVoice() const +{ + return m_pSourceVoice; +} + +void EMusic::_close() +{ + if (m_pSourceVoice) + { + 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; } -LRESULT WINAPI _MciPlayerProc(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam) +bool EMusic::_resetFile() { - MciPlayer * pPlayer = NULL; + // Seek to the data + if (-1 == mmioSeek(m_hmmio, m_ckRiff.dwDataOffset + sizeof(FOURCC), + SEEK_SET)) + return TraceError(L"mmioSeek"); - if (Msg == MM_MCINOTIFY - && wParam == MCI_NOTIFY_SUCCESSFUL - && (pPlayer = (MciPlayer *)GetWindowLongPtr(hWnd, GWLP_USERDATA))) + // 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++) { - if (pPlayer->m_nRepeatTimes > 0) + // Copy the bytes from the io to the buffer. + if (mmioinfoIn.pchNext == mmioinfoIn.pchEndRead) { - pPlayer->m_nRepeatTimes--; + if (0 != mmioAdvance(m_hmmio, &mmioinfoIn, MMIO_READ)) + return TraceError(L"mmioAdvance"); + + if (mmioinfoIn.pchNext == mmioinfoIn.pchEndRead) + return TraceError(L"mmioinfoIn.pchNext"); } - if (pPlayer->m_nRepeatTimes) + // 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) { - mciSendCommand(static_cast(lParam), MCI_SEEK, MCI_SEEK_TO_START, 0); - - MCI_PLAY_PARMS mciPlay = { 0 }; - mciPlay.dwCallback = reinterpret_cast(hWnd); - mciSendCommand(static_cast(lParam), MCI_PLAY, MCI_NOTIFY, reinterpret_cast(&mciPlay)); + wcscpy_s(strDestPath, cchDest, strFullFileName); + bFound = true; + break; } - else + + swprintf_s(strFullFileName, MAX_PATH, L"%s\\%s\\%s", strFullPath, strExeName, strLeafName); + if (GetFileAttributes(strFullFileName) != 0xFFFFFFFF) { - pPlayer->m_bPlaying = false; - return 0; + wcscpy_s(strDestPath, cchDest, strFullFileName); + bFound = true; + break; } + + swprintf_s(strSearch, MAX_PATH, L"%s\\..", strFullPath); + GetFullPathName(strSearch, MAX_PATH, strFullPath, &strFilePart); } - return DefWindowProc(hWnd, Msg, wParam, lParam); -} - - - - -typedef std::pair MusicPair; -typedef std::map MusicList; - -static MusicList& getMciPlayerList() -{ - static MusicList s_List; - return s_List; -} - - -UINT e2d::EMusic::play(const EString & musicFilePath, int repeatTimes) -{ - UINT nRet = preload(musicFilePath); - - if (nRet) - { - getMciPlayerList()[nRet]->play(repeatTimes); - } - return nRet; -} - -UINT e2d::EMusic::play(const EString & musicResourceName, const EString & musicResourceType, const EString & musicExtension, int repeatTimes) -{ - UINT nRet = preload(musicResourceName, musicResourceType, musicExtension); - - if (nRet) - { - 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(); + if (bFound) return true; - } + + // 失败时,将文件作为路径返回,同时也返回错误代码 + wcscpy_s(strDestPath, cchDest, strFilename); + return false; } - -bool e2d::EMusic::resume(const EString & musicName) -{ - return resume(musicName.hash());; -} - -bool e2d::EMusic::pause(UINT musicId) -{ - MusicList::iterator p = getMciPlayerList().find(musicId); - if (p != getMciPlayerList().end()) - { - p->second->pause(); - return true; - } - return false; -} - -bool e2d::EMusic::pause(const EString & musicName) -{ - return pause(musicName.hash()); -} - -bool e2d::EMusic::stop(UINT musicId) -{ - MusicList::iterator p = getMciPlayerList().find(musicId); - if (p != getMciPlayerList().end()) - { - p->second->stop(); - return true; - } - 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(); - } -} diff --git a/core/ebase.h b/core/ebase.h index ff3098ff..602a83d9 100644 --- a/core/ebase.h +++ b/core/ebase.h @@ -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(); diff --git a/core/ecommon.h b/core/ecommon.h index 39844f6e..fa8666fe 100644 --- a/core/ecommon.h +++ b/core/ecommon.h @@ -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 &); diff --git a/core/emacros.h b/core/emacros.h index 2dc81e93..82a8a711 100644 --- a/core/emacros.h +++ b/core/emacros.h @@ -35,14 +35,19 @@ // C RunTime Header Files #include #include +#include +#include #include -#include #include #include -#include +#include +#include + #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 diff --git a/core/emanagers.h b/core/emanagers.h index f42cbef1..eb4416b3 100644 --- a/core/emanagers.h +++ b/core/emanagers.h @@ -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; diff --git a/core/etools.h b/core/etools.h index 43d3ffda..db3f7127 100644 --- a/core/etools.h +++ b/core/etools.h @@ -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; }; } \ No newline at end of file diff --git a/project/vs2017/Easy2D.vcxproj b/project/vs2017/Easy2D.vcxproj index 7b5a6eb2..f74c1383 100644 --- a/project/vs2017/Easy2D.vcxproj +++ b/project/vs2017/Easy2D.vcxproj @@ -101,7 +101,7 @@ Disabled WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) true - None + EditAndContinue false @@ -227,6 +227,7 @@ + diff --git a/project/vs2017/Easy2D.vcxproj.filters b/project/vs2017/Easy2D.vcxproj.filters index aa812845..837392cc 100644 --- a/project/vs2017/Easy2D.vcxproj.filters +++ b/project/vs2017/Easy2D.vcxproj.filters @@ -201,6 +201,9 @@ Tool + + Manager +