Version 0.9

Added: Filter type "IIR", which allows to specify a user-defined IIR filter of arbitrary order with custom coefficients.
Improved: For playback devices, the APO is now installed both as GFX (post-mix) and LFX (pre-mix) APO. Normally the filtering happens in GFX, but the command "Stage" allows to select LFX to apply effects based on the number of input channels.
Added: Expression language. The commands "If", "ElseIf", "Else", "EndIf", "Eval" and inline expressions allow to change the filter behaviour based on runtime conditions.
Added: Command "Copy", which copies audio data between channels.
Added: Command "Delay", which delays the audio on the currently selected channels.
Improved: Configuration files are now locked exclusively for writing while reading them.
Improved: There is now a transition (10 ms) from old to new configuration after loading.
Improved: Substantial internal restructuring to improve extensibility and maintainability. Filtering commands now work exactly in the order they were specified.
Fixed: When the microphone input was in stereo but the application requested a mono stream, the audio was heavily distorted.
Fixed: The sine sweep generated by the Benchmark application had floating-point precision issues leading to severe artifacts when certain filters were applied.
This commit is contained in:
jthedering committed 2014-08-10 18:35:59 +00:00
1 parent 11e52f6210
commit 90ee2b0aa9
89 files changed
+6396 -1375

No files matched your search

+154 -81
View File
@@ -22,20 +22,23 @@
#define INITGUID
#include <mmdeviceapi.h>
#include "LogHelper.h"
#include "RegistryHelper.h"
#include "helpers/LogHelper.h"
#include "helpers/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),
const CRegAPOProperties<1> EqualizerAPO::regGfxProperties(
EQUALIZERAPO_GFX_GUID, L"EqualizerAPO", L"Copyright (C) 2014", 1, 0, __uuidof(IAudioProcessingObject),
(APO_FLAG)( APO_FLAG_FRAMESPERSECOND_MUST_MATCH | APO_FLAG_BITSPERSAMPLE_MUST_MATCH | APO_FLAG_INPLACE));
const CRegAPOProperties<1> EqualizerAPO::regLfxProperties(
EQUALIZERAPO_LFX_GUID, L"EqualizerAPO", L"Copyright (C) 2014", 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)
:CBaseAudioProcessingObject(regGfxProperties)
{
refCount = 1;
if(pUnkOuter != NULL)
@@ -101,6 +104,16 @@ HRESULT EqualizerAPO::Initialize(UINT32 cbDataSize, BYTE* pbyData)
resetChild();
APOInitSystemEffects* initStruct = (APOInitSystemEffects*)pbyData;
GUID apoGuid = initStruct->APOInit.clsid;
try
{
TraceF(L"APO GUID: %s", RegistryHelper::getGuidString(apoGuid).c_str());
}
catch(RegistryException e)
{
LogF(L"Could not convert apo guid to guid string");
}
engine.setLfx((apoGuid == EQUALIZERAPO_LFX_GUID) != 0);
PROPVARIANT var;
PropVariantInit(&var);
@@ -113,34 +126,32 @@ HRESULT EqualizerAPO::Initialize(UINT32 cbDataSize, BYTE* pbyData)
wstring deviceGuid = var.pwszVal;
TraceF(L"Endpoint GUID: %s", deviceGuid.c_str());
wstring childApoGuid;
try
{
DeviceAPOInfo apoInfo;
if(apoInfo.load(deviceGuid))
peq.setDeviceInfo(apoInfo.deviceName, apoInfo.connectionName, apoInfo.deviceGuid);
{
engine.setDeviceInfo(apoInfo.isInput, apoInfo.deviceName, apoInfo.connectionName, apoInfo.deviceGuid);
if(apoGuid == EQUALIZERAPO_LFX_GUID)
childApoGuid = apoInfo.originalLfxGuid;
else
childApoGuid = apoInfo.originalGfxGuid;
}
}
catch(RegistryException e)
{
LogF(L"Could not read endpoint device info because of: %s", e.getMessage());
LogF(L"Could not read endpoint device info because of: %s", e.getMessage().c_str());
}
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", childApoGuid.c_str());
TraceF(L"Child APO GUID: %s", apoGuid.c_str());
if(apoGuid != APOGUID_NULL && apoGuid != APOGUID_NOKEY && apoGuid != APOGUID_NOVALUE)
if(childApoGuid != L"" && childApoGuid != APOGUID_NULL && childApoGuid != APOGUID_NOKEY && childApoGuid != APOGUID_NOVALUE)
{
GUID childGuid;
hr = CLSIDFromString(apoGuid.c_str(), &childGuid);
hr = CLSIDFromString(childApoGuid.c_str(), &childGuid);
if(FAILED(hr))
{
LogF(L"Can't convert guid string to guid");
@@ -210,18 +221,17 @@ HRESULT EqualizerAPO::IsInputFormatSupported(IAudioMediaType* pOutputFormat,
LogF(L"Error in second GetUncompressedAudioFormat");
return hr;
}
TraceF(L"Output format = { %08X, %u, %u, %u, %f, %08X }",
outFormat.guidFormatType.Data1, outFormat.dwSamplesPerFrame, outFormat.dwBytesPerSampleContainer,
outFormat.dwValidBitsPerSample, outFormat.fFramesPerSecond, outFormat.dwChannelMask);
if(childAPO)
{
hr = childAPO->IsInputFormatSupported(pOutputFormat, pRequestedInputFormat, ppSupportedInputFormat);
if(SUCCEEDED(hr))
{
TraceF(L"Success in IsInputFormatSupported of child apo");
return hr;
}
else
{
@@ -230,42 +240,38 @@ HRESULT EqualizerAPO::IsInputFormatSupported(IAudioMediaType* pOutputFormat,
}
}
return CBaseAudioProcessingObject::IsInputFormatSupported(pOutputFormat, pRequestedInputFormat, ppSupportedInputFormat);
}
if(!childAPO || !SUCCEEDED(hr))
{
hr = 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);
// we do not support downmixing currently
if(hr == S_OK && inFormat.dwSamplesPerFrame > 2 && inFormat.dwSamplesPerFrame > outFormat.dwSamplesPerFrame)
{
*ppSupportedInputFormat = pOutputFormat;
hr = S_FALSE;
}
}
if(childRT)
{
childRT->APOProcess(u32NumInputConnections, ppInputConnections, u32NumOutputConnections, ppOutputConnections);
peq.process(outputFrames, outputFrames, ppInputConnections[0]->u32ValidFrameCount);
}
else
peq.process(outputFrames, inputFrames, ppInputConnections[0]->u32ValidFrameCount);
if(hr == S_FALSE)
{
UNCOMPRESSEDAUDIOFORMAT supportedFormat;
HRESULT hr2 = (*ppSupportedInputFormat)->GetUncompressedAudioFormat(&supportedFormat);
if(FAILED(hr2))
{
LogF(L"Error in third GetUncompressedAudioFormat");
return hr2;
}
ppOutputConnections[0]->u32ValidFrameCount = ppInputConnections[0]->u32ValidFrameCount;
ppOutputConnections[0]->u32BufferFlags = ppInputConnections[0]->u32BufferFlags;
TraceF(L"InputFormat not accepted, SupportedInputFormat = { %08X, %u, %u, %u, %f, %08X }",
supportedFormat.guidFormatType.Data1, supportedFormat.dwSamplesPerFrame, supportedFormat.dwBytesPerSampleContainer,
supportedFormat.dwValidBitsPerSample, supportedFormat.fFramesPerSecond, supportedFormat.dwChannelMask);
}
else if(hr == S_OK)
{
TraceF(L"InputFormat accepted");
}
break;
}
case BUFFER_SILENT:
{
ppOutputConnections[0]->u32ValidFrameCount = ppInputConnections[0]->u32ValidFrameCount;
ppOutputConnections[0]->u32BufferFlags = ppInputConnections[0]->u32BufferFlags;
break;
}
}
return hr;
}
HRESULT EqualizerAPO::LockForProcess(UINT32 u32NumInputConnections,
@@ -273,6 +279,35 @@ HRESULT EqualizerAPO::LockForProcess(UINT32 u32NumInputConnections,
APO_CONNECTION_DESCRIPTOR** ppOutputConnections)
{
HRESULT hr;
UNCOMPRESSEDAUDIOFORMAT inFormat;
hr = ppInputConnections[0]->pFormat->GetUncompressedAudioFormat(&inFormat);
if(FAILED(hr))
{
LogF(L"Error in GetUncompressedAudioFormat in LockForProcess");
return hr;
}
unsigned maxInputFrameCount = ppInputConnections[0]->u32MaxFrameCount;
TraceF(L"Input format in LockForProcess = { %08X, %u, %u, %u, %f, %08X, %u }",
inFormat.guidFormatType.Data1, inFormat.dwSamplesPerFrame, inFormat.dwBytesPerSampleContainer,
inFormat.dwValidBitsPerSample, inFormat.fFramesPerSecond, inFormat.dwChannelMask, maxInputFrameCount);
UNCOMPRESSEDAUDIOFORMAT outFormat;
hr = ppOutputConnections[0]->pFormat->GetUncompressedAudioFormat(&outFormat);
if(FAILED(hr))
{
LogF(L"Error in second GetUncompressedAudioFormat in LockForProcess");
return hr;
}
unsigned maxOutputFrameCount = ppOutputConnections[0]->u32MaxFrameCount;
TraceF(L"Output format in LockForProcess = { %08X, %u, %u, %u, %f, %08X, %u }",
outFormat.guidFormatType.Data1, outFormat.dwSamplesPerFrame, outFormat.dwBytesPerSampleContainer,
outFormat.dwValidBitsPerSample, outFormat.fFramesPerSecond, outFormat.dwChannelMask, maxOutputFrameCount);
if(childCfg != NULL)
{
hr = childCfg->LockForProcess(u32NumInputConnections, ppInputConnections, u32NumOutputConnections,
@@ -288,35 +323,36 @@ HRESULT EqualizerAPO::LockForProcess(UINT32 u32NumInputConnections,
LogF(L"Error in CBaseAudioProcessingObject::LockForProcess");
return hr;
}
UNCOMPRESSEDAUDIOFORMAT inFormat;
hr = ppInputConnections[0]->pFormat->GetUncompressedAudioFormat(&inFormat);
if(FAILED(hr))
else if(hr == S_OK)
{
LogF(L"Error in GetUncompressedAudioFormat in LockForProcess");
return hr;
TraceF(L"LockForProcess successful");
}
TraceF(L"Input format in LockForProcess = { %08X, %u, %u, %u, %f, %08X }",
inFormat.guidFormatType.Data1, inFormat.dwSamplesPerFrame, inFormat.dwBytesPerSampleContainer,
inFormat.dwValidBitsPerSample, inFormat.fFramesPerSecond, inFormat.dwChannelMask);
UNCOMPRESSEDAUDIOFORMAT outFormat;
hr = ppOutputConnections[0]->pFormat->GetUncompressedAudioFormat(&outFormat);
if(FAILED(hr))
{
LogF(L"Error in second GetUncompressedAudioFormat in LockForProcess");
return hr;
}
TraceF(L"Output format in LockForProcess = { %08X, %u, %u, %u, %f, %08X }",
outFormat.guidFormatType.Data1, outFormat.dwSamplesPerFrame, outFormat.dwBytesPerSampleContainer,
outFormat.dwValidBitsPerSample, outFormat.fFramesPerSecond, outFormat.dwChannelMask);
unsigned maxFrameCount = maxInputFrameCount;
if(maxFrameCount == 0)
maxFrameCount = maxOutputFrameCount;
if(outFormat.dwChannelMask == 0 && inFormat.dwSamplesPerFrame == outFormat.dwSamplesPerFrame)
peq.initialize(outFormat.fFramesPerSecond, outFormat.dwSamplesPerFrame, inFormat.dwChannelMask);
unsigned realChannelCount;
if(childCfg != NULL)
realChannelCount = outFormat.dwSamplesPerFrame;
else
peq.initialize(outFormat.fFramesPerSecond, outFormat.dwSamplesPerFrame, outFormat.dwChannelMask);
realChannelCount = inFormat.dwSamplesPerFrame;
unsigned channelMask;
if(engine.isCapture())
{
channelMask = inFormat.dwChannelMask;
if(channelMask == 0 && inFormat.dwSamplesPerFrame == outFormat.dwSamplesPerFrame)
channelMask = outFormat.dwChannelMask;
}
else
{
channelMask = outFormat.dwChannelMask;
if(channelMask == 0 && inFormat.dwSamplesPerFrame == outFormat.dwSamplesPerFrame)
channelMask = inFormat.dwChannelMask;
}
engine.initialize(outFormat.fFramesPerSecond, inFormat.dwSamplesPerFrame, realChannelCount, outFormat.dwSamplesPerFrame, channelMask, maxFrameCount);
return hr;
}
@@ -357,6 +393,43 @@ void EqualizerAPO::resetChild()
}
}
#pragma AVRT_CODE_BEGIN
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);
engine.process(outputFrames, outputFrames, ppInputConnections[0]->u32ValidFrameCount);
}
else
engine.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;
}
}
}
#pragma AVRT_CODE_END
HRESULT EqualizerAPO::NonDelegatingQueryInterface(const IID& iid, void** ppv)
{
if(iid == __uuidof(IUnknown))
@@ -393,4 +466,4 @@ ULONG EqualizerAPO::NonDelegatingRelease()
}
return refCount;
}
}