Magic_Game/core/Tool/Music.cpp

596 lines
12 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#include "..\e2dtool.h"
#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
inline bool TraceError(wchar_t* sPrompt)
{
WARN("MusicInfo error: %s failed!", sPrompt);
return false;
}
inline bool TraceError(wchar_t* sPrompt, HRESULT hr)
{
WARN("MusicInfo error: %s (%#X)", sPrompt, hr);
return false;
}
e2d::Music::XAudio2Tool::XAudio2Tool()
{
::CoInitialize(nullptr);
ThrowIfFailed(
XAudio2Create(&_xAudio2, 0)
);
ThrowIfFailed(
_xAudio2->CreateMasteringVoice(&_masteringVoice)
);
}
e2d::Music::XAudio2Tool::~XAudio2Tool()
{
_masteringVoice->DestroyVoice();
_xAudio2->Release();
::CoUninitialize();
}
e2d::Music::XAudio2Tool* e2d::Music::XAudio2Tool::getInstance()
{
static XAudio2Tool instance;
return &instance;
}
IXAudio2 * e2d::Music::XAudio2Tool::getXAudio2()
{
return _xAudio2;
}
IXAudio2MasteringVoice * e2d::Music::XAudio2Tool::getMasteringVoice()
{
return _masteringVoice;
}
e2d::Music::Music()
: _opened(false)
, _wfx(nullptr)
, _hmmio(nullptr)
, _resBuffer(nullptr)
, _waveData(nullptr)
, _dwSize(0)
, _voice(nullptr)
, _voiceCallback()
{
auto xAudio2 = XAudio2Tool::getInstance()->getXAudio2();
xAudio2->AddRef();
}
e2d::Music::Music(const e2d::String & filePath)
: _opened(false)
, _wfx(nullptr)
, _hmmio(nullptr)
, _resBuffer(nullptr)
, _waveData(nullptr)
, _dwSize(0)
, _voice(nullptr)
, _voiceCallback()
{
auto xAudio2 = XAudio2Tool::getInstance()->getXAudio2();
xAudio2->AddRef();
this->open(filePath);
}
e2d::Music::Music(const Resource& res)
: _opened(false)
, _wfx(nullptr)
, _hmmio(nullptr)
, _resBuffer(nullptr)
, _waveData(nullptr)
, _dwSize(0)
, _voice(nullptr)
, _voiceCallback()
{
auto xAudio2 = XAudio2Tool::getInstance()->getXAudio2();
xAudio2->AddRef();
this->open(res);
}
e2d::Music::~Music()
{
close();
auto xAudio2 = XAudio2Tool::getInstance()->getXAudio2();
xAudio2->Release();
}
bool e2d::Music::open(const e2d::String & filePath)
{
if (_opened)
{
close();
}
if (filePath.isEmpty())
{
WARN("MusicInfo::open Invalid file name.");
return false;
}
String actualFilePath = File(filePath).getPath();
if (actualFilePath.isEmpty())
{
WARN("MusicInfo::open File not found.");
return false;
}
// <20><>λ wave <20>ļ<EFBFBD>
wchar_t pFilePath[MAX_PATH];
if (!_findMediaFileCch(pFilePath, MAX_PATH, (const wchar_t *)actualFilePath))
{
WARN("Failed to find media file: %s", pFilePath);
return false;
}
_hmmio = mmioOpen(pFilePath, nullptr, MMIO_ALLOCBUF | MMIO_READ);
if (nullptr == _hmmio)
{
return TraceError(L"mmioOpen");
}
if (!_readMMIO())
{
// <20><>ȡ<EFBFBD><C8A1> wave <20>ļ<EFBFBD>ʱ ReadMMIO <20><><EFBFBD><EFBFBD>ʧ<EFBFBD><CAA7>
mmioClose(_hmmio, 0);
return TraceError(L"ReadMMIO");
}
if (!_resetFile())
return TraceError(L"ResetFile");
// <20><><EFBFBD><EFBFBD><EFBFBD>ļ<EFBFBD><C4BC><EFBFBD><EFBFBD><EFBFBD>wave <20>ļ<EFBFBD><C4BC>Ĵ<EFBFBD>С<EFBFBD><D0A1> _ck.cksize
_dwSize = _ck.cksize;
// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ݶ<EFBFBD>ȡ<EFBFBD><C8A1><EFBFBD>ڴ<EFBFBD><DAB4><EFBFBD>
_waveData = new BYTE[_dwSize];
if (!_read(_waveData, _dwSize))
{
TraceError(L"Failed to read WAV data");
SAFE_DELETE_ARRAY(_waveData);
return false;
}
// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Դ
auto xAudio2 = XAudio2Tool::getInstance()->getXAudio2();
HRESULT hr = xAudio2->CreateSourceVoice(&_voice, _wfx, 0, XAUDIO2_DEFAULT_FREQ_RATIO, &_voiceCallback);
if (FAILED(hr))
{
TraceError(L"Create source voice error", hr);
SAFE_DELETE_ARRAY(_waveData);
return false;
}
_opened = true;
return true;
}
bool e2d::Music::open(const Resource& res)
{
if (_opened)
{
close();
}
HRSRC hResInfo;
HGLOBAL hResData;
DWORD dwSize;
void* pvRes;
if (nullptr == (hResInfo = FindResourceW(HINST_THISCOMPONENT, MAKEINTRESOURCE(res.resNameId), (LPCWSTR)res.resType)))
return TraceError(L"FindResource");
if (nullptr == (hResData = LoadResource(HINST_THISCOMPONENT, hResInfo)))
return TraceError(L"LoadResource");
if (0 == (dwSize = SizeofResource(HINST_THISCOMPONENT, hResInfo)))
return TraceError(L"SizeofResource");
if (nullptr == (pvRes = LockResource(hResData)))
return TraceError(L"LockResource");
_resBuffer = new CHAR[dwSize];
memcpy(_resBuffer, pvRes, dwSize);
MMIOINFO mmioInfo;
ZeroMemory(&mmioInfo, sizeof(mmioInfo));
mmioInfo.fccIOProc = FOURCC_MEM;
mmioInfo.cchBuffer = dwSize;
mmioInfo.pchBuffer = (CHAR*)_resBuffer;
_hmmio = mmioOpen(nullptr, &mmioInfo, MMIO_ALLOCBUF | MMIO_READ);
if (nullptr == _hmmio)
{
return TraceError(L"mmioOpen");
}
if (!_readMMIO())
{
// <20><>ȡ<EFBFBD><C8A1> wave <20>ļ<EFBFBD>ʱ ReadMMIO <20><><EFBFBD><EFBFBD>ʧ<EFBFBD><CAA7>
mmioClose(_hmmio, 0);
return TraceError(L"ReadMMIO");
}
if (!_resetFile())
return TraceError(L"ResetFile");
// <20><><EFBFBD><EFBFBD><EFBFBD>ļ<EFBFBD><C4BC><EFBFBD><EFBFBD><EFBFBD>wave <20>ļ<EFBFBD><C4BC>Ĵ<EFBFBD>С<EFBFBD><D0A1> _ck.cksize
_dwSize = _ck.cksize;
// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ݶ<EFBFBD>ȡ<EFBFBD><C8A1><EFBFBD>ڴ<EFBFBD><DAB4><EFBFBD>
_waveData = new BYTE[_dwSize];
if (!_read(_waveData, _dwSize))
{
TraceError(L"Failed to read WAV data");
SAFE_DELETE_ARRAY(_waveData);
return false;
}
// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Դ
auto xAudio2 = XAudio2Tool::getInstance()->getXAudio2();
HRESULT hr = xAudio2->CreateSourceVoice(&_voice, _wfx, 0, XAUDIO2_DEFAULT_FREQ_RATIO, &_voiceCallback);
if (FAILED(hr))
{
TraceError(L"Create source voice error", hr);
SAFE_DELETE_ARRAY(_waveData);
return false;
}
_opened = true;
return true;
}
bool e2d::Music::play(int nLoopCount)
{
if (!_opened)
{
WARN("Music::play Failed: Music must be opened first!");
return false;
}
if (_voice == nullptr)
{
WARN("Music::play Failed: IXAudio2SourceVoice Null pointer exception!");
return false;
}
XAUDIO2_VOICE_STATE state;
_voice->GetState(&state);
if (state.BuffersQueued)
{
stop();
}
nLoopCount = std::min(nLoopCount, XAUDIO2_LOOP_INFINITE - 1);
nLoopCount = (nLoopCount < 0) ? XAUDIO2_LOOP_INFINITE : nLoopCount;
// <20>ύ wave <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
XAUDIO2_BUFFER buffer = { 0 };
buffer.pAudioData = _waveData;
buffer.Flags = XAUDIO2_END_OF_STREAM;
buffer.AudioBytes = _dwSize;
buffer.LoopCount = nLoopCount;
HRESULT hr;
if (FAILED(hr = _voice->SubmitSourceBuffer(&buffer)))
{
TraceError(L"Submitting source buffer error", hr);
_voice->DestroyVoice();
SAFE_DELETE_ARRAY(_waveData);
return false;
}
hr = _voice->Start(0);
return SUCCEEDED(hr);
}
void e2d::Music::pause()
{
if (_voice)
{
_voice->Stop();
}
}
void e2d::Music::resume()
{
if (_voice)
{
_voice->Start();
}
}
void e2d::Music::stop()
{
if (_voice)
{
if (SUCCEEDED(_voice->Stop()))
{
_voice->ExitLoop();
_voice->FlushSourceBuffers();
}
}
}
void e2d::Music::close()
{
if (_voice)
{
_voice->Stop();
_voice->FlushSourceBuffers();
_voice->DestroyVoice();
_voice = nullptr;
}
if (_hmmio != nullptr)
{
mmioClose(_hmmio, 0);
_hmmio = nullptr;
}
SAFE_DELETE_ARRAY(_resBuffer);
SAFE_DELETE_ARRAY(_waveData);
SAFE_DELETE_ARRAY(_wfx);
_opened = false;
}
bool e2d::Music::isPlaying() const
{
if (_opened && _voice)
{
XAUDIO2_VOICE_STATE state;
_voice->GetState(&state);
if (state.BuffersQueued)
return true;
}
return false;
}
IXAudio2SourceVoice * e2d::Music::getIXAudio2SourceVoice() const
{
return _voice;
}
bool e2d::Music::setVolume(float volume)
{
if (_voice)
{
return SUCCEEDED(_voice->SetVolume(volume));
}
return false;
}
void e2d::Music::setFuncOnEnd(const Function & func)
{
_voiceCallback.SetFuncOnStreamEnd(func);
}
void e2d::Music::setFuncOnLoopEnd(const Function & func)
{
_voiceCallback.SetFuncOnLoopEnd(func);
}
bool e2d::Music::_readMMIO()
{
MMCKINFO ckIn;
PCMWAVEFORMAT pcmWaveFormat;
memset(&ckIn, 0, sizeof(ckIn));
_wfx = nullptr;
if ((0 != mmioDescend(_hmmio, &_ckRiff, nullptr, 0)))
return TraceError(L"mmioDescend");
// ȷ<><C8B7><EFBFBD>ļ<EFBFBD><C4BC><EFBFBD>һ<EFBFBD><D2BB><EFBFBD>Ϸ<EFBFBD><CFB7><EFBFBD> wave <20>ļ<EFBFBD>
if ((_ckRiff.ckid != FOURCC_RIFF) ||
(_ckRiff.fccType != mmioFOURCC('W', 'A', 'V', 'E')))
return TraceError(L"mmioFOURCC");
// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ļ<EFBFBD><C4BC>в<EFBFBD><D0B2><EFBFBD> 'fmt' <20><>
ckIn.ckid = mmioFOURCC('f', 'm', 't', ' ');
if (0 != mmioDescend(_hmmio, &ckIn, &_ckRiff, MMIO_FINDCHUNK))
return TraceError(L"mmioDescend");
// 'fmt' <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ӧ<EFBFBD><D3A6> PCMWAVEFORMAT һ<><D2BB><EFBFBD><EFBFBD>
if (ckIn.cksize < (LONG)sizeof(PCMWAVEFORMAT))
return TraceError(L"sizeof(PCMWAVEFORMAT)");
// <20><> 'fmt' <20><><EFBFBD><EFBFBD>ȡ<EFBFBD><C8A1> pcmWaveFormat <20><>
if (mmioRead(_hmmio, (HPSTR)&pcmWaveFormat,
sizeof(pcmWaveFormat)) != sizeof(pcmWaveFormat))
return TraceError(L"mmioRead");
// <20><><EFBFBD><EFBFBD> WAVEFORMATEX<45><58><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> PCM <20><>ʽ<EFBFBD><CABD><EFBFBD>ٶ<EFBFBD>ȡһ<C8A1><D2BB> WORD <20><>С
// <20><><EFBFBD><EFBFBD><EFBFBD>ݣ<EFBFBD><DDA3><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ݾ<EFBFBD><DDBE>Ƕ<EFBFBD><C7B6><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ĵ<EFBFBD>С
if (pcmWaveFormat.wf.wFormatTag == WAVE_FORMAT_PCM)
{
_wfx = (WAVEFORMATEX*) new CHAR[sizeof(WAVEFORMATEX)];
// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
memcpy(_wfx, &pcmWaveFormat, sizeof(pcmWaveFormat));
_wfx->cbSize = 0;
}
else
{
// <20><>ȡ<EFBFBD><C8A1><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ݵĴ<DDB5>С
WORD cbExtraBytes = 0L;
if (mmioRead(_hmmio, (CHAR*)&cbExtraBytes, sizeof(WORD)) != sizeof(WORD))
return TraceError(L"mmioRead");
_wfx = (WAVEFORMATEX*) new CHAR[sizeof(WAVEFORMATEX) + cbExtraBytes];
// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
memcpy(_wfx, &pcmWaveFormat, sizeof(pcmWaveFormat));
_wfx->cbSize = cbExtraBytes;
// <20><>ȡ<EFBFBD><C8A1><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
if (mmioRead(_hmmio, (CHAR*)(((BYTE*)&(_wfx->cbSize)) + sizeof(WORD)),
cbExtraBytes) != cbExtraBytes)
{
SAFE_DELETE(_wfx);
return TraceError(L"mmioRead");
}
}
if (0 != mmioAscend(_hmmio, &ckIn, 0))
{
SAFE_DELETE(_wfx);
return TraceError(L"mmioAscend");
}
return true;
}
bool e2d::Music::_resetFile()
{
// Seek to the data
if (-1 == mmioSeek(_hmmio, _ckRiff.dwDataOffset + sizeof(FOURCC),
SEEK_SET))
return TraceError(L"mmioSeek");
// Search the input file for the 'data' chunk.
_ck.ckid = mmioFOURCC('d', 'a', 't', 'a');
if (0 != mmioDescend(_hmmio, &_ck, &_ckRiff, MMIO_FINDCHUNK))
return TraceError(L"mmioDescend");
return true;
}
bool e2d::Music::_read(BYTE* pBuffer, DWORD dwSizeToRead)
{
MMIOINFO mmioinfoIn; // current status of _hmmio
if (0 != mmioGetInfo(_hmmio, &mmioinfoIn, 0))
return TraceError(L"mmioGetInfo");
UINT cbDataIn = dwSizeToRead;
if (cbDataIn > _ck.cksize)
cbDataIn = _ck.cksize;
_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(_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(_hmmio, &mmioinfoIn, 0))
return TraceError(L"mmioSetInfo");
return true;
}
bool e2d::Music::_findMediaFileCch(wchar_t* strDestPath, int cchDest, const wchar_t * strFilename)
{
bool bFound = false;
if (nullptr == strFilename || nullptr == strDestPath || cchDest < 10)
return false;
// Get the exe _name, and exe path
wchar_t strExePath[MAX_PATH] = { 0 };
wchar_t strExeName[MAX_PATH] = { 0 };
GetModuleFileName(HINST_THISCOMPONENT, strExePath, MAX_PATH);
strExePath[MAX_PATH - 1] = 0;
wchar_t* 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_t strLeafName[MAX_PATH] = { 0 };
wcscpy_s(strLeafName, MAX_PATH, strFilename);
wchar_t strFullPath[MAX_PATH] = { 0 };
wchar_t strFullFileName[MAX_PATH] = { 0 };
wchar_t strSearch[MAX_PATH] = { 0 };
wchar_t* 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;
// ʧ<><CAA7>ʱ<EFBFBD><CAB1><EFBFBD><EFBFBD><EFBFBD>ļ<EFBFBD><C4BC><EFBFBD>Ϊ·<CEAA><C2B7><EFBFBD><EFBFBD><EFBFBD>أ<EFBFBD>ͬʱҲ<CAB1><D2B2><EFBFBD>ش<EFBFBD><D8B4><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
wcscpy_s(strDestPath, cchDest, strFilename);
return false;
}