From 858f3b43a1197c3b9a8e6f2228995451776b29b4 Mon Sep 17 00:00:00 2001
From: Mark Harmstone <mark@harmstone.com>
Date: Sun, 22 Mar 2015 17:57:38 +0000
Subject: dsound: Implement EAX lowpass filter.

---
 dlls/dsound/dsound_eax.h |  7 +++++++
 dlls/dsound/eax.c        | 48 +++++++++++++++++++++++++++++++++++++++++++++++-
 2 files changed, 54 insertions(+), 1 deletion(-)

diff --git a/dlls/dsound/dsound_eax.h b/dlls/dsound/dsound_eax.h
index 132f060..82b1408 100644
--- a/dlls/dsound/dsound_eax.h
+++ b/dlls/dsound/dsound_eax.h
@@ -121,6 +121,11 @@ typedef struct DelayLine
 } DelayLine;
 
 typedef struct {
+    float coeff;
+    float history[2];
+} FILTER;
+
+typedef struct {
     BOOL using_eax;
     unsigned long environment;
     float volume;
@@ -134,6 +139,8 @@ typedef struct {
     float *SampleBuffer;
     unsigned int TotalSamples;
 
+    FILTER LpFilter;
+
     DelayLine Delay;
 
     unsigned int Offset;
diff --git a/dlls/dsound/eax.c b/dlls/dsound/eax.c
index 6ac4cdf..bbb5dad 100644
--- a/dlls/dsound/eax.c
+++ b/dlls/dsound/eax.c
@@ -92,9 +92,24 @@ static const EFXEAXREVERBPROPERTIES efx_presets[] = {
     { 0.0625f, 0.5000f, 0.3162f, 0.8404f, 1.0000f, 7.5600f, 0.9100f, 1.0000f, 0.4864f, 0.0200f, { 0.0000f, 0.0000f, 0.0000f }, 2.4378f, 0.0300f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.0000f, 4.0000f, 1.0000f, 0.9943f, 5000.0000f, 250.0000f, 0.0000f, 0x0 } /* psychotic */
 };
 
+static float lpFilter2P(FILTER *iir, unsigned int offset, float input)
+{
+    float *history = &iir->history[offset*2];
+    float a = iir->coeff;
+    float output = input;
+
+    output = output + (history[0]-output)*a;
+    history[0] = output;
+    output = output + (history[1]-output)*a;
+    history[1] = output;
+
+    return output;
+}
+
 static void VerbPass(IDirectSoundBufferImpl* dsb, float in, float* out)
 {
-    /* stub */
+    /* Low-pass filter the incoming sample. */
+    in = lpFilter2P(&dsb->eax.LpFilter, 0, in);
 
     /* Step all delays forward one sample. */
     dsb->eax.Offset++;
@@ -137,6 +152,27 @@ void process_eax_buffer(IDirectSoundBufferImpl *dsb, float *buf, DWORD count)
     HeapFree(GetProcessHeap(), 0, out);
 }
 
+static float lpCoeffCalc(float g, float cw)
+{
+    float a = 0.0f;
+
+    if (g < 0.9999f) /* 1-epsilon */
+    {
+        /* Be careful with gains < 0.001, as that causes the coefficient head
+         * towards 1, which will flatten the signal */
+        if (g < 0.001f) g = 0.001f;
+        a = (1 - g*cw - sqrtf(2*g*(1-cw) - g*g*(1 - cw*cw))) /
+            (1 - g);
+    }
+
+    return a;
+}
+
+static float CalcI3DL2HFreq(float hfRef, unsigned int frequency)
+{
+    return cosf(M_PI*2.0f * hfRef / frequency);
+}
+
 static unsigned int NextPowerOf2(unsigned int value)
 {
     if (value > 0)
@@ -214,12 +250,18 @@ static BOOL AllocLines(unsigned int frequency, IDirectSoundBufferImpl *dsb)
 
 static void ReverbUpdate(IDirectSoundBufferImpl *dsb)
 {
+    float cw;
+
     /* avoid segfaults in mixing thread when we recalculate the line offsets */
     EnterCriticalSection(&dsb->device->mixlock);
 
     AllocLines(dsb->device->pwfx->nSamplesPerSec, dsb);
 
     LeaveCriticalSection(&dsb->device->mixlock);
+
+    cw = CalcI3DL2HFreq(dsb->device->eax.eax_props.flHFReference, dsb->device->pwfx->nSamplesPerSec);
+
+    dsb->eax.LpFilter.coeff = lpCoeffCalc(dsb->device->eax.eax_props.flGainHF, cw);
 }
 
 static BOOL ReverbDeviceUpdate(DirectSoundDevice *dev)
@@ -238,6 +280,10 @@ void init_eax_buffer(IDirectSoundBufferImpl *dsb)
     dsb->eax.TotalSamples = 0;
     dsb->eax.SampleBuffer = NULL;
 
+    dsb->eax.LpFilter.coeff = 0.0f;
+    dsb->eax.LpFilter.history[0] = 0.0f;
+    dsb->eax.LpFilter.history[1] = 0.0f;
+
     dsb->eax.Delay.Mask = 0;
     dsb->eax.Delay.Line = NULL;
 
-- 
2.3.3

