359 lines
9.6 KiB
C++
359 lines
9.6 KiB
C++
/*
|
|
This file is part of EqualizerAPO, a system-wide equalizer.
|
|
Copyright (C) 2012 Jonas Thedering
|
|
|
|
This program 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 2 of the License, or
|
|
(at your option) any later version.
|
|
|
|
This program 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, write to the Free Software Foundation, Inc.,
|
|
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
*/
|
|
|
|
#include <string>
|
|
#include <Unknwn.h>
|
|
#define INITGUID
|
|
#include <mmdeviceapi.h>
|
|
|
|
#include "LogHelper.h"
|
|
#include "RegistryHelper.h"
|
|
#include "DeviceAPOInfo.h"
|
|
#include "EqualizerAPO.h"
|
|
|
|
using namespace std;
|
|
|
|
long EqualizerAPO::instCount = 0;
|
|
const CRegAPOProperties<1> EqualizerAPO::regProperties(
|
|
__uuidof(EqualizerAPO), L"EqualizerAPO", L"Copyright (C) 2012", 1, 0, __uuidof(IAudioProcessingObject),
|
|
(APO_FLAG)( APO_FLAG_FRAMESPERSECOND_MUST_MATCH | APO_FLAG_BITSPERSAMPLE_MUST_MATCH | APO_FLAG_INPLACE));
|
|
|
|
EqualizerAPO::EqualizerAPO(IUnknown* pUnkOuter)
|
|
:CBaseAudioProcessingObject(regProperties)
|
|
{
|
|
refCount = 1;
|
|
if(pUnkOuter != NULL)
|
|
this->pUnkOuter = pUnkOuter;
|
|
else
|
|
this->pUnkOuter = reinterpret_cast<IUnknown*>(static_cast<INonDelegatingUnknown*>(this));
|
|
|
|
childAPO = NULL;
|
|
childRT = NULL;
|
|
childCfg = NULL;
|
|
|
|
InterlockedIncrement(&instCount);
|
|
}
|
|
|
|
EqualizerAPO::~EqualizerAPO()
|
|
{
|
|
InterlockedDecrement(&instCount);
|
|
}
|
|
|
|
HRESULT EqualizerAPO::QueryInterface(const IID& iid, void** ppv)
|
|
{
|
|
return pUnkOuter->QueryInterface(iid, ppv);
|
|
}
|
|
|
|
ULONG EqualizerAPO::AddRef()
|
|
{
|
|
return pUnkOuter->AddRef();
|
|
}
|
|
|
|
ULONG EqualizerAPO::Release()
|
|
{
|
|
return pUnkOuter->Release();
|
|
}
|
|
|
|
HRESULT EqualizerAPO::GetLatency(HNSTIME* pTime)
|
|
{
|
|
if(!pTime)
|
|
return E_POINTER;
|
|
|
|
if(!m_bIsLocked)
|
|
return APOERR_ALREADY_UNLOCKED;
|
|
|
|
*pTime = 0;
|
|
if(childAPO)
|
|
childAPO->GetLatency(pTime);
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT EqualizerAPO::Initialize(UINT32 cbDataSize, BYTE* pbyData)
|
|
{
|
|
LogHelper::reset();
|
|
|
|
TraceF(L"Initialize");
|
|
|
|
if((NULL == pbyData) && (0 != cbDataSize))
|
|
return E_INVALIDARG;
|
|
if((NULL != pbyData) && (0 == cbDataSize))
|
|
return E_POINTER;
|
|
if(cbDataSize != sizeof(APOInitSystemEffects) )
|
|
return E_INVALIDARG;
|
|
|
|
resetChild();
|
|
|
|
APOInitSystemEffects* initStruct = (APOInitSystemEffects*)pbyData;
|
|
|
|
PROPVARIANT var;
|
|
PropVariantInit(&var);
|
|
HRESULT hr = initStruct->pAPOEndpointProperties->GetValue(PKEY_AudioEndpoint_GUID, &var);
|
|
if(FAILED(hr))
|
|
{
|
|
LogF(L"Can't read endpoint guid");
|
|
return hr;
|
|
}
|
|
wstring deviceGuid = var.pwszVal;
|
|
TraceF(L"Endpoint GUID: %s", deviceGuid.c_str());
|
|
|
|
DeviceAPOInfo apoInfo;
|
|
if(apoInfo.load(deviceGuid))
|
|
peq.setDeviceInfo(apoInfo.deviceName, apoInfo.connectionName, apoInfo.deviceGuid);
|
|
|
|
wstring apoGuid;
|
|
try
|
|
{
|
|
apoGuid = RegistryHelper::readValue(APP_REGPATH L"\\Child APOs", var.pwszVal);
|
|
}
|
|
catch(RegistryException e)
|
|
{
|
|
LogF(L"Can't read child apo guid because of: %s", e.getMessage());
|
|
return E_NOTFOUND;
|
|
}
|
|
|
|
TraceF(L"Child APO GUID: %s", apoGuid.c_str());
|
|
|
|
if(apoGuid != APOGUID_NOKEY)
|
|
{
|
|
GUID childGuid;
|
|
hr = CLSIDFromString(apoGuid.c_str(), &childGuid);
|
|
if(FAILED(hr))
|
|
{
|
|
LogF(L"Can't convert guid string to guid");
|
|
return hr;
|
|
}
|
|
|
|
hr = CoCreateInstance(childGuid, NULL, CLSCTX_INPROC_SERVER, __uuidof(IAudioProcessingObject), (void**)&childAPO);
|
|
if(FAILED(hr))
|
|
{
|
|
LogF(L"Error in CoCreateInstance for child apo");
|
|
resetChild();
|
|
return hr;
|
|
}
|
|
|
|
hr = childAPO->QueryInterface(__uuidof(IAudioProcessingObjectRT), (void**)&childRT);
|
|
if(FAILED(hr))
|
|
{
|
|
LogF(L"Error in QueryInterface for child apo RT");
|
|
resetChild();
|
|
return hr;
|
|
}
|
|
|
|
hr = childAPO->QueryInterface(__uuidof(IAudioProcessingObjectConfiguration), (void**)&childCfg);
|
|
if(FAILED(hr))
|
|
{
|
|
LogF(L"Error in QueryInterface for child apo configuration");
|
|
resetChild();
|
|
return hr;
|
|
}
|
|
|
|
hr = childAPO->Initialize(cbDataSize, pbyData);
|
|
if(FAILED(hr))
|
|
{
|
|
LogF(L"Error in Initialize of child apo");
|
|
resetChild();
|
|
return hr;
|
|
}
|
|
|
|
TraceF(L"Successfully created and initialized child APO");
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
HRESULT EqualizerAPO::IsInputFormatSupported(IAudioMediaType* pOutputFormat,
|
|
IAudioMediaType* pRequestedInputFormat, IAudioMediaType** ppSupportedInputFormat)
|
|
{
|
|
if(!pRequestedInputFormat)
|
|
return E_POINTER;
|
|
|
|
UNCOMPRESSEDAUDIOFORMAT inFormat;
|
|
HRESULT hr = pRequestedInputFormat->GetUncompressedAudioFormat(&inFormat);
|
|
if(FAILED(hr))
|
|
{
|
|
LogF(L"Error in GetUncompressedAudioFormat");
|
|
return hr;
|
|
}
|
|
|
|
TraceF(L"RequestedInputFormat = { %08X, %u, %u, %u, %f, %08X }",
|
|
inFormat.guidFormatType.Data1, inFormat.dwSamplesPerFrame, inFormat.dwBytesPerSampleContainer,
|
|
inFormat.dwValidBitsPerSample, inFormat.fFramesPerSecond, inFormat.dwChannelMask);
|
|
|
|
if(childAPO)
|
|
{
|
|
hr = childAPO->IsInputFormatSupported(pOutputFormat, pRequestedInputFormat, ppSupportedInputFormat);
|
|
if(SUCCEEDED(hr))
|
|
{
|
|
TraceF(L"Success in IsInputFormatSupported of child apo");
|
|
return hr;
|
|
}
|
|
else
|
|
{
|
|
LogF(L"Failure in IsInputFormatSupported of child apo");
|
|
resetChild();
|
|
}
|
|
}
|
|
|
|
return CBaseAudioProcessingObject::IsInputFormatSupported(pOutputFormat, pRequestedInputFormat, ppSupportedInputFormat);
|
|
}
|
|
|
|
void EqualizerAPO::APOProcess(UINT32 u32NumInputConnections,
|
|
APO_CONNECTION_PROPERTY** ppInputConnections, UINT32 u32NumOutputConnections,
|
|
APO_CONNECTION_PROPERTY** ppOutputConnections)
|
|
{
|
|
switch( ppInputConnections[0]->u32BufferFlags )
|
|
{
|
|
case BUFFER_VALID:
|
|
{
|
|
float* inputFrames = reinterpret_cast<float*>(ppInputConnections[0]->pBuffer);
|
|
float* outputFrames = reinterpret_cast<float*>(ppOutputConnections[0]->pBuffer);
|
|
|
|
if(childRT)
|
|
{
|
|
childRT->APOProcess(u32NumInputConnections, ppInputConnections, u32NumOutputConnections, ppOutputConnections);
|
|
|
|
peq.process(outputFrames, outputFrames, ppInputConnections[0]->u32ValidFrameCount);
|
|
}
|
|
else
|
|
peq.process(outputFrames, inputFrames, ppInputConnections[0]->u32ValidFrameCount);
|
|
|
|
ppOutputConnections[0]->u32ValidFrameCount = ppInputConnections[0]->u32ValidFrameCount;
|
|
ppOutputConnections[0]->u32BufferFlags = ppInputConnections[0]->u32BufferFlags;
|
|
|
|
break;
|
|
}
|
|
case BUFFER_SILENT:
|
|
{
|
|
ppOutputConnections[0]->u32ValidFrameCount = ppInputConnections[0]->u32ValidFrameCount;
|
|
ppOutputConnections[0]->u32BufferFlags = ppInputConnections[0]->u32BufferFlags;
|
|
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
HRESULT EqualizerAPO::LockForProcess(UINT32 u32NumInputConnections,
|
|
APO_CONNECTION_DESCRIPTOR** ppInputConnections, UINT32 u32NumOutputConnections,
|
|
APO_CONNECTION_DESCRIPTOR** ppOutputConnections)
|
|
{
|
|
HRESULT hr;
|
|
if(childCfg != NULL)
|
|
{
|
|
hr = childCfg->LockForProcess(u32NumInputConnections, ppInputConnections, u32NumOutputConnections,
|
|
ppOutputConnections);
|
|
if(SUCCEEDED(hr))
|
|
TraceF(L"Success in LockForProcess of child apo");
|
|
}
|
|
|
|
hr = CBaseAudioProcessingObject::LockForProcess(u32NumInputConnections, ppInputConnections,
|
|
u32NumOutputConnections, ppOutputConnections);
|
|
if(FAILED(hr))
|
|
{
|
|
LogF(L"Error in CBaseAudioProcessingObject::LockForProcess");
|
|
return hr;
|
|
}
|
|
|
|
UNCOMPRESSEDAUDIOFORMAT uncompAudioFormat;
|
|
hr = ppOutputConnections[0]->pFormat->GetUncompressedAudioFormat(&uncompAudioFormat);
|
|
if(FAILED(hr))
|
|
{
|
|
LogF(L"Error in GetUncompressedAudioFormat in LockForProcess");
|
|
return hr;
|
|
}
|
|
|
|
peq.initialize(uncompAudioFormat.fFramesPerSecond, uncompAudioFormat.dwSamplesPerFrame);
|
|
|
|
return hr;
|
|
}
|
|
|
|
HRESULT EqualizerAPO::UnlockForProcess()
|
|
{
|
|
if(childCfg)
|
|
{
|
|
HRESULT hr = childCfg->UnlockForProcess();
|
|
if(FAILED(hr))
|
|
{
|
|
LogF(L"Error in UnlockForProcess");
|
|
return hr;
|
|
}
|
|
}
|
|
|
|
return CBaseAudioProcessingObject::UnlockForProcess();
|
|
}
|
|
|
|
void EqualizerAPO::resetChild()
|
|
{
|
|
if(childAPO != NULL)
|
|
{
|
|
childAPO->Release();
|
|
childAPO = NULL;
|
|
}
|
|
|
|
if(childRT != NULL)
|
|
{
|
|
childRT->Release();
|
|
childRT = NULL;
|
|
}
|
|
|
|
if(childCfg != NULL)
|
|
{
|
|
childCfg->Release();
|
|
childCfg = NULL;
|
|
}
|
|
}
|
|
|
|
HRESULT EqualizerAPO::NonDelegatingQueryInterface(const IID& iid, void** ppv)
|
|
{
|
|
if(iid == __uuidof(IUnknown))
|
|
*ppv = static_cast<INonDelegatingUnknown*>(this);
|
|
else if(iid == __uuidof(IAudioProcessingObject))
|
|
*ppv = static_cast<IAudioProcessingObject*>(this);
|
|
else if(iid == __uuidof(IAudioProcessingObjectRT))
|
|
*ppv = static_cast<IAudioProcessingObjectRT*>(this);
|
|
else if(iid == __uuidof(IAudioProcessingObjectConfiguration))
|
|
*ppv = static_cast<IAudioProcessingObjectConfiguration*>(this);
|
|
else if(iid == __uuidof(IAudioSystemEffects))
|
|
*ppv = static_cast<IAudioSystemEffects*>(this);
|
|
else
|
|
{
|
|
*ppv = NULL;
|
|
return E_NOINTERFACE;
|
|
}
|
|
|
|
reinterpret_cast<IUnknown*>(*ppv)->AddRef();
|
|
return S_OK;
|
|
}
|
|
|
|
ULONG EqualizerAPO::NonDelegatingAddRef()
|
|
{
|
|
return InterlockedIncrement(&refCount);
|
|
}
|
|
|
|
ULONG EqualizerAPO::NonDelegatingRelease()
|
|
{
|
|
if(InterlockedDecrement(&refCount) == 0)
|
|
{
|
|
delete this;
|
|
return 0;
|
|
}
|
|
|
|
return refCount;
|
|
}
|