843 lines
		
	
	
		
			17 KiB
		
	
	
	
		
			C++
		
	
	
	
			
		
		
	
	
			843 lines
		
	
	
		
			17 KiB
		
	
	
	
		
			C++
		
	
	
	
| #include "..\e2dtool.h"
 | |
| #include "..\e2dmanager.h"
 | |
| #include <map>
 | |
| 
 | |
| 
 | |
| #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_IF(true, "MusicInfo error: %s failed!", sPrompt);
 | |
| 	return false;
 | |
| }
 | |
| 
 | |
| inline bool TraceError(wchar_t* sPrompt, HRESULT hr)
 | |
| {
 | |
| 	WARN_IF(true, "MusicInfo error: %s (%#X)", sPrompt, hr);
 | |
| 	return false;
 | |
| }
 | |
| 
 | |
| static IXAudio2 * s_pXAudio2 = nullptr;
 | |
| static IXAudio2MasteringVoice * s_pMasteringVoice = nullptr;
 | |
| static float s_fMusicVolume = 1.0;
 | |
| 
 | |
| 
 | |
| // ÒôÀÖ²¥·ÅÆ÷
 | |
| class MusicPlayer
 | |
| {
 | |
| public:
 | |
| 	MusicPlayer();
 | |
| 
 | |
| 	virtual ~MusicPlayer();
 | |
| 
 | |
| 	bool open(
 | |
| 		const e2d::String& filePath
 | |
| 	);
 | |
| 
 | |
| 	bool MusicPlayer::open(
 | |
| 		int resNameId, 
 | |
| 		const e2d::String& resType
 | |
| 	);
 | |
| 
 | |
| 	bool play(
 | |
| 		int nLoopCount = 0
 | |
| 	);
 | |
| 
 | |
| 	void pause();
 | |
| 
 | |
| 	void resume();
 | |
| 
 | |
| 	void stop();
 | |
| 
 | |
| 	void close();
 | |
| 
 | |
| 	bool setVolume(
 | |
| 		float fVolume
 | |
| 	);
 | |
| 
 | |
| 	bool isPlaying() const;
 | |
| 
 | |
| protected:
 | |
| 	bool _readMMIO();
 | |
| 
 | |
| 	bool _resetFile();
 | |
| 
 | |
| 	bool _read(
 | |
| 		BYTE* pBuffer,
 | |
| 		DWORD dwSizeToRead
 | |
| 	);
 | |
| 
 | |
| 	bool _findMediaFileCch(
 | |
| 		wchar_t* strDestPath,
 | |
| 		int cchDest,
 | |
| 		const wchar_t * strFilename
 | |
| 	);
 | |
| 
 | |
| protected:
 | |
| 	bool _bOpened;
 | |
| 	mutable bool _bPlaying;
 | |
| 	DWORD _dwSize;
 | |
| 	CHAR* _pResourceBuffer;
 | |
| 	BYTE* _pbWaveData;
 | |
| 	HMMIO _hmmio;
 | |
| 	MMCKINFO _ck;
 | |
| 	MMCKINFO _ckRiff;
 | |
| 	WAVEFORMATEX* _pwfx;
 | |
| 	IXAudio2SourceVoice* _pSourceVoice;
 | |
| };
 | |
| 
 | |
| typedef std::map<UINT, MusicPlayer *> MusicMap;
 | |
| 
 | |
| static MusicMap& GetMusicFileList()
 | |
| {
 | |
| 	static MusicMap s_MusicFileList;
 | |
| 	return s_MusicFileList;
 | |
| }
 | |
| 
 | |
| static MusicMap& GetMusicResList()
 | |
| {
 | |
| 	static MusicMap s_MusicResList;
 | |
| 	return s_MusicResList;
 | |
| }
 | |
| 
 | |
| MusicPlayer::MusicPlayer()
 | |
| 	: _bOpened(false)
 | |
| 	, _bPlaying(false)
 | |
| 	, _pwfx(nullptr)
 | |
| 	, _hmmio(nullptr)
 | |
| 	, _pResourceBuffer(nullptr)
 | |
| 	, _pbWaveData(nullptr)
 | |
| 	, _dwSize(0)
 | |
| 	, _pSourceVoice(nullptr)
 | |
| {
 | |
| }
 | |
| 
 | |
| MusicPlayer::~MusicPlayer()
 | |
| {
 | |
| 	close();
 | |
| }
 | |
| 
 | |
| bool MusicPlayer::open(const e2d::String& filePath)
 | |
| {
 | |
| 	if (_bOpened)
 | |
| 	{
 | |
| 		WARN_IF(true, "MusicInfo can be opened only once!");
 | |
| 		return false;
 | |
| 	}
 | |
| 
 | |
| 	if (filePath.isEmpty())
 | |
| 	{
 | |
| 		WARN_IF(true, "MusicInfo::open Invalid file name.");
 | |
| 		return false;
 | |
| 	}
 | |
| 
 | |
| 	if (!s_pXAudio2)
 | |
| 	{
 | |
| 		WARN_IF(true, "IXAudio2 nullptr pointer error!");
 | |
| 		return false;
 | |
| 	}
 | |
| 
 | |
| 	// ¶¨Î» wave Îļþ
 | |
| 	wchar_t pFilePath[MAX_PATH];
 | |
| 	if (!_findMediaFileCch(pFilePath, MAX_PATH, filePath))
 | |
| 	{
 | |
| 		WARN_IF(true, "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())
 | |
| 	{
 | |
| 		// ¶ÁÈ¡·Ç wave Îļþʱ ReadMMIO µ÷ÓÃʧ°Ü
 | |
| 		mmioClose(_hmmio, 0);
 | |
| 		return TraceError(L"_readMMIO");
 | |
| 	}
 | |
| 
 | |
| 	if (!_resetFile())
 | |
| 		return TraceError(L"_resetFile");
 | |
| 
 | |
| 	// ÖØÖÃÎļþºó£¬wave ÎļþµÄ´óСÊÇ _ck.cksize
 | |
| 	_dwSize = _ck.cksize;
 | |
| 
 | |
| 	// ½«Ñù±¾Êý¾Ý¶ÁÈ¡µ½ÄÚ´æÖÐ
 | |
| 	_pbWaveData = new BYTE[_dwSize];
 | |
| 
 | |
| 	if (!_read(_pbWaveData, _dwSize))
 | |
| 	{
 | |
| 		TraceError(L"Failed to read WAV data");
 | |
| 		SAFE_DELETE_ARRAY(_pbWaveData);
 | |
| 		return false;
 | |
| 	}
 | |
| 
 | |
| 	// ´´½¨ÒôÔ´
 | |
| 	HRESULT hr;
 | |
| 	if (FAILED(hr = s_pXAudio2->CreateSourceVoice(&_pSourceVoice, _pwfx)))
 | |
| 	{
 | |
| 		TraceError(L"Create source voice error", hr);
 | |
| 		SAFE_DELETE_ARRAY(_pbWaveData);
 | |
| 		return false;
 | |
| 	}
 | |
| 
 | |
| 	_bOpened = true;
 | |
| 	_bPlaying = false;
 | |
| 	return true;
 | |
| }
 | |
| 
 | |
| bool MusicPlayer::open(int resNameId, const e2d::String& resType)
 | |
| {
 | |
| 	HRSRC hResInfo;
 | |
| 	HGLOBAL hResData;
 | |
| 	DWORD dwSize;
 | |
| 	void* pvRes;
 | |
| 
 | |
| 	if (_bOpened)
 | |
| 	{
 | |
| 		WARN_IF(true, "MusicInfo can be opened only once!");
 | |
| 		return false;
 | |
| 	}
 | |
| 
 | |
| 	if (!s_pXAudio2)
 | |
| 	{
 | |
| 		WARN_IF(true, "IXAudio2 nullptr pointer error!");
 | |
| 		return false;
 | |
| 	}
 | |
| 
 | |
| 	// Loading it as a file failed, so try it as a resource
 | |
| 	if (nullptr == (hResInfo = FindResourceW(HINST_THISCOMPONENT, MAKEINTRESOURCE(resNameId), 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");
 | |
| 
 | |
| 	_pResourceBuffer = new CHAR[dwSize];
 | |
| 	memcpy(_pResourceBuffer, pvRes, dwSize);
 | |
| 
 | |
| 	MMIOINFO mmioInfo;
 | |
| 	ZeroMemory(&mmioInfo, sizeof(mmioInfo));
 | |
| 	mmioInfo.fccIOProc = FOURCC_MEM;
 | |
| 	mmioInfo.cchBuffer = dwSize;
 | |
| 	mmioInfo.pchBuffer = (CHAR*)_pResourceBuffer;
 | |
| 
 | |
| 	_hmmio = mmioOpen(nullptr, &mmioInfo, MMIO_ALLOCBUF | MMIO_READ);
 | |
| 
 | |
| 	if (!_readMMIO())
 | |
| 	{
 | |
| 		// ReadMMIO will fail if its an not a wave file
 | |
| 		mmioClose(_hmmio, 0);
 | |
| 		return TraceError(L"ReadMMIO");
 | |
| 	}
 | |
| 
 | |
| 	if (!_resetFile())
 | |
| 		return TraceError(L"ResetFile");
 | |
| 
 | |
| 	// After the reset, the size of the wav file is _ck.cksize so store it now
 | |
| 	_dwSize = _ck.cksize;
 | |
| 
 | |
| 	// Read the sample data into memory
 | |
| 	_pbWaveData = new BYTE[_dwSize];
 | |
| 
 | |
| 	if (!_read(_pbWaveData, _dwSize))
 | |
| 	{
 | |
| 		TraceError(L"Failed to read WAV data");
 | |
| 		SAFE_DELETE_ARRAY(_pbWaveData);
 | |
| 		return false;
 | |
| 	}
 | |
| 
 | |
| 	// ´´½¨ÒôÔ´
 | |
| 	HRESULT hr;
 | |
| 	if (FAILED(hr = s_pXAudio2->CreateSourceVoice(&_pSourceVoice, _pwfx)))
 | |
| 	{
 | |
| 		TraceError(L"Create source voice error", hr);
 | |
| 		SAFE_DELETE_ARRAY(_pbWaveData);
 | |
| 		return false;
 | |
| 	}
 | |
| 
 | |
| 	_bOpened = true;
 | |
| 	_bPlaying = false;
 | |
| 	return true;
 | |
| }
 | |
| 
 | |
| bool MusicPlayer::play(int nLoopCount)
 | |
| {
 | |
| 	if (!_bOpened)
 | |
| 	{
 | |
| 		WARN_IF(true, "MusicInfo::play Failed: MusicInfo must be opened first!");
 | |
| 		return false;
 | |
| 	}
 | |
| 
 | |
| 	if (_pSourceVoice == nullptr)
 | |
| 	{
 | |
| 		WARN_IF(true, "MusicInfo::play Failed: IXAudio2SourceVoice Null pointer exception!");
 | |
| 		return false;
 | |
| 	}
 | |
| 
 | |
| 	if (_bPlaying)
 | |
| 	{
 | |
| 		stop();
 | |
| 	}
 | |
| 
 | |
| 	nLoopCount = min(nLoopCount, XAUDIO2_LOOP_INFINITE - 1);
 | |
| 	nLoopCount = (nLoopCount < 0) ? XAUDIO2_LOOP_INFINITE : nLoopCount;
 | |
| 
 | |
| 	// Ìá½» wave Ñù±¾Êý¾Ý
 | |
| 	XAUDIO2_BUFFER buffer = { 0 };
 | |
| 	buffer.pAudioData = _pbWaveData;
 | |
| 	buffer.Flags = XAUDIO2_END_OF_STREAM;
 | |
| 	buffer.AudioBytes = _dwSize;
 | |
| 	buffer.LoopCount = nLoopCount;
 | |
| 
 | |
| 	HRESULT hr;
 | |
| 	if (FAILED(hr = _pSourceVoice->SubmitSourceBuffer(&buffer)))
 | |
| 	{
 | |
| 		TraceError(L"Submitting source buffer error", hr);
 | |
| 		_pSourceVoice->DestroyVoice();
 | |
| 		SAFE_DELETE_ARRAY(_pbWaveData);
 | |
| 		return false;
 | |
| 	}
 | |
| 
 | |
| 	if (SUCCEEDED(hr = _pSourceVoice->Start(0)))
 | |
| 	{
 | |
| 		_bPlaying = true;
 | |
| 	}
 | |
| 	
 | |
| 	return SUCCEEDED(hr);
 | |
| }
 | |
| 
 | |
| void MusicPlayer::pause()
 | |
| {
 | |
| 	if (_pSourceVoice)
 | |
| 	{
 | |
| 		if (SUCCEEDED(_pSourceVoice->Stop()))
 | |
| 		{
 | |
| 			_bPlaying = false;
 | |
| 		}
 | |
| 	}
 | |
| }
 | |
| 
 | |
| void MusicPlayer::resume()
 | |
| {
 | |
| 	if (_pSourceVoice)
 | |
| 	{
 | |
| 		if (SUCCEEDED(_pSourceVoice->Start()))
 | |
| 		{
 | |
| 			_bPlaying = true;
 | |
| 		}
 | |
| 	}
 | |
| }
 | |
| 
 | |
| void MusicPlayer::stop()
 | |
| {
 | |
| 	if (_pSourceVoice)
 | |
| 	{
 | |
| 		if (SUCCEEDED(_pSourceVoice->Stop()))
 | |
| 		{
 | |
| 			_pSourceVoice->ExitLoop();
 | |
| 			_pSourceVoice->FlushSourceBuffers();
 | |
| 			_bPlaying = false;
 | |
| 		}
 | |
| 	}
 | |
| }
 | |
| 
 | |
| void MusicPlayer::close()
 | |
| {
 | |
| 	if (_pSourceVoice)
 | |
| 	{
 | |
| 		_pSourceVoice->Stop();
 | |
| 		_pSourceVoice->FlushSourceBuffers();
 | |
| 		_pSourceVoice->DestroyVoice();
 | |
| 		_pSourceVoice = nullptr;
 | |
| 	}
 | |
| 
 | |
| 	if (_hmmio != nullptr)
 | |
| 	{
 | |
| 		mmioClose(_hmmio, 0);
 | |
| 		_hmmio = nullptr;
 | |
| 	}
 | |
| 
 | |
| 	SAFE_DELETE_ARRAY(_pResourceBuffer);
 | |
| 	SAFE_DELETE_ARRAY(_pbWaveData);
 | |
| 	SAFE_DELETE_ARRAY(_pwfx);
 | |
| 
 | |
| 	_bOpened = false;
 | |
| 	_bPlaying = false;
 | |
| }
 | |
| 
 | |
| bool MusicPlayer::isPlaying() const
 | |
| {
 | |
| 	if (_bOpened && _pSourceVoice)
 | |
| 	{
 | |
| 		XAUDIO2_VOICE_STATE state;
 | |
| 		_pSourceVoice->GetState(&state);
 | |
| 
 | |
| 		if (state.BuffersQueued == 0)
 | |
| 		{
 | |
| 			_bPlaying = false;
 | |
| 		}
 | |
| 		return _bPlaying;
 | |
| 	}
 | |
| 	else
 | |
| 	{
 | |
| 		return false;
 | |
| 	}
 | |
| }
 | |
| 
 | |
| bool MusicPlayer::setVolume(float fVolume)
 | |
| {
 | |
| 	if (_pSourceVoice)
 | |
| 	{
 | |
| 		return SUCCEEDED(_pSourceVoice->SetVolume(fVolume));
 | |
| 	}
 | |
| 	return false;
 | |
| }
 | |
| 
 | |
| bool MusicPlayer::_readMMIO()
 | |
| {
 | |
| 	MMCKINFO ckIn;
 | |
| 	PCMWAVEFORMAT pcmWaveFormat;
 | |
| 
 | |
| 	memset(&ckIn, 0, sizeof(ckIn));
 | |
| 
 | |
| 	_pwfx = nullptr;
 | |
| 
 | |
| 	if ((0 != mmioDescend(_hmmio, &_ckRiff, nullptr, 0)))
 | |
| 		return TraceError(L"mmioDescend");
 | |
| 
 | |
| 	// È·ÈÏÎļþÊÇÒ»¸öºÏ·¨µÄ wave Îļþ
 | |
| 	if ((_ckRiff.ckid != FOURCC_RIFF) ||
 | |
| 		(_ckRiff.fccType != mmioFOURCC('W', 'A', 'V', 'E')))
 | |
| 		return TraceError(L"mmioFOURCC");
 | |
| 
 | |
| 	// ÔÚÊäÈëÎļþÖвéÕÒ 'fmt' ¿é
 | |
| 	ckIn.ckid = mmioFOURCC('f', 'm', 't', ' ');
 | |
| 	if (0 != mmioDescend(_hmmio, &ckIn, &_ckRiff, MMIO_FINDCHUNK))
 | |
| 		return TraceError(L"mmioDescend");
 | |
| 
 | |
| 	// 'fmt' ¿éÖÁÉÙÓ¦ºÍ PCMWAVEFORMAT Ò»Ñù´ó
 | |
| 	if (ckIn.cksize < (LONG)sizeof(PCMWAVEFORMAT))
 | |
| 		return TraceError(L"sizeof(PCMWAVEFORMAT)");
 | |
| 
 | |
| 	// ½« 'fmt' ¿é¶ÁÈ¡µ½ pcmWaveFormat ÖÐ
 | |
| 	if (mmioRead(_hmmio, (HPSTR)&pcmWaveFormat,
 | |
| 		sizeof(pcmWaveFormat)) != sizeof(pcmWaveFormat))
 | |
| 		return TraceError(L"mmioRead");
 | |
| 
 | |
| 	// ·ÖÅä WAVEFORMATEX£¬µ«Èç¹ûËü²»ÊÇ PCM ¸ñʽ£¬ÔÙ¶Áȡһ¸ö WORD ´óС
 | |
| 	// µÄÊý¾Ý£¬Õâ¸öÊý¾Ý¾ÍÊǶîÍâ·ÖÅäµÄ´óС
 | |
| 	if (pcmWaveFormat.wf.wFormatTag == WAVE_FORMAT_PCM)
 | |
| 	{
 | |
| 		_pwfx = (WAVEFORMATEX*)new CHAR[sizeof(WAVEFORMATEX)];
 | |
| 
 | |
| 		// ¿½±´Êý¾Ý
 | |
| 		memcpy(_pwfx, &pcmWaveFormat, sizeof(pcmWaveFormat));
 | |
| 		_pwfx->cbSize = 0;
 | |
| 	}
 | |
| 	else
 | |
| 	{
 | |
| 		// ¶ÁÈ¡¶îÍâÊý¾ÝµÄ´óС
 | |
| 		WORD cbExtraBytes = 0L;
 | |
| 		if (mmioRead(_hmmio, (CHAR*)&cbExtraBytes, sizeof(WORD)) != sizeof(WORD))
 | |
| 			return TraceError(L"mmioRead");
 | |
| 
 | |
| 		_pwfx = (WAVEFORMATEX*)new CHAR[sizeof(WAVEFORMATEX) + cbExtraBytes];
 | |
| 
 | |
| 		// ¿½±´Êý¾Ý
 | |
| 		memcpy(_pwfx, &pcmWaveFormat, sizeof(pcmWaveFormat));
 | |
| 		_pwfx->cbSize = cbExtraBytes;
 | |
| 
 | |
| 		// ¶ÁÈ¡¶îÍâÊý¾Ý
 | |
| 		if (mmioRead(_hmmio, (CHAR*)(((BYTE*)&(_pwfx->cbSize)) + sizeof(WORD)),
 | |
| 			cbExtraBytes) != cbExtraBytes)
 | |
| 		{
 | |
| 			SAFE_DELETE(_pwfx);
 | |
| 			return TraceError(L"mmioRead");
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	if (0 != mmioAscend(_hmmio, &ckIn, 0))
 | |
| 	{
 | |
| 		SAFE_DELETE(_pwfx);
 | |
| 		return TraceError(L"mmioAscend");
 | |
| 	}
 | |
| 
 | |
| 	return true;
 | |
| }
 | |
| 
 | |
| bool MusicPlayer::_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 MusicPlayer::_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 MusicPlayer::_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 };
 | |
| 	wchar_t* 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_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;
 | |
| 
 | |
| 	// ʧ°Üʱ£¬½«Îļþ×÷Ϊ·¾¶·µ»Ø£¬Í¬Ê±Ò²·µ»Ø´íÎó´úÂë
 | |
| 	wcscpy_s(strDestPath, cchDest, strFilename);
 | |
| 
 | |
| 	return false;
 | |
| }
 | |
| 
 | |
| 
 | |
| bool e2d::Music::preload(const String& filePath)
 | |
| {
 | |
| 	UINT nRet = filePath.getHashCode();
 | |
| 
 | |
| 	if (GetMusicFileList().end() != GetMusicFileList().find(nRet))
 | |
| 	{
 | |
| 		return true;
 | |
| 	}
 | |
| 	else
 | |
| 	{
 | |
| 		MusicPlayer * pPlayer = new (std::nothrow) MusicPlayer();
 | |
| 
 | |
| 		if (pPlayer->open(filePath))
 | |
| 		{
 | |
| 			pPlayer->setVolume(s_fMusicVolume);
 | |
| 			GetMusicFileList().insert(std::pair<UINT, MusicPlayer *>(nRet, pPlayer));
 | |
| 			return true;
 | |
| 		}
 | |
| 		else
 | |
| 		{
 | |
| 			delete pPlayer;
 | |
| 			pPlayer = nullptr;
 | |
| 		}
 | |
| 	}
 | |
| 	return false;
 | |
| }
 | |
| 
 | |
| bool e2d::Music::preload(int resNameId, const String& resType)
 | |
| {
 | |
| 	if (GetMusicResList().end() != GetMusicResList().find(resNameId))
 | |
| 	{
 | |
| 		return true;
 | |
| 	}
 | |
| 	else
 | |
| 	{
 | |
| 		MusicPlayer * pPlayer = new (std::nothrow) MusicPlayer();
 | |
| 
 | |
| 		if (pPlayer->open(resNameId, resType))
 | |
| 		{
 | |
| 			pPlayer->setVolume(s_fMusicVolume);
 | |
| 			GetMusicResList().insert(std::pair<UINT, MusicPlayer *>(resNameId, pPlayer));
 | |
| 			return true;
 | |
| 		}
 | |
| 		else
 | |
| 		{
 | |
| 			delete pPlayer;
 | |
| 			pPlayer = nullptr;
 | |
| 		}
 | |
| 	}
 | |
| 	return false;
 | |
| }
 | |
| 
 | |
| bool e2d::Music::play(const String& filePath, int nLoopCount)
 | |
| {
 | |
| 	if (Music::preload(filePath))
 | |
| 	{
 | |
| 		UINT nRet = filePath.getHashCode();
 | |
| 		auto pMusic = GetMusicFileList()[nRet];
 | |
| 		if (pMusic->play(nLoopCount))
 | |
| 		{
 | |
| 			return true;
 | |
| 		}
 | |
| 	}
 | |
| 	return false;
 | |
| }
 | |
| 
 | |
| bool e2d::Music::play(int resNameId, const String& resType, int nLoopCount)
 | |
| {
 | |
| 	if (Music::preload(resNameId, resType))
 | |
| 	{
 | |
| 		auto pMusic = GetMusicResList()[resNameId];
 | |
| 		if (pMusic->play(nLoopCount))
 | |
| 		{
 | |
| 			return true;
 | |
| 		}
 | |
| 	}
 | |
| 	return false;
 | |
| }
 | |
| 
 | |
| void e2d::Music::pause(const String& filePath)
 | |
| {
 | |
| 	if (filePath.isEmpty())
 | |
| 		return;
 | |
| 
 | |
| 	UINT nRet = filePath.getHashCode();
 | |
| 
 | |
| 	if (GetMusicFileList().end() != GetMusicFileList().find(nRet))
 | |
| 		GetMusicFileList()[nRet]->pause();
 | |
| }
 | |
| 
 | |
| void e2d::Music::pause(int resNameId, const String& resType)
 | |
| {
 | |
| 	if (GetMusicResList().end() != GetMusicResList().find(resNameId))
 | |
| 		GetMusicResList()[resNameId]->pause();
 | |
| }
 | |
| 
 | |
| void e2d::Music::resume(const String& filePath)
 | |
| {
 | |
| 	if (filePath.isEmpty())
 | |
| 		return;
 | |
| 
 | |
| 	UINT nRet = filePath.getHashCode();
 | |
| 
 | |
| 	if (GetMusicFileList().end() != GetMusicFileList().find(nRet))
 | |
| 		GetMusicFileList()[nRet]->resume();
 | |
| }
 | |
| 
 | |
| void e2d::Music::resume(int resNameId, const String& resType)
 | |
| {
 | |
| 	if (GetMusicResList().end() != GetMusicResList().find(resNameId))
 | |
| 		GetMusicResList()[resNameId]->pause();
 | |
| }
 | |
| 
 | |
| void e2d::Music::stop(const String& filePath)
 | |
| {
 | |
| 	if (filePath.isEmpty())
 | |
| 		return;
 | |
| 
 | |
| 	UINT nRet = filePath.getHashCode();
 | |
| 
 | |
| 	if (GetMusicFileList().end() != GetMusicFileList().find(nRet))
 | |
| 		GetMusicFileList()[nRet]->stop();
 | |
| }
 | |
| 
 | |
| void e2d::Music::stop(int resNameId, const String& resType)
 | |
| {
 | |
| 	if (GetMusicResList().end() != GetMusicResList().find(resNameId))
 | |
| 		GetMusicResList()[resNameId]->stop();
 | |
| }
 | |
| 
 | |
| bool e2d::Music::isPlaying(const String& filePath)
 | |
| {
 | |
| 	if (filePath.isEmpty())
 | |
| 		return false;
 | |
| 
 | |
| 	UINT nRet = filePath.getHashCode();
 | |
| 
 | |
| 	if (GetMusicFileList().end() != GetMusicFileList().find(nRet))
 | |
| 		return GetMusicFileList()[nRet]->isPlaying();
 | |
| 
 | |
| 	return false;
 | |
| }
 | |
| 
 | |
| bool e2d::Music::isPlaying(int resNameId, const String& resType)
 | |
| {
 | |
| 	if (GetMusicResList().end() != GetMusicResList().find(resNameId))
 | |
| 		return GetMusicResList()[resNameId]->isPlaying();
 | |
| 	return false;
 | |
| }
 | |
| 
 | |
| double e2d::Music::getVolume()
 | |
| {
 | |
| 	return s_fMusicVolume;
 | |
| }
 | |
| 
 | |
| void e2d::Music::setVolume(double fVolume)
 | |
| {
 | |
| 	s_fMusicVolume = min(max(float(fVolume), -224), 224);
 | |
| 	for (auto pair : GetMusicFileList())
 | |
| 	{
 | |
| 		pair.second->setVolume(s_fMusicVolume);
 | |
| 	}
 | |
| }
 | |
| 
 | |
| void e2d::Music::pauseAll()
 | |
| {
 | |
| 	for (auto pair : GetMusicFileList())
 | |
| 	{
 | |
| 		pair.second->pause();
 | |
| 	}
 | |
| }
 | |
| 
 | |
| void e2d::Music::resumeAll()
 | |
| {
 | |
| 	for (auto pair : GetMusicFileList())
 | |
| 	{
 | |
| 		pair.second->resume();
 | |
| 	}
 | |
| }
 | |
| 
 | |
| void e2d::Music::stopAll()
 | |
| {
 | |
| 	for (auto pair : GetMusicFileList())
 | |
| 	{
 | |
| 		pair.second->stop();
 | |
| 	}
 | |
| }
 | |
| 
 | |
| IXAudio2 * e2d::Music::getIXAudio2()
 | |
| {
 | |
| 	return s_pXAudio2;
 | |
| }
 | |
| 
 | |
| IXAudio2MasteringVoice * e2d::Music::getIXAudio2MasteringVoice()
 | |
| {
 | |
| 	return s_pMasteringVoice;
 | |
| }
 | |
| 
 | |
| bool e2d::Music::__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");
 | |
| 		e2d::SafeReleaseInterface(s_pXAudio2);
 | |
| 		return false;
 | |
| 	}
 | |
| 
 | |
| 	return true;
 | |
| }
 | |
| 
 | |
| void e2d::Music::__uninit()
 | |
| {
 | |
| 	for (auto pair : GetMusicFileList())
 | |
| 	{
 | |
| 		pair.second->close();
 | |
| 		delete pair.second;
 | |
| 	}
 | |
| 
 | |
| 	GetMusicFileList().clear();
 | |
| 
 | |
| 	if (s_pMasteringVoice)
 | |
| 	{
 | |
| 		s_pMasteringVoice->DestroyVoice();
 | |
| 	}
 | |
| 
 | |
| 	e2d::SafeReleaseInterface(s_pXAudio2);
 | |
| }
 |