Added: Support for independent filter sets per channel. The new command "Channel" allows to specify the channel(s) to which the following "Filter" and "Preamp" commands should apply.

This commit is contained in:
jthedering committed 2013-04-14 19:15:57 +00:00
1 parent cd53c16c17
commit fb5020e72f
4 files changed
+295 -67

No files matched your search

+5 -2
View File
@@ -34,6 +34,7 @@ int main(int argc, char** argv)
{
unsigned sampleRate;
unsigned channelCount;
unsigned channelMask;
unsigned frameCount;
float* buf;
@@ -50,6 +51,7 @@ int main(int argc, char** argv)
sampleRate = info.samplerate;
channelCount = info.channels;
channelMask = 0;
frameCount = (unsigned)info.frames;
buf = new float[frameCount * channelCount];
@@ -65,11 +67,12 @@ int main(int argc, char** argv)
{
sampleRate = 44100;
channelCount = 1;
frameCount = 8820000;
channelMask = 0;
float sweepFrom = 0.1f;
float sweepTo = 20000;
float sweepDiff = sweepTo - sweepFrom;
float length = 200;
frameCount = (unsigned)(length * sampleRate);
printf("No input file given, so generating linear sine sweep from %g to %g Hz over %g seconds\n", sweepFrom, sweepTo, length);
@@ -88,7 +91,7 @@ int main(int argc, char** argv)
ParametricEQ peq;
peq.setDeviceInfo(L"Benchmark", L"File output", L"");
peq.initialize((float)sampleRate, channelCount);
peq.initialize((float)sampleRate, channelCount, channelMask);
PrecisionTimer timer;
timer.start();
+30 -3
View File
@@ -195,6 +195,18 @@ HRESULT EqualizerAPO::IsInputFormatSupported(IAudioMediaType* pOutputFormat,
TraceF(L"RequestedInputFormat = { %08X, %u, %u, %u, %f, %08X }",
inFormat.guidFormatType.Data1, inFormat.dwSamplesPerFrame, inFormat.dwBytesPerSampleContainer,
inFormat.dwValidBitsPerSample, inFormat.fFramesPerSecond, inFormat.dwChannelMask);
UNCOMPRESSEDAUDIOFORMAT outFormat;
hr = pOutputFormat->GetUncompressedAudioFormat(&outFormat);
if(FAILED(hr))
{
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)
{
@@ -270,15 +282,30 @@ HRESULT EqualizerAPO::LockForProcess(UINT32 u32NumInputConnections,
return hr;
}
UNCOMPRESSEDAUDIOFORMAT uncompAudioFormat;
hr = ppOutputConnections[0]->pFormat->GetUncompressedAudioFormat(&uncompAudioFormat);
UNCOMPRESSEDAUDIOFORMAT inFormat;
hr = ppInputConnections[0]->pFormat->GetUncompressedAudioFormat(&inFormat);
if(FAILED(hr))
{
LogF(L"Error in GetUncompressedAudioFormat in LockForProcess");
return hr;
}
peq.initialize(uncompAudioFormat.fFramesPerSecond, uncompAudioFormat.dwSamplesPerFrame);
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);
peq.initialize(outFormat.fFramesPerSecond, outFormat.dwSamplesPerFrame, outFormat.dwChannelMask);
return hr;
}
+203 -52
View File
@@ -26,6 +26,8 @@
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <Shlwapi.h>
#include <Ks.h>
#include <KsMedia.h>
#include "StringHelper.h"
#include "RegistryHelper.h"
@@ -52,20 +54,30 @@ BiQuad::BiQuad(float dbGain, float freq, float srate, float bandwidthOrQ, bool i
a[1] = (1 - (alpha * A)) / temp;
a[2] = - (-2 * cs) / temp;
a[3] = - (1 - (alpha /A)) / temp;
x1 = 0;
x2 = 0;
y1 = 0;
y2 = 0;
}
ChannelData::ChannelData()
{
preamp = 1.0f;
filterCount = 0;
}
ParametricEQ::ParametricEQ()
{
filterCount = 0;
channelCount = 0;
preamp = 1.0f;
memset(sampleData, 0, 1000 * sizeof(float));
channelData = NULL;
lastInputWasSilent = false;
threadHandle = NULL;
}
ParametricEQ::~ParametricEQ()
{
// Make sure notification thread is terminated before cleaning up, otherwise deleted memory might be accessed in loadConfig
if(threadHandle != NULL)
{
SetEvent(shutdownEvent);
@@ -77,6 +89,12 @@ ParametricEQ::~ParametricEQ()
CloseHandle(threadHandle);
threadHandle = NULL;
}
if(channelData != NULL)
{
delete[] channelData;
channelData = NULL;
}
}
void ParametricEQ::setDeviceInfo(const wstring& deviceName, const wstring& connectionName, const wstring& deviceGuid)
@@ -86,13 +104,34 @@ void ParametricEQ::setDeviceInfo(const wstring& deviceName, const wstring& conne
this->deviceGuid = deviceGuid;
}
void ParametricEQ::initialize(float sampleRate, unsigned channelCount)
void ParametricEQ::initialize(float sampleRate, unsigned channelCount, unsigned channelMask)
{
this->sampleRate = sampleRate;
this->channelCount = channelCount;
memset(sampleData, 0, 1000 * sizeof(float));
filterCount = 0;
preamp = 1.0f;
channelData = new ChannelData[channelCount];
if(channelMask == 0)
{
switch(channelCount)
{
case 1:
channelMask = KSAUDIO_SPEAKER_MONO;
break;
case 2:
channelMask = KSAUDIO_SPEAKER_STEREO;
break;
case 4:
channelMask = KSAUDIO_SPEAKER_QUAD;
break;
case 6:
channelMask = KSAUDIO_SPEAKER_5POINT1_SURROUND;
break;
case 8:
channelMask = KSAUDIO_SPEAKER_7POINT1_SURROUND;
break;
}
}
this->channelMask = channelMask;
try
{
@@ -123,17 +162,27 @@ void ParametricEQ::initialize(float sampleRate, unsigned channelCount)
void ParametricEQ::loadConfig()
{
unsigned loadFilterCount = 0;
float loadPreamp = 1.0f;
for(unsigned c=0; c<channelCount; c++)
{
channelData[c].loadPreamp = 1.0f;
channelData[c].loadFilterCount = 0;
}
loadConfig(configPath + L"\\config.txt", loadFilterCount, loadPreamp);
vector<bool> selectedChannels(channelCount, true);
loadConfig(configPath + L"\\config.txt", selectedChannels);
unsigned loadFilterCount = 0;
for(unsigned c=0; c<channelCount; c++)
{
channelData[c].preamp = channelData[c].loadPreamp;
channelData[c].filterCount = channelData[c].loadFilterCount;
loadFilterCount += channelData[c].loadFilterCount;
}
TraceF(L"%d filters loaded", loadFilterCount);
preamp = loadPreamp;
filterCount = loadFilterCount;
}
void ParametricEQ::loadConfig(wstring path, unsigned& loadFilterCount, float& loadPreamp)
void ParametricEQ::loadConfig(const wstring& path, vector<bool> selectedChannels)
{
TraceF(L"Loading configuration from %s", path.c_str());
@@ -188,7 +237,7 @@ void ParametricEQ::loadConfig(wstring path, unsigned& loadFilterCount, float& lo
}
else
{
currentWord += value[i];
currentWord += c;
}
}
@@ -222,31 +271,120 @@ void ParametricEQ::loadConfig(wstring path, unsigned& loadFilterCount, float& lo
if(!deviceMatches)
continue;
if(key.find(L"Filter") == 0)
if(key == L"Channel")
{
selectedChannels = vector<bool>(channelCount, false);
value = value + L" ";
wstring currentWord;
for(unsigned i=0; i<value.length(); i++)
{
wchar_t c = towlower(value[i]);
if(c == L' ')
{
if(currentWord.length() > 0)
{
int channelNr = -1;
if(currentWord == L"all")
{
selectedChannels = vector<bool>(channelCount, true);
}
else if(iswdigit(currentWord[0]))
{
channelNr = wcstol(currentWord.c_str(), NULL, 10) - 1;
}
else
{
int channelPos = -1;
if(currentWord == L"l")
channelPos = SPEAKER_FRONT_LEFT;
else if(currentWord == L"r")
channelPos = SPEAKER_FRONT_RIGHT;
else if(currentWord == L"c")
channelPos = SPEAKER_FRONT_CENTER;
else if(currentWord == L"sub")
channelPos = SPEAKER_LOW_FREQUENCY;
else if(currentWord == L"rl")
channelPos = SPEAKER_BACK_LEFT;
else if(currentWord == L"rr")
channelPos = SPEAKER_BACK_RIGHT;
else if(currentWord == L"rc")
channelPos = SPEAKER_BACK_CENTER;
else if(currentWord == L"sl")
channelPos = SPEAKER_SIDE_LEFT;
else if(currentWord == L"sr")
channelPos = SPEAKER_SIDE_RIGHT;
else
LogF(L"Invalid channel position %s", currentWord.c_str());
if(channelPos != -1)
channelNr = getChannelNumber(channelPos);
}
if(channelNr != -1 && channelNr < (int)channelCount)
{
selectedChannels[channelNr] = true;
}
currentWord.clear();
}
}
else
{
currentWord += c;
}
}
wstringstream channelStream;
for(unsigned c=0; c<channelCount; c++)
{
if(selectedChannels[c])
{
if(channelStream.tellp() > 0)
channelStream << L", ";
channelStream << c+1;
}
}
TraceF(L"Selecting channel(s) %s", channelStream.str().c_str());
}
else if(key.find(L"Filter") == 0)
{
//Conversion to period as decimal mark, if needed
value = StringHelper::replaceCharacters(value, L",", L'.');
if(loadFilterCount < (sizeof(filters)/sizeof(BiQuad)))
wchar_t freqString[10];
float freq, gain, bandwidth;
int matched = swscanf_s(value.c_str(), L" ON PEQ Fc %9s Hz Gain %f dB BW Oct %f", &freqString, 10, &gain, &bandwidth);
if(matched == 3 && (freq = getFreq(freqString)) != -1.0f)
{
wchar_t freqString[10];
float freq, gain, bandwidth;
int matched = swscanf_s(value.c_str(), L" ON PEQ Fc %9s Hz Gain %f dB BW Oct %f", &freqString, 10, &gain, &bandwidth);
for(unsigned c=0; c<channelCount; c++)
{
if(selectedChannels[c] && channelData[c].loadFilterCount < (sizeof(channelData[c].filters)/sizeof(BiQuad)))
{
channelData[c].filters[channelData[c].loadFilterCount++] = BiQuad(gain, freq, sampleRate, bandwidth, false);
}
}
TraceF(L"Adding filter with center frequency %g Hz, gain %g dB and bandwidth %g octaves", freq, gain, bandwidth);
}
else
{
float q;
matched = swscanf_s(value.c_str(), L" ON PK Fc %9s Hz Gain %f dB Q %f", &freqString, 10, &gain, &q);
if(matched == 3 && (freq = getFreq(freqString)) != -1.0f)
{
filters[loadFilterCount++] = BiQuad(gain, freq, sampleRate, bandwidth, false);
TraceF(L"Adding filter with center frequency %g Hz, gain %g dB and bandwidth %g octaves", freq, gain, bandwidth);
}
else
{
float q;
matched = swscanf_s(value.c_str(), L" ON PK Fc %9s Hz Gain %f dB Q %f", &freqString, 10, &gain, &q);
if(matched == 3 && (freq = getFreq(freqString)) != -1.0f)
for(unsigned c=0; c<channelCount; c++)
{
filters[loadFilterCount++] = BiQuad(gain, freq, sampleRate, q, true);
TraceF(L"Adding filter with center frequency %g Hz, gain %g dB and Q %g", freq, gain, q);
if(selectedChannels[c] && channelData[c].loadFilterCount < (sizeof(channelData[c].filters)/sizeof(BiQuad)))
{
channelData[c].filters[channelData[c].loadFilterCount++] = BiQuad(gain, freq, sampleRate, q, true);
}
}
TraceF(L"Adding filter with center frequency %g Hz, gain %g dB and Q %g", freq, gain, q);
}
}
}
@@ -259,8 +397,15 @@ void ParametricEQ::loadConfig(wstring path, unsigned& loadFilterCount, float& lo
int matched = swscanf_s(value.c_str(), L" %f dB", &preamp_dB);
if(matched == 1)
{
loadPreamp = pow(10.0f, preamp_dB / 20.0f);
TraceF(L"Setting preamp to %g dB", preamp_dB);
for(unsigned c=0; c<channelCount; c++)
{
if(selectedChannels[c])
{
channelData[c].loadPreamp *= pow(10.0f, preamp_dB / 20.0f);
}
}
TraceF(L"Adjusting preamp by %g dB", preamp_dB);
}
}
else if(key == L"Include")
@@ -284,14 +429,12 @@ void ParametricEQ::loadConfig(wstring path, unsigned& loadFilterCount, float& lo
else
includePath = value;
loadConfig(includePath, loadFilterCount, loadPreamp);
loadConfig(includePath, selectedChannels);
}
}
}
}
#define IS_DENORMAL(f) (((*(unsigned int *)&(f))&0x7f800000) == 0)
void ParametricEQ::process(float *output, float *input, unsigned frameCount)
{
bool inputSilent = true;
@@ -322,32 +465,25 @@ void ParametricEQ::process(float *output, float *input, unsigned frameCount)
lastInputWasSilent = false;
//Avoid denormals
for(unsigned i = 0; i < (filterCount+1)*channelCount*2; i++)
if(IS_DENORMAL(sampleData[i]))
sampleData[i] = 0;
for (unsigned c=0; c<channelCount; c++)
{
for(unsigned f=0; f<channelData[c].filterCount; f++)
{
channelData[c].filters[f].removeDenormals();
}
}
for (unsigned i = 0; i < frameCount * channelCount; i+=channelCount)
for (unsigned c=0; c<channelCount; c++)
{
float sample = input[i+c];
for(unsigned f=0; f<filterCount; f++)
for(unsigned f=0; f<channelData[c].filterCount; f++)
{
unsigned dataIndex = (c*(filterCount+1) + f)*2;
float result = filters[f].a0 * sample + filters[f].a[0] * sampleData[dataIndex] + filters[f].a[1] * sampleData[dataIndex+1] +
filters[f].a[2] * sampleData[dataIndex+2] + filters[f].a[3] * sampleData[dataIndex+3];
sampleData[dataIndex+1] = sampleData[dataIndex];
sampleData[dataIndex] = sample;
//Input for next stage
sample = result;
sample = channelData[c].filters[f].process(sample);
}
unsigned lastIndex = (c*(filterCount+1) + filterCount)*2;
sampleData[lastIndex + 1] = sampleData[lastIndex];
sampleData[lastIndex] = sample;
output[i+c] = sample * preamp;
output[i+c] = sample * channelData[c].preamp;
}
}
@@ -372,6 +508,21 @@ float ParametricEQ::getFreq(const wstring& freqString)
return -1.0f;
}
unsigned ParametricEQ::getChannelNumber(unsigned position)
{
if((channelMask & position) == 0)
return -1;
int channelNr = 0;
for(unsigned i=1; i<position; i<<=1)
{
if(channelMask & i)
channelNr++;
}
return channelNr;
}
unsigned long __stdcall ParametricEQ::notificationThread(void* parameter)
{
ParametricEQ* peq = (ParametricEQ*)parameter;
+57 -10
View File
@@ -20,14 +20,62 @@
#pragma once
#include <string>
#include <vector>
#define IS_DENORMAL(f) (((*(unsigned int *)&(f))&0x7f800000) == 0)
struct BiQuad
{
BiQuad() {}
BiQuad(float dbGain, float freq, float srate, float bandwidthOrQ, bool isQ);
__declspec(align(16)) float a[4];
BiQuad() {}
BiQuad(float dbGain, float freq, float srate, float bandwidthOrQ, bool isQ);
__forceinline
void removeDenormals()
{
if(IS_DENORMAL(x1))
x1 = 0.0f;
if(IS_DENORMAL(x2))
x2 = 0.0f;
if(IS_DENORMAL(y1))
y1 = 0.0f;
if(IS_DENORMAL(y2))
y2 = 0.0f;
}
__forceinline
float process(float sample)
{
float result = a0 * sample + a[0] * x1 + a[1] * x2 +
a[2] * y1 + a[3] * y2;
x2 = x1;
x1 = sample;
y2 = y1;
y1 = result;
//Input for next stage
return result;
}
__declspec(align(16)) float a[4];
float a0;
float x1, x2;
float y1, y2;
};
struct ChannelData
{
ChannelData();
float preamp;
unsigned filterCount;
BiQuad filters[100];
// used while loading instead of overwriting immediately
float loadPreamp;
unsigned loadFilterCount;
};
class ParametricEQ
@@ -37,13 +85,14 @@ public:
~ParametricEQ();
void setDeviceInfo(const std::wstring& deviceName, const std::wstring& connectionName, const std::wstring& deviceGuid);
void initialize(float sampleRate, unsigned channelCount);
void initialize(float sampleRate, unsigned channelCount, unsigned channelMask);
void loadConfig();
void process(float *output, float *input, unsigned frameCount);
private:
void loadConfig(std::wstring path, unsigned& loadFilterCount, float& loadPreamp);
void loadConfig(const std::wstring& path, std::vector<bool> selectedChannels);
float getFreq(const std::wstring& freqString);
unsigned getChannelNumber(unsigned position);
static unsigned long __stdcall notificationThread(void* parameter);
std::wstring deviceName;
@@ -52,10 +101,8 @@ private:
std::wstring configPath;
float sampleRate;
unsigned channelCount;
float preamp;
BiQuad filters[100];
unsigned filterCount;
float sampleData[2000];
unsigned channelMask;
ChannelData* channelData;
bool lastInputWasSilent;
void* threadHandle;
void* shutdownEvent;