/*
* $Id$
*
* (C) 2006-2011 see AUTHORS
*
* This file is part of mplayerc.
*
* Mplayerc is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* Mplayerc is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*
*/
#include "stdafx.h"
#ifdef REGISTER_FILTER
#include
#endif
#include
#include "../../../DSUtil/DSUtil.h"
#include
#include
#include "MpcAudioRenderer.h"
// === Compatibility with Windows SDK v6.0A (define in KSMedia.h in Windows 7 SDK or later)
#ifndef STATIC_KSDATAFORMAT_SUBTYPE_IEC61937_DOLBY_DIGITAL
#define STATIC_KSDATAFORMAT_SUBTYPE_IEC61937_DOLBY_DIGITAL\
DEFINE_WAVEFORMATEX_GUID(WAVE_FORMAT_DOLBY_AC3_SPDIF)
DEFINE_GUIDSTRUCT("00000092-0000-0010-8000-00aa00389b71", KSDATAFORMAT_SUBTYPE_IEC61937_DOLBY_DIGITAL);
#define KSDATAFORMAT_SUBTYPE_IEC61937_DOLBY_DIGITAL DEFINE_GUIDNAMED(KSDATAFORMAT_SUBTYPE_IEC61937_DOLBY_DIGITAL)
#define STATIC_KSDATAFORMAT_SUBTYPE_IEC61937_DOLBY_DIGITAL_PLUS \
0x0000000aL, 0x0cea, 0x0010, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71
DEFINE_GUIDSTRUCT("0000000a-0cea-0010-8000-00aa00389b71", KSDATAFORMAT_SUBTYPE_IEC61937_DOLBY_DIGITAL_PLUS);
#define KSDATAFORMAT_SUBTYPE_IEC61937_DOLBY_DIGITAL_PLUS DEFINE_GUIDNAMED(KSDATAFORMAT_SUBTYPE_IEC61937_DOLBY_DIGITAL_PLUS)
#define STATIC_KSDATAFORMAT_SUBTYPE_IEC61937_DOLBY_MLP \
0x0000000cL, 0x0cea, 0x0010, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71
DEFINE_GUIDSTRUCT("0000000c-0cea-0010-8000-00aa00389b71", KSDATAFORMAT_SUBTYPE_IEC61937_DOLBY_MLP);
#define KSDATAFORMAT_SUBTYPE_IEC61937_DOLBY_MLP DEFINE_GUIDNAMED(KSDATAFORMAT_SUBTYPE_IEC61937_DOLBY_MLP)
#define STATIC_KSDATAFORMAT_SUBTYPE_IEC61937_DTS\
DEFINE_WAVEFORMATEX_GUID(WAVE_FORMAT_DTS)
DEFINE_GUIDSTRUCT("00000008-0000-0010-8000-00aa00389b71", KSDATAFORMAT_SUBTYPE_IEC61937_DTS);
#define KSDATAFORMAT_SUBTYPE_IEC61937_DTS DEFINE_GUIDNAMED(KSDATAFORMAT_SUBTYPE_IEC61937_DTS)
#define STATIC_KSDATAFORMAT_SUBTYPE_IEC61937_DTS_HD \
0x0000000bL, 0x0cea, 0x0010, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71
DEFINE_GUIDSTRUCT("0000000b-0cea-0010-8000-00aa00389b71", KSDATAFORMAT_SUBTYPE_IEC61937_DTS_HD);
#define KSDATAFORMAT_SUBTYPE_IEC61937_DTS_HD DEFINE_GUIDNAMED(KSDATAFORMAT_SUBTYPE_IEC61937_DTS_HD)
#endif
#ifdef REGISTER_FILTER
const AMOVIESETUP_MEDIATYPE sudPinTypesIn[] = {
{&GUID_NULL},
};
const AMOVIESETUP_PIN sudpPins[] = {
{L"Input", FALSE, FALSE, FALSE, FALSE, &CLSID_NULL, NULL, countof(sudPinTypesIn), sudPinTypesIn},
};
const AMOVIESETUP_FILTER sudFilter[] = {
{&__uuidof(CMpcAudioRenderer), L"MPC Audio Renderer", 0x40000001, countof(sudpPins), sudpPins, CLSID_AudioRendererCategory},
};
CFactoryTemplate g_Templates[] = {
{sudFilter[0].strName, &__uuidof(CMpcAudioRenderer), CreateInstance, NULL, &sudFilter[0]},
{L"CMpcAudioRendererPropertyPage", &__uuidof(CMpcAudioRendererSettingsWnd), CreateInstance >},
};
int g_cTemplates = countof(g_Templates);
STDAPI DllRegisterServer()
{
return AMovieDllRegisterServer2(TRUE);
}
STDAPI DllUnregisterServer()
{
return AMovieDllRegisterServer2(FALSE);
}
#include "../../FilterApp.h"
CFilterApp theApp;
#endif
static GUID lpSoundGUID = {0xdef00000, 0x9c6d, 0x47ed, {0xaa, 0xf1, 0x4d, 0xda, 0x8f, 0x2b, 0x5c, 0x03}}; //DSDEVID_DefaultPlayback from dsound.h
bool CALLBACK DSEnumProc2(LPGUID lpGUID,
LPCTSTR lpszDesc,
LPCTSTR lpszDrvName,
LPVOID lpContext )
{
CString *pStr = (CString *)lpContext;
ASSERT ( pStr );
CString strGUID = *pStr;
if (lpGUID != NULL) // NULL only for "Primary Sound Driver".
{
if (strGUID == lpszDesc) {
memcpy((VOID *)&lpSoundGUID, lpGUID, sizeof(GUID));
}
}
return TRUE;
}
CMpcAudioRenderer::CMpcAudioRenderer(LPUNKNOWN punk, HRESULT *phr)
: CBaseRenderer(__uuidof(this), NAME("MPC - Audio Renderer"), punk, phr)
, m_pDSBuffer (NULL )
, m_pSoundTouch (NULL )
, m_pDS (NULL )
, m_dwDSWriteOff (0 )
, m_nDSBufSize (0 )
, m_dRate (1.0 )
, m_pReferenceClock (NULL )
, m_pWaveFileFormat (NULL )
, pMMDevice (NULL)
, pAudioClient (NULL )
, pRenderClient (NULL )
, m_useWASAPI (true )
, m_bMuteFastForward(false)
, m_csSound_Device (_T(""))
, nFramesInBuffer (0 )
, hnsPeriod (0 )
, hTask (NULL )
, bufferSize (0)
, isAudioClientStarted (false)
, lastBufferTime (0)
, hnsActualDuration (0)
, m_lVolume(DSBVOLUME_MIN)
{
HMODULE hLib;
#ifdef REGISTER_FILTER
CRegKey key;
TCHAR buff[256];
ULONG len;
if (ERROR_SUCCESS == key.Open(HKEY_CURRENT_USER, _T("Software\\Gabest\\Filters\\MPC Audio Renderer"), KEY_READ)) {
DWORD dw;
if (ERROR_SUCCESS == key.QueryDWORDValue(_T("UseWasapi"), dw)) {
m_useWASAPI = !!dw;
}
if (ERROR_SUCCESS == key.QueryDWORDValue(_T("MuteFastForward"), dw)) {
m_bMuteFastForward = !!dw;
}
len = sizeof(buff)/sizeof(buff[0]);
memset(buff, 0, sizeof(buff));
if (ERROR_SUCCESS == key.QueryStringValue(_T("SoundDevice"), buff, &len)) {
m_csSound_Device = CString(buff);
}
}
#else
m_useWASAPI = !!AfxGetApp()->GetProfileInt(_T("Filters\\MPC Audio Renderer"), _T("UseWasapi"), m_useWASAPI);
m_bMuteFastForward = !!AfxGetApp()->GetProfileInt(_T("Filters\\MPC Audio Renderer"), _T("MuteFastForward"), m_bMuteFastForward);
m_csSound_Device = AfxGetApp()->GetProfileString(_T("Filters\\MPC Audio Renderer"), _T("SoundDevice"), _T(""));
#endif
m_useWASAPIAfterRestart = m_useWASAPI;
// Load Vista specifics DLLs
hLib = LoadLibrary (L"AVRT.dll");
if (hLib != NULL) {
pfAvSetMmThreadCharacteristicsW = (PTR_AvSetMmThreadCharacteristicsW) GetProcAddress (hLib, "AvSetMmThreadCharacteristicsW");
pfAvRevertMmThreadCharacteristics = (PTR_AvRevertMmThreadCharacteristics) GetProcAddress (hLib, "AvRevertMmThreadCharacteristics");
} else {
m_useWASAPI = false; // Wasapi not available below Vista
}
TRACE(_T("CMpcAudioRenderer constructor\n"));
if (!m_useWASAPI) {
DirectSoundEnumerate((LPDSENUMCALLBACK)DSEnumProc2, (VOID*)&m_csSound_Device);
m_pSoundTouch = new soundtouch::SoundTouch();
*phr = DirectSoundCreate8 (&lpSoundGUID, &m_pDS, NULL);
}
}
CMpcAudioRenderer::~CMpcAudioRenderer()
{
Stop();
SAFE_DELETE (m_pSoundTouch);
SAFE_RELEASE (m_pDSBuffer);
SAFE_RELEASE (m_pDS);
SAFE_RELEASE (pRenderClient);
SAFE_RELEASE (pAudioClient);
SAFE_RELEASE (pMMDevice);
if (m_pReferenceClock) {
SetSyncSource(NULL);
SAFE_RELEASE (m_pReferenceClock);
}
if (m_pWaveFileFormat) {
BYTE *p = (BYTE *)m_pWaveFileFormat;
SAFE_DELETE_ARRAY(p);
}
if (hTask != NULL && pfAvRevertMmThreadCharacteristics != NULL) {
pfAvRevertMmThreadCharacteristics(hTask);
}
}
HRESULT CMpcAudioRenderer::CheckInputType(const CMediaType *pmt)
{
return CheckMediaType(pmt);
}
HRESULT CMpcAudioRenderer::CheckMediaType(const CMediaType *pmt)
{
HRESULT hr = S_OK;
if (pmt == NULL) {
return E_INVALIDARG;
}
TRACE(_T("CMpcAudioRenderer::CheckMediaType\n"));
WAVEFORMATEX *pwfx = (WAVEFORMATEX *) pmt->Format();
if (pwfx == NULL) {
return VFW_E_TYPE_NOT_ACCEPTED;
}
if ((pmt->majortype != MEDIATYPE_Audio ) ||
(pmt->formattype != FORMAT_WaveFormatEx )) {
TRACE(_T("CMpcAudioRenderer::CheckMediaType Not supported\n"));
return VFW_E_TYPE_NOT_ACCEPTED;
}
if (m_useWASAPI) {
hr=CheckAudioClient((WAVEFORMATEX *)NULL);
if (FAILED(hr)) {
TRACE(_T("CMpcAudioRenderer::CheckMediaType Error on check audio client\n"));
return hr;
}
if (!pAudioClient) {
TRACE(_T("CMpcAudioRenderer::CheckMediaType Error, audio client not loaded\n"));
return VFW_E_CANNOT_CONNECT;
}
if (pAudioClient->IsFormatSupported(AUDCLNT_SHAREMODE_EXCLUSIVE, pwfx, NULL) != S_OK) {
TRACE(_T("CMpcAudioRenderer::CheckMediaType WASAPI client refused the format\n"));
return VFW_E_TYPE_NOT_ACCEPTED;
}
TRACE(_T("CMpcAudioRenderer::CheckMediaType WASAPI client accepted the format\n"));
} else if (pwfx->wFormatTag != WAVE_FORMAT_PCM) {
return VFW_E_TYPE_NOT_ACCEPTED;
}
return S_OK;
}
void CMpcAudioRenderer::OnReceiveFirstSample(IMediaSample *pMediaSample)
{
if (!m_useWASAPI) {
ClearBuffer();
}
}
BOOL CMpcAudioRenderer::ScheduleSample(IMediaSample *pMediaSample)
{
REFERENCE_TIME StartSample;
REFERENCE_TIME EndSample;
// Is someone pulling our leg
if (pMediaSample == NULL) {
return FALSE;
}
// Get the next sample due up for rendering. If there aren't any ready
// then GetNextSampleTimes returns an error. If there is one to be done
// then it succeeds and yields the sample times. If it is due now then
// it returns S_OK other if it's to be done when due it returns S_FALSE
HRESULT hr = GetSampleTimes(pMediaSample, &StartSample, &EndSample);
if (FAILED(hr)) {
return FALSE;
}
// If we don't have a reference clock then we cannot set up the advise
// time so we simply set the event indicating an image to render. This
// will cause us to run flat out without any timing or synchronisation
if (hr == S_OK) {
EXECUTE_ASSERT(SetEvent((HANDLE) m_RenderEvent));
return TRUE;
}
if (m_dRate <= 1.1) {
ASSERT(m_dwAdvise == 0);
ASSERT(m_pClock);
WaitForSingleObject((HANDLE)m_RenderEvent,0);
hr = m_pClock->AdviseTime( (REFERENCE_TIME) m_tStart, StartSample, (HEVENT)(HANDLE) m_RenderEvent, &m_dwAdvise);
if (SUCCEEDED(hr)) {
return TRUE;
}
} else {
hr = DoRenderSample (pMediaSample);
}
// We could not schedule the next sample for rendering despite the fact
// we have a valid sample here. This is a fair indication that either
// the system clock is wrong or the time stamp for the sample is duff
ASSERT(m_dwAdvise == 0);
return FALSE;
}
HRESULT CMpcAudioRenderer::DoRenderSample(IMediaSample *pMediaSample)
{
if (m_useWASAPI) {
return DoRenderSampleWasapi(pMediaSample);
} else {
return DoRenderSampleDirectSound(pMediaSample);
}
}
STDMETHODIMP CMpcAudioRenderer::NonDelegatingQueryInterface(REFIID riid, void **ppv)
{
if (riid == IID_IReferenceClock) {
return GetReferenceClockInterface(riid, ppv);
} else if (riid == IID_IDispatch) {
return GetInterface(static_cast(this), ppv);
} else if (riid == IID_IBasicAudio) {
return GetInterface(static_cast(this), ppv);
} else if (riid == __uuidof(ISpecifyPropertyPages)) {
return GetInterface(static_cast(this), ppv);
} else if (riid == __uuidof(ISpecifyPropertyPages2)) {
return GetInterface(static_cast(this), ppv);
} else if (riid == __uuidof(IMpcAudioRendererFilter)) {
return GetInterface(static_cast(this), ppv);
}
return CBaseRenderer::NonDelegatingQueryInterface (riid, ppv);
}
HRESULT CMpcAudioRenderer::SetMediaType(const CMediaType *pmt)
{
if (! pmt) {
return E_POINTER;
}
TRACE(_T("CMpcAudioRenderer::SetMediaType\n"));
if (m_useWASAPI) {
// New media type set but render client already initialized => reset it
if (pRenderClient!=NULL) {
WAVEFORMATEX *pNewWf = (WAVEFORMATEX *) pmt->Format();
TRACE(_T("CMpcAudioRenderer::SetMediaType Render client already initialized. Reinitialization...\n"));
CheckAudioClient(pNewWf);
}
}
if (m_pWaveFileFormat) {
BYTE *p = (BYTE *)m_pWaveFileFormat;
SAFE_DELETE_ARRAY(p);
}
m_pWaveFileFormat=NULL;
WAVEFORMATEX *pwf = (WAVEFORMATEX *) pmt->Format();
if (pwf!=NULL) {
int size = sizeof(WAVEFORMATEX) + pwf->cbSize;
m_pWaveFileFormat = (WAVEFORMATEX *)new BYTE[size];
if (! m_pWaveFileFormat) {
return E_OUTOFMEMORY;
}
memcpy(m_pWaveFileFormat, pwf, size);
if (!m_useWASAPI && m_pSoundTouch && (pwf->nChannels <= 2)) {
m_pSoundTouch->setSampleRate (pwf->nSamplesPerSec);
m_pSoundTouch->setChannels (pwf->nChannels);
m_pSoundTouch->setTempoChange (0);
m_pSoundTouch->setPitchSemiTones(0);
}
}
return CBaseRenderer::SetMediaType (pmt);
}
HRESULT CMpcAudioRenderer::CompleteConnect(IPin *pReceivePin)
{
HRESULT hr = S_OK;
TRACE(_T("CMpcAudioRenderer::CompleteConnect\n"));
if (!m_useWASAPI && ! m_pDS) {
return E_FAIL;
}
if (SUCCEEDED(hr)) {
hr = CBaseRenderer::CompleteConnect(pReceivePin);
}
if (SUCCEEDED(hr)) {
hr = InitCoopLevel();
}
if (!m_useWASAPI) {
if (SUCCEEDED(hr)) {
hr = CreateDSBuffer();
}
}
if (SUCCEEDED(hr)) {
TRACE(_T("CMpcAudioRenderer::CompleteConnect Success\n"));
}
return hr;
}
STDMETHODIMP CMpcAudioRenderer::Run(REFERENCE_TIME tStart)
{
HRESULT hr;
if (m_State == State_Running) {
return NOERROR;
}
if (m_useWASAPI) {
hr=CheckAudioClient(m_pWaveFileFormat);
if (FAILED(hr)) {
TRACE(_T("CMpcAudioRenderer::Run Error on check audio client\n"));
return hr;
}
// Rather start the client at the last moment when the buffer is fed
/*hr = pAudioClient->Start();
if (FAILED (hr))
{
TRACE(_T("CMpcAudioRenderer::Run Start error"));
return hr;
}*/
} else {
if (m_pDSBuffer &&
m_pPosition &&
m_pWaveFileFormat &&
SUCCEEDED(m_pPosition->GetRate(&m_dRate))) {
if (m_dRate < 1.0) {
hr = m_pDSBuffer->SetFrequency ((long)(m_pWaveFileFormat->nSamplesPerSec * m_dRate));
if (FAILED (hr)) {
return hr;
}
} else {
hr = m_pDSBuffer->SetFrequency ((long)m_pWaveFileFormat->nSamplesPerSec);
m_pSoundTouch->setRateChange((float)(m_dRate-1.0)*100);
if (m_bMuteFastForward) {
if (m_dRate == 1.0) {
m_pDSBuffer->SetVolume(m_lVolume);
} else {
m_pDSBuffer->SetVolume(DSBVOLUME_MIN);
}
}
}
}
ClearBuffer();
}
hr = CBaseRenderer::Run(tStart);
return hr;
}
STDMETHODIMP CMpcAudioRenderer::Stop()
{
if (m_pDSBuffer) {
m_pDSBuffer->Stop();
}
isAudioClientStarted=false;
return CBaseRenderer::Stop();
};
STDMETHODIMP CMpcAudioRenderer::Pause()
{
if (m_pDSBuffer) {
m_pDSBuffer->Stop();
}
if (pAudioClient && isAudioClientStarted) {
pAudioClient->Stop();
}
isAudioClientStarted=false;
return CBaseRenderer::Pause();
};
// === IDispatch
STDMETHODIMP CMpcAudioRenderer::GetTypeInfoCount(UINT * pctinfo)
{
return E_NOTIMPL;
}
STDMETHODIMP CMpcAudioRenderer::GetTypeInfo(UINT itinfo, LCID lcid, ITypeInfo** pptinfo)
{
return E_NOTIMPL;
}
STDMETHODIMP CMpcAudioRenderer::GetIDsOfNames(REFIID riid, OLECHAR** rgszNames, UINT cNames, LCID lcid, DISPID* rgdispid)
{
return E_NOTIMPL;
}
STDMETHODIMP CMpcAudioRenderer::Invoke(DISPID dispidMember, REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS* pdispparams, VARIANT* pvarResult, EXCEPINFO* pexcepinfo, UINT* puArgErr)
{
return E_NOTIMPL;
}
// === IBasicAudio
STDMETHODIMP CMpcAudioRenderer::put_Volume(long lVolume)
{
m_lVolume = lVolume;
if (!m_useWASAPI && m_pDSBuffer) {
return m_pDSBuffer->SetVolume(lVolume);
}
return S_OK;
}
STDMETHODIMP CMpcAudioRenderer::get_Volume(long *plVolume)
{
if (!m_useWASAPI && m_pDSBuffer) {
return m_pDSBuffer->GetVolume(plVolume);
}
return S_OK;
}
STDMETHODIMP CMpcAudioRenderer::put_Balance(long lBalance)
{
if (!m_useWASAPI && m_pDSBuffer) {
return m_pDSBuffer->SetPan(lBalance);
}
return S_OK;
}
STDMETHODIMP CMpcAudioRenderer::get_Balance(long *plBalance)
{
if (!m_useWASAPI && m_pDSBuffer) {
return m_pDSBuffer->GetPan(plBalance);
}
return S_OK;
}
// === ISpecifyPropertyPages2
STDMETHODIMP CMpcAudioRenderer::GetPages(CAUUID* pPages)
{
CheckPointer(pPages, E_POINTER);
pPages->cElems = 1;
pPages->pElems = (GUID*)CoTaskMemAlloc(sizeof(GUID) * pPages->cElems);
pPages->pElems[0] = __uuidof(CMpcAudioRendererSettingsWnd);
return S_OK;
}
STDMETHODIMP CMpcAudioRenderer::CreatePage(const GUID& guid, IPropertyPage** ppPage)
{
CheckPointer(ppPage, E_POINTER);
if (*ppPage != NULL) {
return E_INVALIDARG;
}
HRESULT hr;
if (guid == __uuidof(CMpcAudioRendererSettingsWnd)) {
(*ppPage = DNew CInternalPropertyPageTempl(NULL, &hr))->AddRef();
}
return *ppPage ? S_OK : E_FAIL;
}
// === IMpcAudioRendererFilter
STDMETHODIMP CMpcAudioRenderer::Apply()
{
#ifdef REGISTER_FILTER
CRegKey key;
if (ERROR_SUCCESS == key.Create(HKEY_CURRENT_USER, _T("Software\\Gabest\\Filters\\MPC Audio Renderer"))) {
key.SetDWORDValue(_T("UseWasapi"), m_useWASAPIAfterRestart);
key.SetDWORDValue(_T("MuteFastForward"), m_bMuteFastForward);
key.SetStringValue(_T("SoundDevice"), m_csSound_Device);
}
#else
AfxGetApp()->WriteProfileInt(_T("Filters\\MPC Audio Renderer"), _T("UseWasapi"), m_useWASAPI);
AfxGetApp()->WriteProfileInt(_T("Filters\\MPC Audio Renderer"), _T("MuteFastForward"), m_bMuteFastForward);
AfxGetApp()->WriteProfileString(_T("Filters\\MPC Audio Renderer"), _T("SoundDevice"), m_csSound_Device);
#endif
return S_OK;
}
STDMETHODIMP CMpcAudioRenderer::SetWasapiMode(BOOL nValue)
{
CAutoLock cAutoLock(&m_csProps);
m_useWASAPIAfterRestart = !!nValue;
return S_OK;
}
STDMETHODIMP_(BOOL) CMpcAudioRenderer::GetWasapiMode()
{
CAutoLock cAutoLock(&m_csProps);
return m_useWASAPIAfterRestart;
}
STDMETHODIMP CMpcAudioRenderer::SetMuteFastForward(BOOL nValue)
{
CAutoLock cAutoLock(&m_csProps);
m_bMuteFastForward = !!nValue;
return S_OK;
}
STDMETHODIMP_(BOOL) CMpcAudioRenderer::GetMuteFastForward()
{
CAutoLock cAutoLock(&m_csProps);
return m_bMuteFastForward;
}
STDMETHODIMP CMpcAudioRenderer::SetSoundDevice(CString nValue)
{
CAutoLock cAutoLock(&m_csProps);
m_csSound_Device = nValue;
return S_OK;
}
STDMETHODIMP_(CString) CMpcAudioRenderer::GetSoundDevice()
{
CAutoLock cAutoLock(&m_csProps);
return m_csSound_Device;
}
HRESULT CMpcAudioRenderer::GetReferenceClockInterface(REFIID riid, void **ppv)
{
HRESULT hr = S_OK;
if (m_pReferenceClock) {
return m_pReferenceClock->NonDelegatingQueryInterface(riid, ppv);
}
m_pReferenceClock = new CBaseReferenceClock (NAME("Mpc Audio Clock"), NULL, &hr);
if (! m_pReferenceClock) {
return E_OUTOFMEMORY;
}
m_pReferenceClock->AddRef();
hr = SetSyncSource(m_pReferenceClock);
if (FAILED(hr)) {
SetSyncSource(NULL);
return hr;
}
return GetReferenceClockInterface(riid, ppv);
}
HRESULT CMpcAudioRenderer::EndOfStream(void)
{
if (m_pDSBuffer) {
m_pDSBuffer->Stop();
}
#if !FILEWRITER
if (pAudioClient && isAudioClientStarted) {
pAudioClient->Stop();
}
#endif
isAudioClientStarted=false;
return CBaseRenderer::EndOfStream();
}
#pragma region // ==== DirectSound
HRESULT CMpcAudioRenderer::CreateDSBuffer()
{
if (! m_pWaveFileFormat) {
return E_POINTER;
}
HRESULT hr = S_OK;
LPDIRECTSOUNDBUFFER pDSBPrimary = NULL;
DSBUFFERDESC dsbd;
DSBUFFERDESC cDSBufferDesc;
DSBCAPS bufferCaps;
DWORD dwDSBufSize = m_pWaveFileFormat->nAvgBytesPerSec * 4;
ZeroMemory(&bufferCaps, sizeof(bufferCaps));
ZeroMemory(&dsbd, sizeof(DSBUFFERDESC));
dsbd.dwSize = sizeof(DSBUFFERDESC);
dsbd.dwFlags = DSBCAPS_PRIMARYBUFFER;
dsbd.dwBufferBytes = 0;
dsbd.lpwfxFormat = NULL;
if (SUCCEEDED (hr = m_pDS->CreateSoundBuffer( &dsbd, &pDSBPrimary, NULL ))) {
hr = pDSBPrimary->SetFormat(m_pWaveFileFormat);
ATLASSERT(SUCCEEDED(hr));
SAFE_RELEASE (pDSBPrimary);
}
SAFE_RELEASE (m_pDSBuffer);
cDSBufferDesc.dwSize = sizeof (DSBUFFERDESC);
cDSBufferDesc.dwFlags = DSBCAPS_GLOBALFOCUS |
DSBCAPS_GETCURRENTPOSITION2 |
DSBCAPS_CTRLVOLUME |
DSBCAPS_CTRLPAN |
DSBCAPS_CTRLFREQUENCY;
cDSBufferDesc.dwBufferBytes = dwDSBufSize;
cDSBufferDesc.dwReserved = 0;
cDSBufferDesc.lpwfxFormat = m_pWaveFileFormat;
cDSBufferDesc.guid3DAlgorithm = GUID_NULL;
hr = m_pDS->CreateSoundBuffer (&cDSBufferDesc, &m_pDSBuffer, NULL);
m_nDSBufSize = 0;
if (SUCCEEDED(hr)) {
bufferCaps.dwSize = sizeof(bufferCaps);
hr = m_pDSBuffer->GetCaps(&bufferCaps);
}
if (SUCCEEDED (hr)) {
m_nDSBufSize = bufferCaps.dwBufferBytes;
hr = ClearBuffer();
m_pDSBuffer->SetFrequency ((long)(m_pWaveFileFormat->nSamplesPerSec * m_dRate));
}
return hr;
}
HRESULT CMpcAudioRenderer::ClearBuffer()
{
HRESULT hr = S_FALSE;
VOID* pDSLockedBuffer = NULL;
DWORD dwDSLockedSize = 0;
if (m_pDSBuffer) {
m_dwDSWriteOff = 0;
m_pDSBuffer->SetCurrentPosition(0);
hr = m_pDSBuffer->Lock (0, 0, &pDSLockedBuffer, &dwDSLockedSize, NULL, NULL, DSBLOCK_ENTIREBUFFER);
if (SUCCEEDED (hr)) {
memset (pDSLockedBuffer, 0, dwDSLockedSize);
hr = m_pDSBuffer->Unlock (pDSLockedBuffer, dwDSLockedSize, NULL, NULL);
}
}
return hr;
}
HRESULT CMpcAudioRenderer::InitCoopLevel()
{
HRESULT hr = S_OK;
IVideoWindow* pVideoWindow = NULL;
HWND hWnd = NULL;
CComBSTR bstrCaption;
hr = m_pGraph->QueryInterface (__uuidof(IVideoWindow), (void**) &pVideoWindow);
if (SUCCEEDED (hr)) {
pVideoWindow->get_Owner((OAHWND*)&hWnd);
SAFE_RELEASE (pVideoWindow);
}
if (!hWnd) {
hWnd = GetTopWindow(NULL);
}
ATLASSERT(hWnd != NULL);
if (!m_useWASAPI) {
hr = m_pDS->SetCooperativeLevel(hWnd, DSSCL_PRIORITY);
} else if (hTask == NULL) {
// Ask MMCSS to temporarily boost the thread priority
// to reduce glitches while the low-latency stream plays.
DWORD taskIndex = 0;
if (pfAvSetMmThreadCharacteristicsW) {
hTask = pfAvSetMmThreadCharacteristicsW(TEXT("Pro Audio"), &taskIndex);
TRACE(_T("CMpcAudioRenderer::InitCoopLevel Putting thread in higher priority for Wasapi mode (lowest latency)\n"));
hr=GetLastError();
if (hTask == NULL) {
return hr;
}
}
}
return hr;
}
HRESULT CMpcAudioRenderer::DoRenderSampleDirectSound(IMediaSample *pMediaSample)
{
HRESULT hr = S_OK;
DWORD dwStatus = 0;
const long lSize = pMediaSample->GetActualDataLength();
DWORD dwPlayCursor = 0;
DWORD dwWriteCursor = 0;
hr = m_pDSBuffer->GetStatus(&dwStatus);
if (SUCCEEDED(hr) && (dwStatus & DSBSTATUS_BUFFERLOST)) {
hr = m_pDSBuffer->Restore();
}
if ((SUCCEEDED(hr)) && ((dwStatus & DSBSTATUS_PLAYING) != DSBSTATUS_PLAYING)) {
hr = m_pDSBuffer->Play( 0, 0, DSBPLAY_LOOPING);
ATLASSERT(SUCCEEDED(hr));
}
if (SUCCEEDED(hr)) {
hr = m_pDSBuffer->GetCurrentPosition(&dwPlayCursor, &dwWriteCursor);
}
if (SUCCEEDED(hr)) {
if ( ( (dwPlayCursor < dwWriteCursor) &&
(
((m_dwDSWriteOff >= dwPlayCursor) && (m_dwDSWriteOff <= dwWriteCursor))
||
((m_dwDSWriteOff < dwPlayCursor) && (m_dwDSWriteOff + lSize >= dwPlayCursor))
)
)
||
( (dwWriteCursor < dwPlayCursor) &&
(
(m_dwDSWriteOff >= dwPlayCursor) || (m_dwDSWriteOff < dwWriteCursor)
) ) ) {
m_dwDSWriteOff = dwWriteCursor;
}
if (m_dwDSWriteOff >= (DWORD)m_nDSBufSize) {
m_dwDSWriteOff = 0;
}
}
if (SUCCEEDED(hr)) {
hr = WriteSampleToDSBuffer(pMediaSample, NULL);
}
return hr;
}
HRESULT CMpcAudioRenderer::WriteSampleToDSBuffer(IMediaSample *pMediaSample, bool *looped)
{
if (! m_pDSBuffer) {
return E_POINTER;
}
REFERENCE_TIME rtStart = 0;
REFERENCE_TIME rtStop = 0;
HRESULT hr = S_OK;
bool loop = false;
VOID* pDSLockedBuffers[2] = { NULL, NULL };
DWORD dwDSLockedSize[2] = { 0, 0 };
BYTE* pMediaBuffer = NULL;
long lSize = pMediaSample->GetActualDataLength();
hr = pMediaSample->GetPointer(&pMediaBuffer);
if (m_dRate > 1.0) {
int nBytePerSample = m_pWaveFileFormat->nChannels * (m_pWaveFileFormat->wBitsPerSample/8);
m_pSoundTouch->putSamples((const short*)pMediaBuffer, lSize / nBytePerSample);
lSize = m_pSoundTouch->receiveSamples ((short*)pMediaBuffer, lSize / nBytePerSample) * nBytePerSample;
}
pMediaSample->GetTime (&rtStart, &rtStop);
if (rtStart < 0) {
DWORD dwPercent = (DWORD) ((-rtStart * 100) / (rtStop - rtStart));
DWORD dwRemove = (lSize*dwPercent/100);
dwRemove = (dwRemove / m_pWaveFileFormat->nBlockAlign) * m_pWaveFileFormat->nBlockAlign;
pMediaBuffer += dwRemove;
lSize -= dwRemove;
}
if (SUCCEEDED (hr)) {
hr = m_pDSBuffer->Lock (m_dwDSWriteOff, lSize, &pDSLockedBuffers[0], &dwDSLockedSize[0], &pDSLockedBuffers[1], &dwDSLockedSize[1], 0 );
}
if (SUCCEEDED (hr)) {
if (pDSLockedBuffers [0] != NULL) {
memcpy(pDSLockedBuffers[0], pMediaBuffer, dwDSLockedSize[0]);
m_dwDSWriteOff += dwDSLockedSize[0];
}
if (pDSLockedBuffers [1] != NULL) {
memcpy(pDSLockedBuffers[1], &pMediaBuffer[dwDSLockedSize[0]], dwDSLockedSize[1]);
m_dwDSWriteOff = dwDSLockedSize[1];
loop = true;
}
hr = m_pDSBuffer->Unlock(pDSLockedBuffers[0], dwDSLockedSize[0], pDSLockedBuffers[1], dwDSLockedSize[1]);
ATLASSERT (dwDSLockedSize [0] + dwDSLockedSize [1] == (DWORD)lSize);
}
if (SUCCEEDED(hr) && looped) {
*looped = loop;
}
return hr;
}
#pragma endregion
#pragma region // ==== WASAPI
HRESULT CMpcAudioRenderer::DoRenderSampleWasapi(IMediaSample *pMediaSample)
{
HRESULT hr = S_OK;
REFERENCE_TIME rtStart = 0;
REFERENCE_TIME rtStop = 0;
BYTE *pMediaBuffer = NULL;
BYTE *pInputBufferPointer = NULL;
BYTE *pInputBufferEnd = NULL;
BYTE *pData;
bufferSize = pMediaSample->GetActualDataLength();
const long lSize = bufferSize;
pMediaSample->GetTime (&rtStart, &rtStop);
AM_MEDIA_TYPE *pmt;
if (SUCCEEDED(pMediaSample->GetMediaType(&pmt)) && pmt!=NULL) {
CMediaType mt(*pmt);
if ((WAVEFORMATEXTENSIBLE*)mt.Format() != NULL) {
hr=CheckAudioClient(&(((WAVEFORMATEXTENSIBLE*)mt.Format())->Format));
} else {
hr=CheckAudioClient((WAVEFORMATEX*)mt.Format());
}
if (FAILED(hr)) {
TRACE(_T("CMpcAudioRenderer::DoRenderSampleWasapi Error while checking audio client with input media type\n"));
return hr;
}
DeleteMediaType(pmt);
pmt=NULL;
}
// Initialization
hr = pMediaSample->GetPointer(&pMediaBuffer);
if (FAILED (hr)) {
return hr;
}
pInputBufferPointer=&pMediaBuffer[0];
pInputBufferEnd=&pMediaBuffer[0]+lSize;
WORD frameSize = m_pWaveFileFormat->nBlockAlign;
// Sleep for half the buffer duration since last buffer feed
DWORD currentTime=GetTickCount();
if (lastBufferTime!=0 && hnsActualDuration!= 0 && lastBufferTimeGetCurrentPadding(&numFramesPadding);
UINT32 numFramesAvailable = nFramesInBuffer - numFramesPadding;
UINT32 nAvailableBytes=numFramesAvailable*frameSize;
UINT32 nBytesToWrite=nAvailableBytes;
// More room than enough in the output buffer
if (nAvailableBytes > (size_t)(pInputBufferEnd - pInputBufferPointer)) {
nBytesToWrite=pInputBufferEnd - pInputBufferPointer;
numFramesAvailable=(UINT32)((float)nBytesToWrite/frameSize);
}
// Grab the next empty buffer from the audio device.
hr = pRenderClient->GetBuffer(numFramesAvailable, &pData);
if (FAILED (hr)) {
TRACE(_T("CMpcAudioRenderer::DoRenderSampleWasapi GetBuffer failed with size %ld : (error %lx)\n"),nFramesInBuffer,hr);
return hr;
}
// Load the buffer with data from the audio source.
if (pData != NULL) {
memcpy(&pData[0], pInputBufferPointer, nBytesToWrite);
pInputBufferPointer += nBytesToWrite;
} else {
TRACE(_T("CMpcAudioRenderer::DoRenderSampleWasapi Output buffer is NULL\n"));
}
hr = pRenderClient->ReleaseBuffer(numFramesAvailable, 0); // no flags
if (FAILED (hr)) {
TRACE(_T("CMpcAudioRenderer::DoRenderSampleWasapi ReleaseBuffer failed with size %ld (error %lx)\n"),nFramesInBuffer,hr);
return hr;
}
if (!isAudioClientStarted) {
TRACE(_T("CMpcAudioRenderer::DoRenderSampleWasapi Starting audio client\n"));
pAudioClient->Start();
isAudioClientStarted=true;
}
if (pInputBufferPointer >= pInputBufferEnd) {
lastBufferTime=GetTickCount();
// This is the duration of the filled buffer
hnsActualDuration=(double)REFTIMES_PER_SEC * numFramesAvailable / m_pWaveFileFormat->nSamplesPerSec;
// Sleep time is half this duration
hnsActualDuration=(DWORD)(hnsActualDuration/REFTIMES_PER_MILLISEC/2);
break;
}
// Buffer not completely filled, sleep for half buffer capacity duration
hnsActualDuration=(double)REFTIMES_PER_SEC * nFramesInBuffer / m_pWaveFileFormat->nSamplesPerSec;
// Sleep time is half this duration
hnsActualDuration=(DWORD)(hnsActualDuration/REFTIMES_PER_MILLISEC/2);
Sleep(hnsActualDuration);
}
return hr;
}
#pragma endregion
HRESULT CMpcAudioRenderer::CheckAudioClient(WAVEFORMATEX *pWaveFormatEx)
{
HRESULT hr = S_OK;
CAutoLock cAutoLock(&m_csCheck);
TRACE(_T("CMpcAudioRenderer::CheckAudioClient\n"));
if (pMMDevice == NULL) {
hr = GetAudioDevice(&pMMDevice);
}
// If no WAVEFORMATEX structure provided and client already exists, return it
if (pAudioClient != NULL && pWaveFormatEx == NULL) {
return hr;
}
// Just create the audio client if no WAVEFORMATEX provided
if (pAudioClient == NULL && pWaveFormatEx==NULL) {
if (SUCCEEDED (hr)) {
hr=CreateAudioClient(pMMDevice, &pAudioClient);
}
return hr;
}
// Compare the exisiting WAVEFORMATEX with the one provided
WAVEFORMATEX *pNewWaveFormatEx = NULL;
if (CheckFormatChanged(pWaveFormatEx, &pNewWaveFormatEx)) {
// Format has changed, audio client has to be reinitialized
TRACE(_T("CMpcAudioRenderer::CheckAudioClient Format changed, reinitialize the audio client\n"));
if (m_pWaveFileFormat) {
BYTE *p = (BYTE *)m_pWaveFileFormat;
SAFE_DELETE_ARRAY(p);
}
m_pWaveFileFormat=pNewWaveFormatEx;
hr = pAudioClient->IsFormatSupported(AUDCLNT_SHAREMODE_EXCLUSIVE, pWaveFormatEx, NULL);
if (SUCCEEDED(hr)) {
if (pAudioClient!=NULL && isAudioClientStarted) {
pAudioClient->Stop();
}
isAudioClientStarted=false;
SAFE_RELEASE(pRenderClient);
SAFE_RELEASE(pAudioClient);
if (SUCCEEDED (hr)) {
hr=CreateAudioClient(pMMDevice, &pAudioClient);
}
} else {
TRACE(_T("CMpcAudioRenderer::CheckAudioClient New format not supported, accept it anyway\n"));
return S_OK;
}
} else if (pRenderClient == NULL) {
TRACE(_T("CMpcAudioRenderer::CheckAudioClient First initialization of the audio renderer\n"));
} else {
return hr;
}
SAFE_RELEASE(pRenderClient);
if (SUCCEEDED (hr)) {
hr=InitAudioClient(pWaveFormatEx, pAudioClient, &pRenderClient);
}
return hr;
}
HRESULT CMpcAudioRenderer::GetAvailableAudioDevices(IMMDeviceCollection **ppMMDevices)
{
HRESULT hr;
CComPtr enumerator;
TRACE(_T("CMpcAudioRenderer::GetAvailableAudioDevices\n"));
hr = enumerator.CoCreateInstance(__uuidof(MMDeviceEnumerator));
if (FAILED(hr))
{
TRACE(_T("CMpcAudioRenderer::GetAvailableAudioDevices - failed to get MMDeviceEnumerator\n"));
return S_FALSE;
}
//IMMDevice* pEndpoint = NULL;
//IPropertyStore* pProps = NULL;
//LPWSTR pwszID = NULL;
enumerator->EnumAudioEndpoints(eRender, DEVICE_STATE_ACTIVE, ppMMDevices);
UINT count(0);
hr = (*ppMMDevices)->GetCount(&count);
return hr;
}
HRESULT CMpcAudioRenderer::GetAudioDevice(IMMDevice **ppMMDevice)
{
TRACE(_T("CMpcAudioRenderer::GetAudioDevice\n"));
CComPtr enumerator;
IMMDeviceCollection* devices;
IPropertyStore* pProps = NULL;
HRESULT hr = enumerator.CoCreateInstance(__uuidof(MMDeviceEnumerator));
if (hr != S_OK)
{
TRACE(_T("CMpcAudioRenderer::GetAudioDevice - failed to create MMDeviceEnumerator!\n"));
return hr;
}
TRACE(_T("CMpcAudioRenderer::GetAudioDevice - Target end point: %s\n"), m_csSound_Device);
if (GetAvailableAudioDevices(&devices) == S_OK && devices)
{
UINT count(0);
hr = devices->GetCount(&count);
if (hr != S_OK)
{
TRACE(_T("CMpcAudioRenderer::GetAudioDevice - devices->GetCount failed: (0x%08x)\n"), hr);
return hr;
}
for (ULONG i = 0 ; i < count ; i++)
{
LPWSTR pwszID = NULL;
IMMDevice *endpoint = NULL;
hr = devices->Item(i, &endpoint);
if (hr == S_OK)
{
hr = endpoint->GetId(&pwszID);
if (hr == S_OK)
{
if (endpoint->OpenPropertyStore(STGM_READ, &pProps) == S_OK)
{
PROPVARIANT varName;
PropVariantInit(&varName);
// Found the configured audio endpoint
if ((pProps->GetValue(PKEY_Device_FriendlyName, &varName) == S_OK) && (m_csSound_Device == varName.pwszVal))
{
TRACE(_T("CMpcAudioRenderer::GetAudioDevice - devices->GetId OK, num: (%d), pwszVal: %s, pwszID: %s\n"), i, varName.pwszVal, pwszID);
enumerator->GetDevice(pwszID, ppMMDevice);
SAFE_RELEASE(devices);
*(ppMMDevice) = endpoint;
CoTaskMemFree(pwszID);
pwszID = NULL;
PropVariantClear(&varName);
SAFE_RELEASE(pProps);
return S_OK;
}
else
{
PropVariantClear(&varName);
SAFE_RELEASE(pProps);
SAFE_RELEASE(endpoint);
CoTaskMemFree(pwszID);
pwszID = NULL;
}
}
}
else
{
TRACE(_T("CMpcAudioRenderer::GetAudioDevice - devices->GetId failed: (0x%08x)\n"), hr);
}
}
else
{
TRACE(_T("CMpcAudioRenderer::GetAudioDevice - devices->Item failed: (0x%08x)\n"), hr);
}
CoTaskMemFree(pwszID);
pwszID = NULL;
}
}
TRACE(_T("CMpcAudioRenderer::GetAudioDevice - Unable to find selected audio device, using the default end point!\n"));
hr = enumerator->GetDefaultAudioEndpoint(eRender, eConsole, ppMMDevice);
SAFE_RELEASE(devices);
return hr;
}
bool CMpcAudioRenderer::CheckFormatChanged(WAVEFORMATEX *pWaveFormatEx, WAVEFORMATEX **ppNewWaveFormatEx)
{
bool formatChanged=false;
if (m_pWaveFileFormat==NULL) {
formatChanged=true;
} else if (pWaveFormatEx->wFormatTag != m_pWaveFileFormat->wFormatTag
|| pWaveFormatEx->nChannels != m_pWaveFileFormat->nChannels
|| pWaveFormatEx->wBitsPerSample != m_pWaveFileFormat->wBitsPerSample) { // TODO : improve the checks
formatChanged=true;
}
if (!formatChanged) {
return false;
}
int size = sizeof(WAVEFORMATEX) + pWaveFormatEx->cbSize; // Always true, even for WAVEFORMATEXTENSIBLE and WAVEFORMATEXTENSIBLE_IEC61937
*ppNewWaveFormatEx = (WAVEFORMATEX *)new BYTE[size];
if (! *ppNewWaveFormatEx) {
return false;
}
memcpy(*ppNewWaveFormatEx, pWaveFormatEx, size);
return true;
}
HRESULT CMpcAudioRenderer::GetBufferSize(WAVEFORMATEX *pWaveFormatEx, REFERENCE_TIME *pHnsBufferPeriod)
{
if (pWaveFormatEx==NULL) {
return S_OK;
}
if (pWaveFormatEx->cbSize <22) { //WAVEFORMATEX
return S_OK;
}
WAVEFORMATEXTENSIBLE *wfext=(WAVEFORMATEXTENSIBLE*)pWaveFormatEx;
if (bufferSize==0)
if (wfext->SubFormat==KSDATAFORMAT_SUBTYPE_IEC61937_DOLBY_MLP) {
bufferSize=61440;
} else if (wfext->SubFormat==KSDATAFORMAT_SUBTYPE_IEC61937_DTS_HD) {
bufferSize=32768;
} else if (wfext->SubFormat==KSDATAFORMAT_SUBTYPE_IEC61937_DOLBY_DIGITAL_PLUS) {
bufferSize=24576;
} else if (wfext->Format.wFormatTag==WAVE_FORMAT_DOLBY_AC3_SPDIF) {
bufferSize=6144;
} else {
return S_OK;
}
*pHnsBufferPeriod = (REFERENCE_TIME)( (REFERENCE_TIME)bufferSize * 10000 * 8 / ((REFERENCE_TIME)pWaveFormatEx->nChannels * pWaveFormatEx->wBitsPerSample *
1.0 * pWaveFormatEx->nSamplesPerSec) /*+ 0.5*/);
*pHnsBufferPeriod *= 1000;
TRACE(_T("CMpcAudioRenderer::GetBufferSize set a %lld period for a %ld buffer size\n"),*pHnsBufferPeriod,bufferSize);
return S_OK;
}
HRESULT CMpcAudioRenderer::InitAudioClient(WAVEFORMATEX *pWaveFormatEx, IAudioClient *pAudioClient, IAudioRenderClient **ppRenderClient)
{
TRACE(_T("CMpcAudioRenderer::InitAudioClient\n"));
HRESULT hr=S_OK;
// Initialize the stream to play at the minimum latency.
//if (SUCCEEDED (hr)) hr = pAudioClient->GetDevicePeriod(NULL, &hnsPeriod);
hnsPeriod=500000; //50 ms is the best according to James @Slysoft
hr = pAudioClient->IsFormatSupported(AUDCLNT_SHAREMODE_EXCLUSIVE, pWaveFormatEx, NULL);
if (FAILED(hr)) {
TRACE(_T("CMpcAudioRenderer::InitAudioClient not supported (0x%08x)\n"), hr);
} else {
TRACE(_T("CMpcAudioRenderer::InitAudioClient format supported\n"));
}
GetBufferSize(pWaveFormatEx, &hnsPeriod);
if (SUCCEEDED (hr)) hr = pAudioClient->Initialize(AUDCLNT_SHAREMODE_EXCLUSIVE,0/*AUDCLNT_STREAMFLAGS_EVENTCALLBACK*/,
hnsPeriod,hnsPeriod,pWaveFormatEx,NULL);
if (FAILED (hr) && hr != AUDCLNT_E_BUFFER_SIZE_NOT_ALIGNED) {
TRACE(_T("CMpcAudioRenderer::InitAudioClient failed (0x%08x)\n"), hr);
return hr;
}
if (AUDCLNT_E_BUFFER_SIZE_NOT_ALIGNED == hr) {
// if the buffer size was not aligned, need to do the alignment dance
TRACE(_T("CMpcAudioRenderer::InitAudioClient Buffer size not aligned. Realigning\n"));
// get the buffer size, which will be aligned
hr = pAudioClient->GetBufferSize(&nFramesInBuffer);
// throw away this IAudioClient
pAudioClient->Release();
pAudioClient=NULL;
// calculate the new aligned periodicity
hnsPeriod = // hns =
(REFERENCE_TIME)(
10000.0 * // (hns / ms) *
1000 * // (ms / s) *
nFramesInBuffer / // frames /
pWaveFormatEx->nSamplesPerSec // (frames / s)
+ 0.5 // rounding
);
if (SUCCEEDED (hr)) {
hr=CreateAudioClient(pMMDevice, &pAudioClient);
}
TRACE(_T("CMpcAudioRenderer::InitAudioClient Trying again with periodicity of %I64u hundred-nanoseconds, or %u frames.\n"), hnsPeriod, nFramesInBuffer);
if (SUCCEEDED (hr))
hr = pAudioClient->Initialize(AUDCLNT_SHAREMODE_EXCLUSIVE,0/*AUDCLNT_STREAMFLAGS_EVENTCALLBACK*/,
hnsPeriod, hnsPeriod, pWaveFormatEx, NULL);
if (FAILED(hr)) {
TRACE(_T("CMpcAudioRenderer::InitAudioClient Failed to reinitialize the audio client\n"));
return hr;
}
} // if (AUDCLNT_E_BUFFER_SIZE_NOT_ALIGNED == hr)
// get the buffer size, which is aligned
if (SUCCEEDED (hr)) {
hr = pAudioClient->GetBufferSize(&nFramesInBuffer);
}
// calculate the new period
if (SUCCEEDED (hr)) {
hr = pAudioClient->GetService(__uuidof(IAudioRenderClient), (void**)(ppRenderClient));
}
if (FAILED (hr)) {
TRACE(_T("CMpcAudioRenderer::InitAudioClient service initialization failed (0x%08x)\n"), hr);
} else {
TRACE(_T("CMpcAudioRenderer::InitAudioClient service initialization success\n"));
}
return hr;
}
HRESULT CMpcAudioRenderer::CreateAudioClient(IMMDevice *pMMDevice, IAudioClient **ppAudioClient)
{
HRESULT hr = S_OK;
hnsPeriod = 0;
TRACE(_T("CMpcAudioRenderer::CreateAudioClient\n"));
if (*ppAudioClient) {
if (isAudioClientStarted) {
(*ppAudioClient)->Stop();
}
SAFE_RELEASE(*ppAudioClient);
isAudioClientStarted=false;
}
if (pMMDevice==NULL) {
TRACE(_T("CMpcAudioRenderer::CreateAudioClient failed, device not loaded\n"));
return E_FAIL;
}
hr = pMMDevice->Activate(__uuidof(IAudioClient), CLSCTX_ALL, NULL, reinterpret_cast(ppAudioClient));
if (FAILED(hr)) {
TRACE(_T("CMpcAudioRenderer::CreateAudioClient activation failed (0x%08x)\n"), hr);
} else {
TRACE(_T("CMpcAudioRenderer::CreateAudioClient success\n"));
}
return hr;
}