Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

HACK: add low-pass filter for up/downmix with LFE channel. Temporary …

…for merge - final version ready but requires merge of AEDSP after AE merge to mainline.
  • Loading branch information...
commit 09ba962fea3c6de35e4dd1d8622ea414ce659d68 1 parent 01f7fd8
@DDDamian authored
View
4 xbmc/cores/AudioEngine/Engines/SoftAEStream.cpp
@@ -79,7 +79,7 @@ void CSoftAEStream::InitializeRemap()
if (!AE_IS_RAW(m_initDataFormat))
{
/* re-init the remappers */
- m_remap .Initialize(m_initChannelLayout, AE.GetChannelLayout() , false, false, AE.GetStdChLayout());
+ m_remap .Initialize(m_initChannelLayout, AE.GetChannelLayout() , false, m_initSampleRate, false, AE.GetStdChLayout());
m_vizRemap.Initialize(m_initChannelLayout, CAEChannelInfo(AE_CH_LAYOUT_2_0), false, true);
/*
@@ -153,7 +153,7 @@ void CSoftAEStream::Initialize()
else
{
if (
- !m_remap .Initialize(m_initChannelLayout, m_aeChannelLayout , false, false, AE.GetStdChLayout()) ||
+ !m_remap .Initialize(m_initChannelLayout, m_aeChannelLayout , false, m_initSampleRate, false, AE.GetStdChLayout()) ||
!m_vizRemap.Initialize(m_initChannelLayout, CAEChannelInfo(AE_CH_LAYOUT_2_0), false, true))
{
m_valid = false;
View
206 xbmc/cores/AudioEngine/Utils/AERemap.cpp
@@ -29,19 +29,39 @@
using namespace std;
-CAERemap::CAERemap()
+CAERemap::CAERemap() :
+ m_returnBuffer (NULL ),
+ m_returnSamples (0 ),
+ m_needsLPF (false),
+ m_initialized (false),
+ m_iir (&m_iirF)
{
+ memset(&m_iirF, 0, sizeof(m_iirF));
}
CAERemap::~CAERemap()
{
+ DeInitialize();
}
-bool CAERemap::Initialize(CAEChannelInfo input, CAEChannelInfo output, bool finalStage, bool forceNormalize/* = false */, enum AEStdChLayout stdChLayout/* = AE_CH_LAYOUT_INVALID */)
+void CAERemap::DeInitialize()
+{
+ memset(&m_iirF, 0, sizeof(m_iirF));
+}
+
+bool CAERemap::Initialize(CAEChannelInfo input, CAEChannelInfo output, bool finalStage, unsigned int sampleRate,
+ bool forceNormalize/* = false */, enum AEStdChLayout stdChLayout/* = AE_CH_LAYOUT_INVALID */)
{
if (!input.Count() || !output.Count())
return false;
+ /* HACK: needs LP Filter - here until DSP class merged */
+ if (output.HasChannel(AE_CH_LFE))
+ {
+ if (InitializeLPF(output, sampleRate))
+ m_needsLPF = true;
+ }
+
/* build the downmix matrix */
memset(m_mixInfo, 0, sizeof(m_mixInfo));
m_output = output;
@@ -185,7 +205,7 @@ bool CAERemap::Initialize(CAEChannelInfo input, CAEChannelInfo output, bool fina
#undef RM
if (g_guiSettings.GetBool("audiooutput.stereoupmix"))
- BuildUpmixMatrix(input, output);
+ BuildUpmixMatrix(input, output, sampleRate);
/* normalize the values */
bool normalize;
@@ -280,7 +300,7 @@ void CAERemap::ResolveMix(const AEChannel from, CAEChannelInfo to)
}
/* This method has unrolled loop for higher performance */
-void CAERemap::Remap(float * const in, float * const out, const unsigned int frames) const
+void CAERemap::Remap(float * const in, float * out, const unsigned int frames)
{
const unsigned int frameBlocks = frames & ~0x3;
@@ -357,9 +377,14 @@ void CAERemap::Remap(float * const in, float * const out, const unsigned int fra
}
}
}
+
+ if (m_needsLPF)
+ {
+ ProcessLPF(out, frames);
+ }
}
-inline void CAERemap::BuildUpmixMatrix(const CAEChannelInfo& input, const CAEChannelInfo& output)
+inline void CAERemap::BuildUpmixMatrix(const CAEChannelInfo& input, const CAEChannelInfo& output, unsigned int sampleRate)
{
#define UM(from, to) \
if (!m_mixInfo[to].in_src && m_mixInfo[to].in_dst) \
@@ -410,3 +435,174 @@ inline void CAERemap::BuildUpmixMatrix(const CAEChannelInfo& input, const CAECha
inputInfo->srcIndex[0].level /= sqrt((float)(inputInfo->cpyCount + 1));
}
}
+
+/* HACK: LP Filter - here until DSP class merged */
+bool CAERemap::InitializeLPF(const CAEChannelInfo& channels, const unsigned int sampleRate)
+{
+ LFP_TYPE *coef;
+ LFP_TYPE fs, fc; /* Sampling frequency, cutoff frequency */
+ LFP_TYPE Q; /* Resonance > 1.0 < 1000 */
+ unsigned nInd;
+ LFP_TYPE a0, a1, a2, b0, b1, b2;
+ LFP_TYPE k; /* overall gain factor */
+
+ m_iir->length = FILTER_SECTIONS; /* Number of filter sections */
+
+ /* Store number of channels for Process() */
+ m_channelCount = channels.Count();
+
+ /* Check that we have an LFE channel and get its number */
+ m_lfeChannel = 0;
+
+ for (unsigned int c = 0; c < m_channelCount; c++)
+ {
+ if (channels[c] == AE_CH_LFE)
+ {
+ m_lfeChannel = c;
+ break;
+ }
+ }
+
+ if (!m_lfeChannel) return false; /* no LFE channel present */
+
+ /* Set up history and coefficient storage memory */
+ memset(m_iir->history, 0, 2 * m_iir->length * sizeof(LFP_TYPE));
+ memset(m_iir->coef, 0, (4 * m_iir->length + 1) * sizeof(LFP_TYPE));
+
+ /* Setup filter s-domain coefficients */
+ /* Section 1 */
+ coefs[0].a0 = 1.0;
+ coefs[0].a1 = 0;
+ coefs[0].a2 = 0;
+ coefs[0].b0 = 1.0;
+ coefs[0].b1 = 0.765367;
+ coefs[0].b2 = 1.0;
+
+ /* Section 2 */
+ coefs[1].a0 = 1.0;
+ coefs[1].a1 = 0;
+ coefs[1].a2 = 0;
+ coefs[1].b0 = 1.0;
+ coefs[1].b1 = 1.847759;
+ coefs[1].b2 = 1.0;
+
+ k = 1.0; /* Set overall filter gain */
+ coef = m_iir->coef + 1; /* Skip k, or gain */
+
+ Q = 1.0; /* Resonance */
+ fc = 150.0; /* Filter cutoff (Hz) */
+ fs = sampleRate; /* Sampling frequency (Hz) */
+
+ /* Compute z-domain coefficients for each biquad section
+ for new Cutoff Frequency and Resonance */
+ for (nInd = 0; nInd < m_iir->length; nInd++)
+ {
+ a0 = coefs[nInd].a0;
+ a1 = coefs[nInd].a1;
+ a2 = coefs[nInd].a2;
+
+ b0 = coefs[nInd].b0;
+ b1 = coefs[nInd].b1 / Q; /* Divide by resonance or Q */
+ b2 = coefs[nInd].b2;
+
+ szxform(&a0, &a1, &a2, &b0, &b1, &b2, fc, fs, &k, coef);
+
+ coef += 4; /* Point to next filter section */
+ }
+
+ /* Update overall filter gain in coef array */
+ m_iir->coef[0] = k;
+
+ m_initialized = true;
+
+ return true;
+}
+
+void CAERemap::ProcessLPF(float* data, unsigned int frames)
+{
+ float* pSampleBuffer = data; /* incoming buffer of samples */
+ float* pOffset; /* storage for current sample address */
+
+ LFP_TYPE *hist1_ptr, *hist2_ptr, *coef_ptr;
+ LFP_TYPE new_hist, history1, history2;
+ LFP_TYPE output;
+
+ for (unsigned int c = 0; c < frames; c++)
+ {
+ coef_ptr = m_iir->coef; /* pointer to cycle coefficients */
+
+ /* calculate where our next LFE sample is */
+ pOffset = pSampleBuffer + (c * m_channelCount + m_lfeChannel);
+
+ /* 1st number of coefficients array is overall input scale factor or filter gain */
+ output = (LFP_TYPE)pOffset[0] * (*coef_ptr++);
+
+ hist1_ptr = m_iir->history; /* first history */
+ hist2_ptr = hist1_ptr + 1; /* next history */
+
+ for (unsigned int i = 0 ; i < m_iir->length; i++)
+ {
+ history1 = *hist1_ptr; /* history values */
+ history2 = *hist2_ptr;
+
+ output = output - history1 * (*coef_ptr++);
+ new_hist = output - history2 * (*coef_ptr++); /* poles */
+
+ output = new_hist + history1 * (*coef_ptr++);
+ output = output + history2 * (*coef_ptr++); /* zeros */
+
+ *hist2_ptr++ = *hist1_ptr;
+ *hist1_ptr++ = new_hist;
+ hist1_ptr++;
+ hist2_ptr++;
+ }
+
+ pOffset[0] = (float)output;
+ }
+
+ return;
+}
+
+void CAERemap::szxform(LFP_TYPE *a0, LFP_TYPE *a1, LFP_TYPE *a2, /* numerator coefficients */
+ LFP_TYPE *b0, LFP_TYPE *b1, LFP_TYPE *b2, /* denominator coefficients */
+ LFP_TYPE fc, /* Filter cutoff frequency */
+ LFP_TYPE fs, /* sampling rate */
+ LFP_TYPE *k, /* overall gain factor */
+ LFP_TYPE *coef) /* pointer to 4 iir coefficients */
+{
+ /* Calculate a1 and a2 and overwrite the original values */
+ prewarp(a0, a1, a2, fc, fs);
+ prewarp(b0, b1, b2, fc, fs);
+ bilinear(*a0, *a1, *a2, *b0, *b1, *b2, k, fs, coef);
+}
+
+void CAERemap::bilinear(LFP_TYPE a0, LFP_TYPE a1, LFP_TYPE a2, /* numerator coefficients */
+ LFP_TYPE b0, LFP_TYPE b1, LFP_TYPE b2, /* denominator coefficients */
+ LFP_TYPE *k, /* overall gain factor */
+ LFP_TYPE fs, /* sampling rate */
+ LFP_TYPE *coef) /* pointer to 4 iir coefficients */
+{
+ LFP_TYPE ad, bd;
+
+ ad = 4. * a2 * fs * fs + 2. * a1 * fs + a0;
+ bd = 4. * b2 * fs * fs + 2. * b1 * fs + b0;
+
+ *k *= ad/bd;
+
+ *coef++ = (2. * b0 - 8. * b2 * fs * fs) / bd;
+ *coef++ = (4. * b2 * fs * fs - 2. * b1 * fs + b0) / bd;
+ *coef++ = (2. * a0 - 8. * a2 * fs * fs) / ad;
+ *coef = (4. * a2 * fs * fs - 2. * a1 * fs + a0) / ad;
+}
+
+void CAERemap::prewarp(LFP_TYPE *a0, LFP_TYPE *a1, LFP_TYPE *a2, LFP_TYPE fc, LFP_TYPE fs)
+{
+ LFP_TYPE wp, pi;
+
+ pi = 4.0 * atan(1.0);
+ wp = 2.0 * fs * tan(pi * fc / fs);
+
+ *a2 = (*a2) / (wp * wp);
+ *a1 = (*a1) / wp;
+}
+
View
62 xbmc/cores/AudioEngine/Utils/AERemap.h
@@ -22,13 +22,28 @@
#include "AEAudioFormat.h"
+/* HACK: needs LP Filter - here until DSP class merged */
+#define FILTER_SECTIONS 2
+/* dont use double precision on embedded devices */
+#if defined(__arm__) || defined(TARGET_DARWIN_IOS)
+ #define LFP_TYPE float
+#else
+ #define LFP_DOUBLE
+ #define LFP_TYPE double
+#endif
+
class CAERemap {
public:
CAERemap();
~CAERemap();
- bool Initialize(CAEChannelInfo input, CAEChannelInfo output, bool finalStage, bool forceNormalize = false, enum AEStdChLayout stdChLayout = AE_CH_LAYOUT_INVALID);
- void Remap(float * const in, float * const out, const unsigned int frames) const;
+ bool Initialize(CAEChannelInfo input, CAEChannelInfo output, bool finalStage, unsigned int sampleRate, bool forceNormalize = false, enum AEStdChLayout stdChLayout = AE_CH_LAYOUT_INVALID);
+ void Remap(float * const in, float * out, const unsigned int frames);
+
+ /* HACK: needs LP Filter - here until DSP class merged */
+ bool InitializeLPF(const CAEChannelInfo& channels, const unsigned int sampleRate);
+ void ProcessLPF(float* data, unsigned int frames);
+ void DeInitialize();
private:
typedef struct {
@@ -51,6 +66,47 @@ class CAERemap {
int m_outChannels;
void ResolveMix(const AEChannel from, CAEChannelInfo to);
- void BuildUpmixMatrix(const CAEChannelInfo& input, const CAEChannelInfo& output);
+ void BuildUpmixMatrix(const CAEChannelInfo& input, const CAEChannelInfo& output, unsigned int sampleRate);
+
+ /* HACK: needs LP Filter - here until DSP class merged */
+ typedef struct
+ {
+ unsigned int length; /* size of filter */
+ LFP_TYPE history[2 * FILTER_SECTIONS * sizeof(LFP_TYPE)]; /* pointer to history in filter */
+ LFP_TYPE coef [4 * FILTER_SECTIONS + 1 * sizeof(LFP_TYPE)]; /* pointer to coefficients of filter */
+ } FILTER;
+
+ typedef struct
+ {
+ LFP_TYPE a0, a1, a2; /* numerator coefficients */
+ LFP_TYPE b0, b1, b2; /* denominator coefficients */
+ } BIQUAD;
+
+ FILTER m_iirF; /* the history and its coefficients */
+ FILTER* m_iir; /* pointer to above */
+
+ BIQUAD coefs[FILTER_SECTIONS]; /* filter coefficients */
+
+ void szxform (LFP_TYPE *a0, LFP_TYPE *a1, LFP_TYPE *a2, /* numerator coefficients */
+ LFP_TYPE *b0, LFP_TYPE *b1, LFP_TYPE *b2, /* denominator coefficients */
+ LFP_TYPE fc, /* Filter cutoff frequency */
+ LFP_TYPE fs, /* sampling rate */
+ LFP_TYPE *k, /* overall gain factor */
+ LFP_TYPE *coef);
+
+ void bilinear (LFP_TYPE a0, LFP_TYPE a1, LFP_TYPE a2, /* numerator coefficients */
+ LFP_TYPE b0, LFP_TYPE b1, LFP_TYPE b2, /* denominator coefficients */
+ LFP_TYPE *k, /* overall gain factor */
+ LFP_TYPE fs, /* sampling rate */
+ LFP_TYPE *coef); /* pointer to 4 iir coefficients */
+
+ void prewarp (LFP_TYPE *a0, LFP_TYPE *a1, LFP_TYPE *a2, LFP_TYPE fc, LFP_TYPE fs);
+
+ float* m_returnBuffer; /* store float buffer data for GetOutput() */
+ unsigned int m_returnSamples; /* store number of samples for GetOutput() */
+ unsigned int m_channelCount; /* number of channels passed by engine */
+ unsigned int m_lfeChannel; /* channel number of the lfe channel */
+ bool m_initialized; /* filter structure has been initialized */
+ bool m_needsLPF; /* has been up/downmixed to include LFE */
};
View
2  xbmc/cores/AudioEngine/Utils/AEWAVLoader.cpp
@@ -273,7 +273,7 @@ bool CAEWAVLoader::Remap(CAEChannelInfo to, enum AEStdChLayout stdChLayout/* = A
};
CAERemap remap;
- if (!remap.Initialize(layouts[m_channelCount - 1], to, false, false, stdChLayout))
+ if (!remap.Initialize(layouts[m_channelCount - 1], to, false, m_sampleRate, false, stdChLayout))
return false;
float *remapped = (float*)_aligned_malloc(sizeof(float) * m_frameCount * to.Count(), 16);
Please sign in to comment.
Something went wrong with that request. Please try again.