Magic_Game/core/Tool/Music.cpp

475 lines
9.9 KiB
C++
Raw Normal View History

#include "..\etools.h"
2018-02-01 22:07:44 +08:00
#include "..\emanagers.h"
2018-02-01 22:07:44 +08:00
using namespace e2d;
2018-02-01 22:07:44 +08:00
#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
2018-02-01 22:07:44 +08:00
inline bool TraceError(LPCTSTR sPrompt)
{
WARN_IF(true, "Music error: %s failed!", sPrompt);
2018-02-01 22:07:44 +08:00
return false;
}
2018-02-01 22:07:44 +08:00
inline bool TraceError(LPCTSTR sPrompt, HRESULT hr)
2018-01-30 16:45:38 +08:00
{
WARN_IF(true, "Music error: %s (%#X)", sPrompt, hr);
2018-02-01 22:07:44 +08:00
return false;
}
Music::Music()
2018-02-01 22:07:44 +08:00
: m_bOpened(false)
, m_bPlaying(false)
2018-02-01 22:07:44 +08:00
, m_pwfx(nullptr)
, m_hmmio(nullptr)
, m_pResourceBuffer(nullptr)
, m_pbWaveData(nullptr)
, m_dwSize(0)
, m_pSourceVoice(nullptr)
{
}
Music::~Music()
{
2018-02-01 22:07:44 +08:00
_close();
}
2018-02-27 16:32:17 +08:00
bool Music::_open(LPCWSTR strFileName)
{
2018-02-01 22:07:44 +08:00
if (m_bOpened)
{
WARN_IF(true, L"Music can be opened only once!");
return false;
2018-02-01 22:07:44 +08:00
}
2018-02-01 22:07:44 +08:00
if (strFileName == nullptr)
{
WARN_IF(true, L"Music::_open Invalid file name.");
2018-02-01 22:07:44 +08:00
return false;
}
IXAudio2 * pXAudio2 = MusicManager::getIXAudio2();
2018-02-01 22:07:44 +08:00
if (!pXAudio2)
{
WARN_IF(true, L"IXAudio2 nullptr pointer error!");
return false;
}
2018-02-01 22:07:44 +08:00
// <20><>λ wave <20>ļ<EFBFBD>
wchar_t strFilePath[MAX_PATH];
if (!_findMediaFileCch(strFilePath, MAX_PATH, strFileName))
{
WARN_IF(true, L"Failed to find media file: %s", strFileName);
return false;
}
2018-02-01 22:07:44 +08:00
m_hmmio = mmioOpen(strFilePath, nullptr, MMIO_ALLOCBUF | MMIO_READ);
2018-02-01 22:07:44 +08:00
if (nullptr == m_hmmio)
{
2018-02-01 22:07:44 +08:00
return TraceError(L"mmioOpen");
}
2018-02-01 22:07:44 +08:00
if (!_readMMIO())
{
2018-02-01 22:07:44 +08:00
// <20><>ȡ<EFBFBD><C8A1> wave <20>ļ<EFBFBD>ʱ ReadMMIO <20><><EFBFBD><EFBFBD>ʧ<EFBFBD><CAA7>
mmioClose(m_hmmio, 0);
return TraceError(L"_readMMIO");
}
2018-02-01 22:07:44 +08:00
if (!_resetFile())
return TraceError(L"_resetFile");
2018-02-01 22:07:44 +08:00
// <20><><EFBFBD><EFBFBD><EFBFBD>ļ<EFBFBD><C4BC><EFBFBD><EFBFBD><EFBFBD>wave <20>ļ<EFBFBD><C4BC>Ĵ<EFBFBD>С<EFBFBD><D0A1> m_ck.cksize
m_dwSize = m_ck.cksize;
2018-02-01 22:07:44 +08:00
// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ݶ<EFBFBD>ȡ<EFBFBD><C8A1><EFBFBD>ڴ<EFBFBD><DAB4><EFBFBD>
m_pbWaveData = new BYTE[m_dwSize];
if (!_read(m_pbWaveData, m_dwSize))
{
2018-02-01 22:07:44 +08:00
TraceError(L"Failed to read WAV data");
SAFE_DELETE_ARRAY(m_pbWaveData);
return false;
}
2018-02-01 22:07:44 +08:00
// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Դ
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 Music::play(int nLoopCount)
{
2018-02-01 22:07:44 +08:00
HRESULT hr;
if (m_bPlaying)
{
stop();
}
2018-02-06 21:11:54 +08:00
nLoopCount = min(nLoopCount, XAUDIO2_LOOP_INFINITE - 1);
nLoopCount = (nLoopCount < 0) ? XAUDIO2_LOOP_INFINITE : nLoopCount;
2018-02-01 22:07:44 +08:00
// <20>ύ wave <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
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)))
{
2018-02-01 22:07:44 +08:00
TraceError(L"Error %#X submitting source buffer", hr);
m_pSourceVoice->DestroyVoice();
SAFE_DELETE_ARRAY(m_pbWaveData);
return false;
}
2018-02-01 22:07:44 +08:00
if (SUCCEEDED(hr = m_pSourceVoice->Start(0)))
{
m_bPlaying = true;
}
return SUCCEEDED(hr);
}
bool Music::pause()
{
2018-02-01 22:07:44 +08:00
if (m_pSourceVoice)
{
if (SUCCEEDED(m_pSourceVoice->Stop()))
{
m_bPlaying = false;
return true;
}
}
return false;
}
bool Music::resume()
{
2018-02-01 22:07:44 +08:00
if (m_pSourceVoice)
{
if (SUCCEEDED(m_pSourceVoice->Start()))
{
m_bPlaying = true;
return true;
}
}
return false;
}
bool Music::stop()
{
2018-02-01 22:07:44 +08:00
if (m_pSourceVoice)
{
if (SUCCEEDED(m_pSourceVoice->Stop()))
{
m_pSourceVoice->ExitLoop();
m_pSourceVoice->FlushSourceBuffers();
m_bPlaying = false;
return true;
}
}
return false;
}
bool Music::isPlaying()
{
2018-02-01 22:07:44 +08:00
if (m_pSourceVoice)
{
2018-02-01 22:07:44 +08:00
XAUDIO2_VOICE_STATE state;
m_pSourceVoice->GetState(&state);
2018-02-01 22:07:44 +08:00
if (state.BuffersQueued == 0)
{
m_bPlaying = false;
}
return m_bPlaying;
}
else
{
return false;
}
}
float Music::getVolume() const
{
2018-02-01 22:07:44 +08:00
float fVolume = 0.0f;
if (m_pSourceVoice)
{
m_pSourceVoice->GetVolume(&fVolume);
}
return fVolume;
}
bool Music::setVolume(float fVolume)
{
2018-02-01 22:07:44 +08:00
if (m_pSourceVoice)
{
return SUCCEEDED(m_pSourceVoice->SetVolume(min(max(fVolume, -224), 224)));
}
return false;
}
float Music::getFrequencyRatio() const
{
2018-02-01 22:07:44 +08:00
float fFrequencyRatio = 0.0f;
if (m_pSourceVoice)
{
2018-02-01 22:07:44 +08:00
m_pSourceVoice->GetFrequencyRatio(&fFrequencyRatio);
}
2018-02-01 22:07:44 +08:00
return fFrequencyRatio;
}
bool Music::setFrequencyRatio(float fFrequencyRatio)
{
2018-02-01 22:07:44 +08:00
if (m_pSourceVoice)
{
fFrequencyRatio = min(max(fFrequencyRatio, XAUDIO2_MIN_FREQ_RATIO), XAUDIO2_MAX_FREQ_RATIO);
return SUCCEEDED(m_pSourceVoice->SetFrequencyRatio(fFrequencyRatio));
}
return false;
}
IXAudio2SourceVoice * Music::getIXAudio2SourceVoice() const
2018-02-01 22:07:44 +08:00
{
return m_pSourceVoice;
}
void Music::_close()
{
2018-02-01 22:07:44 +08:00
if (m_pSourceVoice)
{
2018-02-04 21:24:27 +08:00
m_pSourceVoice->Stop();
m_pSourceVoice->FlushSourceBuffers();
2018-02-01 22:07:44 +08:00
m_pSourceVoice->DestroyVoice();
m_pSourceVoice = nullptr;
}
2018-02-01 22:07:44 +08:00
if (m_hmmio != nullptr)
{
2018-02-01 22:07:44 +08:00
mmioClose(m_hmmio, 0);
m_hmmio = nullptr;
}
2018-02-01 22:07:44 +08:00
SAFE_DELETE_ARRAY(m_pResourceBuffer);
SAFE_DELETE_ARRAY(m_pbWaveData);
SAFE_DELETE_ARRAY(m_pwfx);
2018-02-01 22:07:44 +08:00
m_bOpened = false;
m_bPlaying = false;
2018-01-30 16:45:38 +08:00
}
bool Music::_readMMIO()
2018-02-01 22:07:44 +08:00
{
MMCKINFO ckIn;
PCMWAVEFORMAT pcmWaveFormat;
2018-01-30 16:45:38 +08:00
2018-02-01 22:07:44 +08:00
memset(&ckIn, 0, sizeof(ckIn));
2018-01-30 16:45:38 +08:00
2018-02-01 22:07:44 +08:00
m_pwfx = nullptr;
2018-01-30 16:45:38 +08:00
2018-02-01 22:07:44 +08:00
if ((0 != mmioDescend(m_hmmio, &m_ckRiff, nullptr, 0)))
return TraceError(L"mmioDescend");
2018-01-30 16:45:38 +08:00
2018-02-01 22:07:44 +08:00
// ȷ<><C8B7><EFBFBD>ļ<EFBFBD><C4BC><EFBFBD>һ<EFBFBD><D2BB><EFBFBD>Ϸ<EFBFBD><CFB7><EFBFBD> wave <20>ļ<EFBFBD>
if ((m_ckRiff.ckid != FOURCC_RIFF) ||
(m_ckRiff.fccType != mmioFOURCC('W', 'A', 'V', 'E')))
return TraceError(L"mmioFOURCC");
2018-01-30 16:45:38 +08:00
2018-02-01 22:07:44 +08:00
// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ļ<EFBFBD><C4BC>в<EFBFBD><D0B2><EFBFBD> 'fmt' <20><>
ckIn.ckid = mmioFOURCC('f', 'm', 't', ' ');
if (0 != mmioDescend(m_hmmio, &ckIn, &m_ckRiff, MMIO_FINDCHUNK))
return TraceError(L"mmioDescend");
2018-01-30 16:45:38 +08:00
2018-02-01 22:07:44 +08:00
// '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(m_hmmio, (HPSTR)&pcmWaveFormat,
sizeof(pcmWaveFormat)) != sizeof(pcmWaveFormat))
return TraceError(L"mmioRead");
2018-01-30 16:45:38 +08:00
2018-02-01 22:07:44 +08:00
// <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)
2018-01-30 16:45:38 +08:00
{
2018-02-01 22:07:44 +08:00
m_pwfx = (WAVEFORMATEX*)new CHAR[sizeof(WAVEFORMATEX)];
// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
memcpy(m_pwfx, &pcmWaveFormat, sizeof(pcmWaveFormat));
m_pwfx->cbSize = 0;
2018-01-30 16:45:38 +08:00
}
2018-02-01 22:07:44 +08:00
else
{
// <20><>ȡ<EFBFBD><C8A1><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ݵĴ<DDB5>С
WORD cbExtraBytes = 0L;
if (mmioRead(m_hmmio, (CHAR*)&cbExtraBytes, sizeof(WORD)) != sizeof(WORD))
return TraceError(L"mmioRead");
2018-01-30 16:45:38 +08:00
2018-02-01 22:07:44 +08:00
m_pwfx = (WAVEFORMATEX*)new CHAR[sizeof(WAVEFORMATEX) + cbExtraBytes];
// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
memcpy(m_pwfx, &pcmWaveFormat, sizeof(pcmWaveFormat));
m_pwfx->cbSize = cbExtraBytes;
2018-01-30 16:45:38 +08:00
2018-02-01 22:07:44 +08:00
// <20><>ȡ<EFBFBD><C8A1><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
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))
2018-01-30 16:45:38 +08:00
{
2018-02-01 22:07:44 +08:00
SAFE_DELETE(m_pwfx);
return TraceError(L"mmioAscend");
2018-01-30 16:45:38 +08:00
}
2018-02-01 22:07:44 +08:00
return true;
2018-01-30 16:45:38 +08:00
}
bool Music::_resetFile()
2018-01-30 16:45:38 +08:00
{
2018-02-01 22:07:44 +08:00
// Seek to the data
if (-1 == mmioSeek(m_hmmio, m_ckRiff.dwDataOffset + sizeof(FOURCC),
SEEK_SET))
return TraceError(L"mmioSeek");
2018-01-30 16:45:38 +08:00
2018-02-01 22:07:44 +08:00
// 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");
2018-01-30 16:45:38 +08:00
2018-02-01 22:07:44 +08:00
return true;
2018-01-30 16:45:38 +08:00
}
bool Music::_read(BYTE* pBuffer, DWORD dwSizeToRead)
2018-01-30 16:45:38 +08:00
{
2018-02-01 22:07:44 +08:00
MMIOINFO mmioinfoIn; // current status of m_hmmio
2018-01-30 16:45:38 +08:00
2018-02-01 22:07:44 +08:00
if (0 != mmioGetInfo(m_hmmio, &mmioinfoIn, 0))
return TraceError(L"mmioGetInfo");
2018-01-30 16:45:38 +08:00
2018-02-01 22:07:44 +08:00
UINT cbDataIn = dwSizeToRead;
if (cbDataIn > m_ck.cksize)
cbDataIn = m_ck.cksize;
2018-01-30 16:45:38 +08:00
2018-02-01 22:07:44 +08:00
m_ck.cksize -= cbDataIn;
2018-01-30 16:45:38 +08:00
2018-02-01 22:07:44 +08:00
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");
2018-01-30 16:45:38 +08:00
2018-02-01 22:07:44 +08:00
if (mmioinfoIn.pchNext == mmioinfoIn.pchEndRead)
return TraceError(L"mmioinfoIn.pchNext");
}
2018-01-30 16:45:38 +08:00
2018-02-01 22:07:44 +08:00
// Actual copy.
*((BYTE*)pBuffer + cT) = *((BYTE*)mmioinfoIn.pchNext);
mmioinfoIn.pchNext++;
2018-01-30 16:45:38 +08:00
}
2018-02-01 22:07:44 +08:00
if (0 != mmioSetInfo(m_hmmio, &mmioinfoIn, 0))
return TraceError(L"mmioSetInfo");
2018-01-30 16:45:38 +08:00
2018-02-01 22:07:44 +08:00
return true;
2018-01-30 16:45:38 +08:00
}
bool Music::_findMediaFileCch(WCHAR* strDestPath, int cchDest, LPCWSTR strFilename)
2018-01-30 16:45:38 +08:00
{
2018-02-01 22:07:44 +08:00
bool bFound = false;
2018-01-30 16:45:38 +08:00
2018-02-01 22:07:44 +08:00
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)
2018-01-30 16:45:38 +08:00
{
2018-02-01 22:07:44 +08:00
wcscpy_s(strExeName, MAX_PATH, &strLastSlash[1]);
2018-01-30 16:45:38 +08:00
2018-02-01 22:07:44 +08:00
// Chop the exe name from the exe path
*strLastSlash = 0;
2018-01-30 16:45:38 +08:00
2018-02-01 22:07:44 +08:00
// Chop the .exe from the exe name
strLastSlash = wcsrchr(strExeName, TEXT('.'));
if (strLastSlash)
*strLastSlash = 0;
2018-01-30 16:45:38 +08:00
}
2018-02-01 22:07:44 +08:00
wcscpy_s(strDestPath, cchDest, strFilename);
if (GetFileAttributes(strDestPath) != 0xFFFFFFFF)
return true;
2018-01-30 16:45:38 +08:00
2018-02-01 22:07:44 +08:00
// 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')
2018-01-30 16:45:38 +08:00
{
2018-02-01 22:07:44 +08:00
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);
2018-01-30 16:45:38 +08:00
}
2018-02-01 22:07:44 +08:00
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;
2018-01-30 16:45:38 +08:00
}