1042 lines
26 KiB
C++
1042 lines
26 KiB
C++
|
#include "priv.h"
|
||
|
#include "sccls.h"
|
||
|
#include <mmsystem.h>
|
||
|
#include <amstream.h>
|
||
|
#include "power.h"
|
||
|
#include "dsound.h"
|
||
|
#include "Zaxxon.h"
|
||
|
|
||
|
#ifndef SAFERELEASE
|
||
|
#define SAFERELEASE(x) if (x) { x->Release(); x = NULL; }
|
||
|
#endif
|
||
|
|
||
|
#define AUDIO_MAXBUFFER 64000
|
||
|
|
||
|
// Which is better?
|
||
|
// 2Megs causes 25% cpu usage every 5 seconds
|
||
|
// 1meg causes 15% cpu usage every 2 seconds
|
||
|
// 100k causes a contant 6% cpu usage
|
||
|
// 64k?
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
#define EVENT_NONE 0x0
|
||
|
#define EVENT_PLAY 0x1
|
||
|
#define EVENT_STOP 0x2
|
||
|
#define EVENT_PAUSE 0x3
|
||
|
#define EVENT_FORWARD 0x4
|
||
|
#define EVENT_BACKWARD 0x5
|
||
|
#define EVENT_TERMINATE 0x6
|
||
|
#define EVENT_ADDSONG 0x7
|
||
|
#define EVENT_REMOVESONG 0x8
|
||
|
#define EVENT_NEXTSONG 0x9
|
||
|
#define EVENT_PREVSONG 0xA
|
||
|
#define EVENT_REGISTER 0xB
|
||
|
#define EVENT_DEREGISTER 0xC
|
||
|
#define EVENT_CLEARPLAYLIST 0xD
|
||
|
#define EVENT_SETSONG 0xE
|
||
|
|
||
|
struct ZAXXONEVENT
|
||
|
{
|
||
|
ZAXXONEVENT():_dwEvent(EVENT_NONE), _pNext(NULL) { }
|
||
|
|
||
|
DWORD _dwEvent;
|
||
|
ZAXXONEVENT* _pNext;
|
||
|
|
||
|
union
|
||
|
{
|
||
|
TCHAR szPath[MAX_PATH];
|
||
|
UINT szSeconds;
|
||
|
UINT iIndex;
|
||
|
HWND hwnd;
|
||
|
};
|
||
|
};
|
||
|
|
||
|
typedef struct
|
||
|
{
|
||
|
HANDLE hThread;
|
||
|
CRITICAL_SECTION crit;
|
||
|
HANDLE hEvents;
|
||
|
ZAXXONEVENT* pEvents;
|
||
|
} ZAXXON_ARG;
|
||
|
|
||
|
|
||
|
class CNotifyList
|
||
|
{
|
||
|
HDSA _hdsa;
|
||
|
public:
|
||
|
CNotifyList()
|
||
|
{
|
||
|
_hdsa = DSA_Create(sizeof(HWND), 1);
|
||
|
}
|
||
|
~CNotifyList()
|
||
|
{
|
||
|
if (_hdsa)
|
||
|
DSA_Destroy(_hdsa);
|
||
|
}
|
||
|
|
||
|
void AddNotify(HWND hwnd)
|
||
|
{
|
||
|
if (_hdsa)
|
||
|
DSA_AppendItem(_hdsa, &hwnd);
|
||
|
}
|
||
|
|
||
|
void RemoveNotify(HWND hwnd)
|
||
|
{
|
||
|
//
|
||
|
}
|
||
|
|
||
|
void SendNotify(UINT uMsg, WPARAM wParam, LPARAM lParam)
|
||
|
{
|
||
|
for (int i = 0; i < DSA_GetItemCount(_hdsa); i++)
|
||
|
{
|
||
|
DWORD_PTR l;
|
||
|
HWND hwnd = *(HWND*)DSA_GetItemPtr(_hdsa, i);
|
||
|
SendMessageTimeout(hwnd, uMsg, wParam, lParam, SMTO_ABORTIFHUNG, 100, &l);
|
||
|
}
|
||
|
}
|
||
|
};
|
||
|
|
||
|
class CPlayList
|
||
|
{
|
||
|
int iIndex;
|
||
|
HDPA _hdpa;
|
||
|
static BOOL CALLBACK s_DestroyPlaylist(void* pv, void* pvData)
|
||
|
{
|
||
|
PWSTR psz = (PWSTR)pv;
|
||
|
if (psz)
|
||
|
{
|
||
|
Str_SetPtr(&psz, NULL);
|
||
|
}
|
||
|
|
||
|
return TRUE;
|
||
|
}
|
||
|
|
||
|
public:
|
||
|
int GetIndex() { return iIndex;}
|
||
|
CPlayList()
|
||
|
{
|
||
|
_hdpa = NULL;
|
||
|
iIndex = -1;
|
||
|
}
|
||
|
|
||
|
~CPlayList()
|
||
|
{
|
||
|
Empty();
|
||
|
}
|
||
|
|
||
|
BOOL AddSong(PWSTR psz)
|
||
|
{
|
||
|
if (!_hdpa)
|
||
|
{
|
||
|
_hdpa = DPA_Create(4);
|
||
|
}
|
||
|
|
||
|
if (!_hdpa)
|
||
|
return FALSE;
|
||
|
|
||
|
TCHAR* pszSet = NULL;
|
||
|
Str_SetPtr(&pszSet, psz);
|
||
|
return DPA_AppendPtr(_hdpa, pszSet) != -1;
|
||
|
}
|
||
|
|
||
|
BOOL RemoveSong(int i)
|
||
|
{
|
||
|
if (!_hdpa)
|
||
|
return FALSE;
|
||
|
|
||
|
if (i < iIndex)
|
||
|
iIndex--;
|
||
|
|
||
|
TCHAR* pszSet = (TCHAR*)DPA_DeletePtr(_hdpa, i);
|
||
|
Str_SetPtr(&pszSet, NULL);
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
BOOL GetSong(int i, PWSTR psz, int cch)
|
||
|
{
|
||
|
if (!_hdpa)
|
||
|
return FALSE;
|
||
|
|
||
|
PWSTR pszSong = (PWSTR)DPA_FastGetPtr(_hdpa, i);
|
||
|
if (pszSong)
|
||
|
{
|
||
|
iIndex = i;
|
||
|
StrCpyN(psz, pszSong, cch);
|
||
|
return TRUE;
|
||
|
}
|
||
|
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
BOOL GetNextSong(PWSTR psz, int cch)
|
||
|
{
|
||
|
if (!_hdpa)
|
||
|
return FALSE;
|
||
|
|
||
|
if (iIndex >= DPA_GetPtrCount(_hdpa) - 1)
|
||
|
iIndex = -1;
|
||
|
return GetSong(++iIndex, psz, cch);
|
||
|
|
||
|
}
|
||
|
|
||
|
BOOL GetPrevSong(PWSTR psz, int cch)
|
||
|
{
|
||
|
if (!_hdpa)
|
||
|
return FALSE;
|
||
|
|
||
|
if (iIndex <= 0)
|
||
|
iIndex = DPA_GetPtrCount(_hdpa);
|
||
|
return GetSong(--iIndex, psz, cch);
|
||
|
}
|
||
|
|
||
|
BOOL Empty()
|
||
|
{
|
||
|
if (_hdpa)
|
||
|
{
|
||
|
DPA_DestroyCallback(_hdpa, s_DestroyPlaylist, NULL);
|
||
|
}
|
||
|
_hdpa = NULL;
|
||
|
|
||
|
return TRUE;
|
||
|
}
|
||
|
};
|
||
|
|
||
|
HRESULT CreateBuffer(IDirectSound* pds, WAVEFORMATEX* pwfx, DWORD dwBufferSize, IDirectSoundBuffer** ppdsb)
|
||
|
{
|
||
|
HRESULT hr = S_OK;
|
||
|
IDirectSoundBuffer* psbPrimary;
|
||
|
// Get the primary buffer
|
||
|
DSBUFFERDESC dsbdesc;
|
||
|
ZeroMemory(&dsbdesc, sizeof(DSBUFFERDESC));
|
||
|
dsbdesc.dwSize = sizeof(DSBUFFERDESC);
|
||
|
dsbdesc.dwFlags = DSBCAPS_PRIMARYBUFFER;
|
||
|
hr = pds->CreateSoundBuffer(&dsbdesc, &psbPrimary, NULL);
|
||
|
if (SUCCEEDED(hr))
|
||
|
{
|
||
|
hr = psbPrimary->SetFormat(pwfx);
|
||
|
|
||
|
if (SUCCEEDED(hr))
|
||
|
{
|
||
|
dsbdesc.dwFlags = DSBCAPS_GETCURRENTPOSITION2 |
|
||
|
DSBCAPS_GLOBALFOCUS |
|
||
|
DSBCAPS_CTRLPOSITIONNOTIFY;
|
||
|
|
||
|
dsbdesc.dwBufferBytes = dwBufferSize;
|
||
|
dsbdesc.lpwfxFormat = pwfx;
|
||
|
|
||
|
hr = pds->CreateSoundBuffer(&dsbdesc, ppdsb, NULL);
|
||
|
}
|
||
|
psbPrimary->Release();
|
||
|
}
|
||
|
|
||
|
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
HRESULT CreateDirectSound(IDirectSound** ppds)
|
||
|
{
|
||
|
IDirectSound* pds = NULL;
|
||
|
HRESULT hr = CoCreateInstance(CLSID_DirectSound, NULL, CLSCTX_INPROC_SERVER,
|
||
|
IID_IDirectSound, (void**)&pds);
|
||
|
if (SUCCEEDED(hr))
|
||
|
{
|
||
|
hr = pds->Initialize(NULL); // Don't support more than one at the moment.
|
||
|
if (SUCCEEDED(hr))
|
||
|
{
|
||
|
hr = pds->SetCooperativeLevel(GetDesktopWindow(), DSSCL_PRIORITY);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (SUCCEEDED(hr))
|
||
|
{
|
||
|
*ppds = pds;
|
||
|
pds->AddRef();
|
||
|
}
|
||
|
|
||
|
SAFERELEASE(pds);
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
class CZaxxonPlayingSample
|
||
|
{
|
||
|
public:
|
||
|
CZaxxonPlayingSample();
|
||
|
~CZaxxonPlayingSample();
|
||
|
|
||
|
HRESULT Open(PWSTR psz);
|
||
|
HRESULT SetBuffer(PBYTE pBuf, DWORD dwSize);
|
||
|
HRESULT CopySampleData(PBYTE pBuffer1, DWORD dwBytesForBuffer1,
|
||
|
PBYTE pBuffer2, DWORD dwBytesForBuffer2,
|
||
|
DWORD* pdwBytesLeft1, DWORD* pdwBytesLeft2);
|
||
|
HRESULT GetSampleFormat(WAVEFORMATEX* pwfx);
|
||
|
|
||
|
private:
|
||
|
void CloseOut();
|
||
|
HRESULT _SetupMediaStream();
|
||
|
TCHAR _szPath[MAX_PATH];
|
||
|
IAMMultiMediaStream *_pMMStream;
|
||
|
IMediaStream *_pStream;
|
||
|
IAudioStreamSample *_pSample;
|
||
|
IAudioMediaStream *_pAudioStream;
|
||
|
IAudioData *_pAudioData;
|
||
|
WAVEFORMATEX _wfx;
|
||
|
HANDLE _hRenderEvent;
|
||
|
PBYTE _pBuffer;
|
||
|
};
|
||
|
|
||
|
|
||
|
CZaxxonPlayingSample::CZaxxonPlayingSample():
|
||
|
_pMMStream(NULL),
|
||
|
_pStream(NULL),
|
||
|
_pSample(NULL),
|
||
|
_pAudioStream(NULL),
|
||
|
_pAudioData(NULL),
|
||
|
_hRenderEvent(NULL),
|
||
|
_pBuffer(NULL)
|
||
|
{
|
||
|
ZeroMemory(&_wfx, sizeof(WAVEFORMATEX));
|
||
|
}
|
||
|
|
||
|
CZaxxonPlayingSample::~CZaxxonPlayingSample()
|
||
|
{
|
||
|
CloseOut();
|
||
|
}
|
||
|
|
||
|
void CZaxxonPlayingSample::CloseOut()
|
||
|
{
|
||
|
ATOMICRELEASE(_pMMStream);
|
||
|
ATOMICRELEASE(_pAudioData);
|
||
|
ATOMICRELEASE(_pSample);
|
||
|
ATOMICRELEASE(_pStream);
|
||
|
ATOMICRELEASE(_pAudioStream);
|
||
|
if (_hRenderEvent)
|
||
|
{
|
||
|
CloseHandle(_hRenderEvent);
|
||
|
_hRenderEvent = NULL;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
HRESULT CZaxxonPlayingSample::Open(PWSTR psz)
|
||
|
{
|
||
|
CloseOut();
|
||
|
lstrcpy(_szPath, psz);
|
||
|
return _SetupMediaStream();
|
||
|
}
|
||
|
|
||
|
|
||
|
HRESULT CZaxxonPlayingSample::SetBuffer(PBYTE pBuf, DWORD dwSize)
|
||
|
{
|
||
|
if (_pAudioData && _pAudioStream)
|
||
|
{
|
||
|
_pAudioData->SetBuffer(dwSize, pBuf, 0);
|
||
|
_pAudioData->SetFormat(&_wfx);
|
||
|
|
||
|
_pBuffer = pBuf;
|
||
|
|
||
|
return _pAudioStream->CreateSample(_pAudioData, 0, &_pSample);
|
||
|
}
|
||
|
|
||
|
return E_FAIL;
|
||
|
}
|
||
|
|
||
|
|
||
|
HRESULT CZaxxonPlayingSample::GetSampleFormat(WAVEFORMATEX* pwfx)
|
||
|
{
|
||
|
CopyMemory(pwfx, &_wfx, sizeof(_wfx));
|
||
|
return S_OK;
|
||
|
}
|
||
|
|
||
|
HRESULT CZaxxonPlayingSample::CopySampleData(PBYTE pBuffer1, DWORD dwBytesForBuffer1,
|
||
|
PBYTE pBuffer2, DWORD dwBytesForBuffer2,
|
||
|
DWORD* pdwBytesLeft1, DWORD* pdwBytesLeft2)
|
||
|
{
|
||
|
if (!_pSample)
|
||
|
return E_FAIL;
|
||
|
|
||
|
HRESULT hr = _pSample->Update(0, _hRenderEvent, NULL, 0);
|
||
|
if (FAILED(hr) || MS_S_ENDOFSTREAM == hr)
|
||
|
{
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
DWORD dwLength;
|
||
|
WaitForSingleObject(_hRenderEvent, INFINITE);
|
||
|
_pAudioData->GetInfo(NULL, NULL, &dwLength);
|
||
|
|
||
|
*pdwBytesLeft1 = 0;
|
||
|
*pdwBytesLeft2 = 0;
|
||
|
|
||
|
// _pBuffer contains the audio data. Copy.
|
||
|
if (dwLength < dwBytesForBuffer1)
|
||
|
{
|
||
|
*pdwBytesLeft2 = dwBytesForBuffer2;
|
||
|
*pdwBytesLeft1 = dwBytesForBuffer1 - dwLength;
|
||
|
|
||
|
dwBytesForBuffer1 = dwLength;
|
||
|
dwBytesForBuffer2 = 0;
|
||
|
}
|
||
|
|
||
|
|
||
|
CopyMemory(pBuffer1, _pBuffer, dwBytesForBuffer1);
|
||
|
dwLength -= dwBytesForBuffer1;
|
||
|
|
||
|
if (dwBytesForBuffer2 > 0 && dwLength > 0)
|
||
|
{
|
||
|
CopyMemory(pBuffer2, _pBuffer + dwBytesForBuffer1, dwLength);
|
||
|
if (dwLength < dwBytesForBuffer2)
|
||
|
*pdwBytesLeft2 = dwBytesForBuffer2 - dwLength;
|
||
|
}
|
||
|
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
HRESULT CZaxxonPlayingSample::_SetupMediaStream()
|
||
|
{
|
||
|
HRESULT hr = E_INVALIDARG;
|
||
|
if (_szPath[0] != 0)
|
||
|
{
|
||
|
hr = CoCreateInstance(CLSID_AMMultiMediaStream, NULL, CLSCTX_INPROC_SERVER,
|
||
|
IID_IAMMultiMediaStream, (void **)&_pMMStream);
|
||
|
if (SUCCEEDED(hr))
|
||
|
{
|
||
|
_pMMStream->Initialize(STREAMTYPE_READ, AMMSF_NOGRAPHTHREAD, NULL);
|
||
|
_pMMStream->AddMediaStream(NULL, &MSPID_PrimaryAudio, 0, NULL);
|
||
|
hr = _pMMStream->OpenFile(_szPath, AMMSF_RUN);
|
||
|
if (SUCCEEDED(hr))
|
||
|
{
|
||
|
hr = CoCreateInstance(CLSID_AMAudioData, NULL, CLSCTX_INPROC_SERVER,
|
||
|
IID_IAudioData, (void **)&_pAudioData);
|
||
|
|
||
|
if (SUCCEEDED(hr))
|
||
|
{
|
||
|
hr = _pMMStream->GetMediaStream(MSPID_PrimaryAudio, &_pStream);
|
||
|
if (SUCCEEDED(hr))
|
||
|
{
|
||
|
hr = _pStream->QueryInterface(IID_IAudioMediaStream, (void **)&_pAudioStream);
|
||
|
if (SUCCEEDED(hr))
|
||
|
{
|
||
|
_pAudioStream->GetFormat(&_wfx);
|
||
|
_hRenderEvent = CreateEvent(NULL, NULL, TRUE, NULL);
|
||
|
hr = S_OK;
|
||
|
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
ZAXXONEVENT* GetZaxxonEvent(ZAXXON_ARG* pza)
|
||
|
{
|
||
|
ZAXXONEVENT* pz = NULL;
|
||
|
if (pza->pEvents)
|
||
|
{
|
||
|
EnterCriticalSection(&pza->crit);
|
||
|
if (pza->pEvents)
|
||
|
{
|
||
|
pz = pza->pEvents;
|
||
|
pza->pEvents = pza->pEvents->_pNext;
|
||
|
}
|
||
|
|
||
|
LeaveCriticalSection(&pza->crit);
|
||
|
}
|
||
|
|
||
|
return pz;
|
||
|
}
|
||
|
|
||
|
HRESULT CopySample(CZaxxonPlayingSample* pzax, IDirectSoundBuffer* pdsb, DWORD dwStart, DWORD dwNumBytes)
|
||
|
{
|
||
|
HRESULT hr;
|
||
|
PBYTE pBuffer1;
|
||
|
PBYTE pBuffer2;
|
||
|
DWORD dwBytesToCopyToBuffer1;
|
||
|
DWORD dwBytesToCopyToBuffer2;
|
||
|
DWORD dwBytesLeft1;
|
||
|
DWORD dwBytesLeft2;
|
||
|
|
||
|
if (!pdsb)
|
||
|
return E_ACCESSDENIED;
|
||
|
|
||
|
hr = pdsb->Lock(dwStart, dwNumBytes, (void**)&pBuffer1, &dwBytesToCopyToBuffer1,
|
||
|
(void**)&pBuffer2, &dwBytesToCopyToBuffer2, 0);
|
||
|
if (SUCCEEDED(hr))
|
||
|
{
|
||
|
hr = pzax->CopySampleData(
|
||
|
pBuffer1, dwBytesToCopyToBuffer1,
|
||
|
pBuffer2, dwBytesToCopyToBuffer2,
|
||
|
&dwBytesLeft1,
|
||
|
&dwBytesLeft2);
|
||
|
|
||
|
if (FAILED(hr) || MS_S_ENDOFSTREAM == hr)
|
||
|
{
|
||
|
pdsb->Stop();
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
if (dwBytesLeft1 > 0)
|
||
|
{
|
||
|
ZeroMemory(pBuffer1 + dwBytesToCopyToBuffer1 - dwBytesLeft1,
|
||
|
dwBytesLeft1);
|
||
|
}
|
||
|
|
||
|
if (dwBytesLeft2 > 0)
|
||
|
{
|
||
|
ZeroMemory(pBuffer2 + dwBytesToCopyToBuffer2 - dwBytesLeft2,
|
||
|
dwBytesLeft2);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
pdsb->Unlock(pBuffer1, dwBytesToCopyToBuffer1,
|
||
|
pBuffer2, dwBytesToCopyToBuffer2);
|
||
|
|
||
|
}
|
||
|
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
BOOL SetupSample(CZaxxonPlayingSample* pzaxSample, PWSTR psz, PBYTE pBuffer, DSBPOSITIONNOTIFY* rgdsbp, int cdsbpn, IDirectSound* pds, IDirectSoundBuffer** ppdsb)
|
||
|
{
|
||
|
if (SUCCEEDED(pzaxSample->Open(psz)))
|
||
|
{
|
||
|
WAVEFORMATEX wfx;
|
||
|
|
||
|
pzaxSample->SetBuffer(pBuffer, AUDIO_MAXBUFFER / 2);
|
||
|
pzaxSample->GetSampleFormat(&wfx);
|
||
|
|
||
|
if (SUCCEEDED(CreateBuffer(pds, &wfx, AUDIO_MAXBUFFER, ppdsb)))
|
||
|
{
|
||
|
IDirectSoundNotify* pdsn;
|
||
|
if (SUCCEEDED((*ppdsb)->QueryInterface(IID_IDirectSoundNotify, (void**)&pdsn)))
|
||
|
{
|
||
|
for (int i = 0; i < cdsbpn; i++)
|
||
|
{
|
||
|
ResetEvent(rgdsbp[i].hEventNotify);
|
||
|
}
|
||
|
|
||
|
pdsn->SetNotificationPositions(cdsbpn, rgdsbp);
|
||
|
pdsn->Release();
|
||
|
}
|
||
|
|
||
|
if (SUCCEEDED(CopySample(pzaxSample, *ppdsb, 0, AUDIO_MAXBUFFER / 2)))
|
||
|
{
|
||
|
(*ppdsb)->SetCurrentPosition(0);
|
||
|
(*ppdsb)->Play(0, 0, DSBPLAY_LOOPING);
|
||
|
|
||
|
return TRUE;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
(*ppdsb)->Release();
|
||
|
*ppdsb = NULL;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
ULONG CALLBACK AudioRenderThread(LPVOID pvArg)
|
||
|
{
|
||
|
CPlayList playlist;
|
||
|
CNotifyList notifylist;
|
||
|
ZAXXON_ARG* pza = (ZAXXON_ARG*)pvArg;
|
||
|
HRESULT hr = CoInitialize(NULL);
|
||
|
PBYTE pBuffer = (PBYTE)LocalAlloc(LMEM_FIXED, AUDIO_MAXBUFFER);
|
||
|
if (SUCCEEDED(hr) && pBuffer)
|
||
|
{
|
||
|
IDirectSound* pds = NULL;
|
||
|
IDirectSoundBuffer* pdsb = NULL;
|
||
|
|
||
|
HANDLE rgEvent[] =
|
||
|
{
|
||
|
CreateEvent(NULL, FALSE, FALSE, NULL),
|
||
|
CreateEvent(NULL, FALSE, FALSE, NULL),
|
||
|
pza->hEvents,
|
||
|
};
|
||
|
|
||
|
DSBPOSITIONNOTIFY rgdsbp[] =
|
||
|
{
|
||
|
{0, rgEvent[0]},
|
||
|
{AUDIO_MAXBUFFER / 2, rgEvent[1]},
|
||
|
};
|
||
|
|
||
|
if (SUCCEEDED(CreateDirectSound(&pds)))
|
||
|
{
|
||
|
CZaxxonPlayingSample zaxSample;
|
||
|
|
||
|
BOOL fPaused = FALSE;
|
||
|
BOOL fDone = FALSE;
|
||
|
while (!fDone)
|
||
|
{
|
||
|
DWORD dwEvent = WaitForMultipleObjects(ARRAYSIZE(rgEvent), rgEvent, FALSE, INFINITE);
|
||
|
dwEvent -= WAIT_OBJECT_0;
|
||
|
|
||
|
if (dwEvent < 2)
|
||
|
{
|
||
|
DWORD dwStartByte;
|
||
|
if (dwEvent + 1 >= ARRAYSIZE(rgdsbp))
|
||
|
dwStartByte = rgdsbp[0].dwOffset;
|
||
|
else
|
||
|
dwStartByte = rgdsbp[dwEvent + 1].dwOffset;
|
||
|
|
||
|
hr = CopySample(&zaxSample, pdsb, dwStartByte, AUDIO_MAXBUFFER / 2);
|
||
|
if (FAILED(hr) || MS_S_ENDOFSTREAM == hr)
|
||
|
{
|
||
|
TCHAR pszPath[MAX_PATH];
|
||
|
if (playlist.GetNextSong(pszPath, MAX_PATH))
|
||
|
{
|
||
|
ATOMICRELEASE(pdsb);
|
||
|
notifylist.SendNotify(WM_SONGCHANGE, (WPARAM)pszPath, 0);
|
||
|
SetupSample(&zaxSample, pszPath, pBuffer, rgdsbp, ARRAYSIZE(rgdsbp), pds, &pdsb);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
ZAXXONEVENT* pZaxxonEvent;
|
||
|
while ((pZaxxonEvent = GetZaxxonEvent(pza)))
|
||
|
{
|
||
|
switch (pZaxxonEvent->_dwEvent)
|
||
|
{
|
||
|
case EVENT_STOP:
|
||
|
if (pdsb)
|
||
|
pdsb->Stop();
|
||
|
notifylist.SendNotify(WM_SONGSTOP, 0, 0);
|
||
|
fPaused = FALSE;
|
||
|
break;
|
||
|
|
||
|
case EVENT_ADDSONG:
|
||
|
playlist.AddSong(pZaxxonEvent->szPath);
|
||
|
break;
|
||
|
|
||
|
case EVENT_REMOVESONG:
|
||
|
if (pZaxxonEvent->iIndex == playlist.GetIndex())
|
||
|
{
|
||
|
fPaused = FALSE;
|
||
|
TCHAR pszPath[MAX_PATH];
|
||
|
if (playlist.GetNextSong(pszPath, MAX_PATH))
|
||
|
{
|
||
|
if (pdsb)
|
||
|
pdsb->Stop();
|
||
|
ATOMICRELEASE(pdsb);
|
||
|
notifylist.SendNotify(WM_SONGCHANGE, (WPARAM)pszPath, 0);
|
||
|
SetupSample(&zaxSample, pszPath, pBuffer, rgdsbp, ARRAYSIZE(rgdsbp), pds, &pdsb);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
playlist.RemoveSong(pZaxxonEvent->iIndex);
|
||
|
break;
|
||
|
|
||
|
case EVENT_CLEARPLAYLIST:
|
||
|
if (pdsb)
|
||
|
pdsb->Stop();
|
||
|
fPaused = FALSE;
|
||
|
notifylist.SendNotify(WM_SONGSTOP, 0, 0);
|
||
|
playlist.Empty();
|
||
|
break;
|
||
|
|
||
|
case EVENT_REGISTER:
|
||
|
notifylist.AddNotify(pZaxxonEvent->hwnd);
|
||
|
break;
|
||
|
case EVENT_DEREGISTER:
|
||
|
notifylist.RemoveNotify(pZaxxonEvent->hwnd);
|
||
|
break;
|
||
|
|
||
|
case EVENT_PLAY:
|
||
|
if (fPaused)
|
||
|
{
|
||
|
if (pdsb)
|
||
|
pdsb->Play(0, 0, DSBPLAY_LOOPING);
|
||
|
fPaused = FALSE;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
fPaused = FALSE;
|
||
|
TCHAR pszPath[MAX_PATH];
|
||
|
if (playlist.GetNextSong(pszPath, MAX_PATH))
|
||
|
{
|
||
|
ATOMICRELEASE(pdsb);
|
||
|
notifylist.SendNotify(WM_SONGCHANGE, (WPARAM)pszPath, 0);
|
||
|
SetupSample(&zaxSample, pszPath, pBuffer, rgdsbp, ARRAYSIZE(rgdsbp), pds, &pdsb);
|
||
|
}
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case EVENT_NEXTSONG:
|
||
|
{
|
||
|
fPaused = FALSE;
|
||
|
TCHAR pszPath[MAX_PATH];
|
||
|
if (playlist.GetNextSong(pszPath, MAX_PATH))
|
||
|
{
|
||
|
if (pdsb)
|
||
|
pdsb->Stop();
|
||
|
ATOMICRELEASE(pdsb);
|
||
|
notifylist.SendNotify(WM_SONGCHANGE, (WPARAM)pszPath, 0);
|
||
|
SetupSample(&zaxSample, pszPath, pBuffer, rgdsbp, ARRAYSIZE(rgdsbp), pds, &pdsb);
|
||
|
}
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case EVENT_PREVSONG:
|
||
|
{
|
||
|
fPaused = FALSE;
|
||
|
TCHAR pszPath[MAX_PATH];
|
||
|
if (playlist.GetPrevSong(pszPath, MAX_PATH))
|
||
|
{
|
||
|
if (pdsb)
|
||
|
pdsb->Stop();
|
||
|
ATOMICRELEASE(pdsb);
|
||
|
notifylist.SendNotify(WM_SONGCHANGE, (WPARAM)pszPath, 0);
|
||
|
SetupSample(&zaxSample, pszPath, pBuffer, rgdsbp, ARRAYSIZE(rgdsbp), pds, &pdsb);
|
||
|
|
||
|
}
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case EVENT_SETSONG:
|
||
|
{
|
||
|
fPaused = FALSE;
|
||
|
TCHAR pszPath[MAX_PATH];
|
||
|
if (playlist.GetSong(pZaxxonEvent->iIndex, pszPath, MAX_PATH))
|
||
|
{
|
||
|
if (pdsb)
|
||
|
pdsb->Stop();
|
||
|
ATOMICRELEASE(pdsb);
|
||
|
notifylist.SendNotify(WM_SONGCHANGE, (WPARAM)pszPath, 0);
|
||
|
SetupSample(&zaxSample, pszPath, pBuffer, rgdsbp, ARRAYSIZE(rgdsbp), pds, &pdsb);
|
||
|
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
case EVENT_PAUSE:
|
||
|
if (pdsb)
|
||
|
pdsb->Stop();
|
||
|
fPaused = TRUE;
|
||
|
|
||
|
break;
|
||
|
|
||
|
case EVENT_TERMINATE:
|
||
|
fDone = TRUE;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
delete pZaxxonEvent;
|
||
|
}
|
||
|
|
||
|
ResetEvent(pza->hEvents);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
SAFERELEASE(pdsb);
|
||
|
SAFERELEASE(pds);
|
||
|
|
||
|
if (rgEvent[0])
|
||
|
CloseHandle(rgEvent[0]);
|
||
|
|
||
|
if (rgEvent[1])
|
||
|
CloseHandle(rgEvent[1]);
|
||
|
|
||
|
CoUninitialize();
|
||
|
}
|
||
|
|
||
|
if (pBuffer)
|
||
|
LocalFree((HLOCAL)pBuffer);
|
||
|
return 1;
|
||
|
}
|
||
|
|
||
|
class CZaxxonPlayer : public IZaxxonPlayer
|
||
|
{
|
||
|
ZAXXON_ARG _za;
|
||
|
LPTSTR _pszFile;
|
||
|
int _cRef;
|
||
|
HRESULT _LoadTypeLib();
|
||
|
BOOL _AddPlayEvent(int i);
|
||
|
BOOL _AddRemoveEvent(int i);
|
||
|
BOOL _AddSongEvent(int iEvent, PWSTR pszFile);
|
||
|
BOOL _AddHWNDEvent(int iEvent, HWND hwnd);
|
||
|
BOOL _AddPositionEvent(DWORD dwEvent, UINT uSeconds);
|
||
|
BOOL _AddEvent(DWORD dwEvent);
|
||
|
BOOL _AddEventToList(ZAXXONEVENT* pzEvent);
|
||
|
public:
|
||
|
CZaxxonPlayer();
|
||
|
virtual ~CZaxxonPlayer();
|
||
|
|
||
|
STDMETHODIMP QueryInterface(REFIID riid, void** ppvObj);
|
||
|
STDMETHODIMP_(ULONG) AddRef();
|
||
|
STDMETHODIMP_(ULONG) Release();
|
||
|
|
||
|
STDMETHODIMP Play();
|
||
|
STDMETHODIMP Stop();
|
||
|
STDMETHODIMP NextSong();
|
||
|
STDMETHODIMP PrevSong();
|
||
|
STDMETHODIMP SetSong(int i);
|
||
|
STDMETHODIMP Forward(UINT iSeconds);
|
||
|
STDMETHODIMP Backward(UINT iSeconds);
|
||
|
STDMETHODIMP Pause();
|
||
|
STDMETHODIMP AddSong(LPWSTR pszFile);
|
||
|
STDMETHODIMP RemoveSong(int i);
|
||
|
STDMETHODIMP Register(HWND hwnd);
|
||
|
STDMETHODIMP DeRegister(HWND hwnd);
|
||
|
STDMETHODIMP ClearPlaylist();
|
||
|
};
|
||
|
|
||
|
BOOL CZaxxonPlayer::_AddRemoveEvent(int i)
|
||
|
{
|
||
|
ZAXXONEVENT* pze = new ZAXXONEVENT;
|
||
|
if (pze)
|
||
|
{
|
||
|
pze->_dwEvent = EVENT_REMOVESONG;
|
||
|
pze->iIndex = i;
|
||
|
return _AddEventToList(pze);
|
||
|
}
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
BOOL CZaxxonPlayer::_AddPlayEvent(int i)
|
||
|
{
|
||
|
ZAXXONEVENT* pze = new ZAXXONEVENT;
|
||
|
if (pze)
|
||
|
{
|
||
|
pze->_dwEvent = EVENT_SETSONG;
|
||
|
pze->iIndex = i;
|
||
|
return _AddEventToList(pze);
|
||
|
}
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
BOOL CZaxxonPlayer::_AddPositionEvent(DWORD dwEvent, UINT uSeconds)
|
||
|
{
|
||
|
return FALSE;
|
||
|
|
||
|
}
|
||
|
|
||
|
BOOL CZaxxonPlayer::_AddHWNDEvent(int iEvent, HWND hwnd)
|
||
|
{
|
||
|
ZAXXONEVENT* pze = new ZAXXONEVENT;
|
||
|
if (pze)
|
||
|
{
|
||
|
pze->_dwEvent = iEvent;
|
||
|
pze->hwnd = hwnd;
|
||
|
return _AddEventToList(pze);
|
||
|
}
|
||
|
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
|
||
|
BOOL CZaxxonPlayer::_AddSongEvent(int iEvent, PWSTR pszFile)
|
||
|
{
|
||
|
ZAXXONEVENT* pze = new ZAXXONEVENT;
|
||
|
if (pze)
|
||
|
{
|
||
|
pze->_dwEvent = iEvent;
|
||
|
StrCpyN(pze->szPath, pszFile, ARRAYSIZE(pze->szPath));
|
||
|
return _AddEventToList(pze);
|
||
|
}
|
||
|
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
|
||
|
BOOL CZaxxonPlayer::_AddEvent(DWORD dwEvent)
|
||
|
{
|
||
|
ZAXXONEVENT* pze = new ZAXXONEVENT;
|
||
|
if (pze)
|
||
|
{
|
||
|
pze->_dwEvent = dwEvent;
|
||
|
return _AddEventToList(pze);
|
||
|
}
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
BOOL CZaxxonPlayer::_AddEventToList(ZAXXONEVENT* pzEvent)
|
||
|
{
|
||
|
EnterCriticalSection(&_za.crit);
|
||
|
BOOL fRet = FALSE;
|
||
|
ZAXXONEVENT** ppza = &_za.pEvents;
|
||
|
|
||
|
while (*ppza)
|
||
|
{
|
||
|
ppza = &(*ppza)->_pNext;
|
||
|
}
|
||
|
|
||
|
*ppza = pzEvent;
|
||
|
SetEvent(_za.hEvents);
|
||
|
|
||
|
LeaveCriticalSection(&_za.crit);
|
||
|
|
||
|
return fRet;
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
CZaxxonPlayer::CZaxxonPlayer() : _cRef(1)
|
||
|
{
|
||
|
ZeroMemory(&_za, sizeof(_za));
|
||
|
InitializeCriticalSection(&_za.crit);
|
||
|
_za.hEvents = CreateEvent(NULL, TRUE, FALSE, NULL);
|
||
|
if (_za.hEvents)
|
||
|
{
|
||
|
DWORD thread;
|
||
|
_za.hThread = CreateThread(NULL, 0, AudioRenderThread, (LPVOID)&_za, 0, &thread);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
CZaxxonPlayer::~CZaxxonPlayer()
|
||
|
{
|
||
|
if (_za.hThread)
|
||
|
{
|
||
|
_AddEvent(EVENT_TERMINATE);
|
||
|
WaitForSingleObject(_za.hThread, INFINITE);
|
||
|
|
||
|
CloseHandle(_za.hThread);
|
||
|
}
|
||
|
|
||
|
CloseHandle(_za.hEvents);
|
||
|
|
||
|
DeleteCriticalSection(&_za.crit);
|
||
|
for (ZAXXONEVENT* pza = GetZaxxonEvent(&_za); pza; pza = GetZaxxonEvent(&_za))
|
||
|
{
|
||
|
delete pza;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
STDMETHODIMP CZaxxonPlayer::QueryInterface(REFIID riid, LPVOID * ppvObj)
|
||
|
{
|
||
|
static const QITAB qit[] =
|
||
|
{
|
||
|
QITABENT(CZaxxonPlayer, IZaxxonPlayer),
|
||
|
{ 0 },
|
||
|
};
|
||
|
|
||
|
return QISearch(this, qit, riid, ppvObj);
|
||
|
}
|
||
|
|
||
|
ULONG CZaxxonPlayer::AddRef()
|
||
|
{
|
||
|
_cRef++;
|
||
|
return _cRef;
|
||
|
}
|
||
|
|
||
|
ULONG CZaxxonPlayer::Release()
|
||
|
{
|
||
|
ASSERT(_cRef > 0);
|
||
|
_cRef--;
|
||
|
|
||
|
if (_cRef > 0)
|
||
|
return _cRef;
|
||
|
|
||
|
delete this;
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
HRESULT CZaxxonPlayer::Play()
|
||
|
{
|
||
|
_AddEvent(EVENT_PLAY);
|
||
|
return S_OK;
|
||
|
}
|
||
|
|
||
|
HRESULT CZaxxonPlayer::Stop()
|
||
|
{
|
||
|
_AddEvent(EVENT_STOP);
|
||
|
return E_NOTIMPL;
|
||
|
}
|
||
|
|
||
|
HRESULT CZaxxonPlayer::Forward(UINT iSeconds)
|
||
|
{
|
||
|
return E_NOTIMPL;
|
||
|
}
|
||
|
|
||
|
HRESULT CZaxxonPlayer::Backward(UINT iSeconds)
|
||
|
{
|
||
|
return E_NOTIMPL;
|
||
|
}
|
||
|
|
||
|
HRESULT CZaxxonPlayer::Pause()
|
||
|
{
|
||
|
_AddEvent(EVENT_PAUSE);
|
||
|
return S_OK;
|
||
|
|
||
|
}
|
||
|
|
||
|
HRESULT CZaxxonPlayer::AddSong(LPWSTR pszFile)
|
||
|
{
|
||
|
_AddSongEvent(EVENT_ADDSONG, pszFile);
|
||
|
|
||
|
return S_OK;
|
||
|
}
|
||
|
|
||
|
HRESULT CZaxxonPlayer::RemoveSong(int i)
|
||
|
{
|
||
|
_AddRemoveEvent(i);
|
||
|
|
||
|
return S_OK;
|
||
|
}
|
||
|
|
||
|
HRESULT CZaxxonPlayer::NextSong()
|
||
|
{
|
||
|
_AddEvent(EVENT_NEXTSONG);
|
||
|
|
||
|
return S_OK;
|
||
|
}
|
||
|
|
||
|
HRESULT CZaxxonPlayer::PrevSong()
|
||
|
{
|
||
|
_AddEvent(EVENT_PREVSONG);
|
||
|
|
||
|
return S_OK;
|
||
|
}
|
||
|
|
||
|
HRESULT CZaxxonPlayer::SetSong(int i)
|
||
|
{
|
||
|
_AddPlayEvent(i);
|
||
|
return S_OK;
|
||
|
}
|
||
|
|
||
|
HRESULT CZaxxonPlayer::Register(HWND hwnd)
|
||
|
{
|
||
|
_AddHWNDEvent(EVENT_REGISTER, hwnd);
|
||
|
return S_OK;
|
||
|
|
||
|
}
|
||
|
|
||
|
HRESULT CZaxxonPlayer::DeRegister(HWND hwnd)
|
||
|
{
|
||
|
_AddHWNDEvent(EVENT_REGISTER, hwnd);
|
||
|
return S_OK;
|
||
|
}
|
||
|
|
||
|
HRESULT CZaxxonPlayer::ClearPlaylist()
|
||
|
{
|
||
|
_AddEvent(EVENT_CLEARPLAYLIST);
|
||
|
|
||
|
return S_OK;
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
STDAPI CZaxxonPlayer_CreateInstance(IUnknown *punk, REFIID riid, void **ppv)
|
||
|
{
|
||
|
HRESULT hr;
|
||
|
CZaxxonPlayer *pzax = new CZaxxonPlayer;
|
||
|
if (pzax)
|
||
|
{
|
||
|
hr = pzax->QueryInterface(riid, ppv);
|
||
|
pzax->Release();
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
hr = E_OUTOFMEMORY;
|
||
|
*ppv = NULL;
|
||
|
}
|
||
|
return hr;
|
||
|
}
|