From 51655ef153a69a2cd2e34e8791a4e3b9088962fe Mon Sep 17 00:00:00 2001
From: Mark Harmstone <mark@harmstone.com>
Date: Sun, 22 Mar 2015 18:19:17 +0000
Subject: dsound: Implement EAX decorrelator.

---
 dlls/dsound/dsound_eax.h |  3 +++
 dlls/dsound/eax.c        | 46 ++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 49 insertions(+)

diff --git a/dlls/dsound/dsound_eax.h b/dlls/dsound/dsound_eax.h
index 184f7ce..c33a2ff 100644
--- a/dlls/dsound/dsound_eax.h
+++ b/dlls/dsound/dsound_eax.h
@@ -151,6 +151,9 @@ typedef struct {
         unsigned int Offset[4];
     } Early;
 
+    DelayLine Decorrelator;
+    unsigned int DecoTap[3];
+
     unsigned int Offset;
 } eax_buffer_info;
 
diff --git a/dlls/dsound/eax.c b/dlls/dsound/eax.c
index 964e5f8..d40b7d6 100644
--- a/dlls/dsound/eax.c
+++ b/dlls/dsound/eax.c
@@ -92,11 +92,21 @@ 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 const float DECO_FRACTION = 0.15f;
+static const float DECO_MULTIPLIER = 2.0f;
+
 static const float EARLY_LINE_LENGTH[4] =
 {
     0.0015f, 0.0045f, 0.0135f, 0.0405f
 };
 
+static const float LATE_LINE_LENGTH[4] =
+{
+    0.0211f, 0.0311f, 0.0461f, 0.0680f
+};
+
+static const float LATE_LINE_MULTIPLIER = 4.0f;
+
 static float lpFilter2P(FILTER *iir, unsigned int offset, float input)
 {
     float *history = &iir->history[offset*2];
@@ -273,6 +283,26 @@ static float lpCoeffCalc(float g, float cw)
     return a;
 }
 
+static void UpdateDecorrelator(float density, unsigned int frequency, eax_buffer_info *State)
+{
+    unsigned int index;
+    float length;
+
+    /* The late reverb inputs are decorrelated to smooth the reverb tail and
+     * reduce harsh echos.  The first tap occurs immediately, while the
+     * remaining taps are delayed by multiples of a fraction of the smallest
+     * cyclical delay time.
+     *
+     * offset[index] = (FRACTION (MULTIPLIER^index)) smallest_delay
+     */
+    for (index = 0; index < 3; index++)
+    {
+        length = (DECO_FRACTION * powf(DECO_MULTIPLIER, (float)index)) *
+                 LATE_LINE_LENGTH[0] * (1.0f + (density * LATE_LINE_MULTIPLIER));
+        State->DecoTap[index] = fastf2u(length * frequency);
+    }
+}
+
 static float CalcI3DL2HFreq(float hfRef, unsigned int frequency)
 {
     return cosf(M_PI*2.0f * hfRef / frequency);
@@ -333,6 +363,13 @@ static BOOL AllocLines(unsigned int frequency, IDirectSoundBufferImpl *dsb)
         totalSamples += CalcLineLength(EARLY_LINE_LENGTH[index], totalSamples,
                                        frequency, &dsb->eax.Early.Delay[index]);
 
+    /* The decorrelator line is calculated from the lowest reverb density (a
+     * parameter value of 1). */
+    length = (DECO_FRACTION * DECO_MULTIPLIER * DECO_MULTIPLIER) *
+             LATE_LINE_LENGTH[0] * (1.0f + LATE_LINE_MULTIPLIER);
+    totalSamples += CalcLineLength(length, totalSamples, frequency,
+                                   &dsb->eax.Decorrelator);
+
     if (totalSamples != dsb->eax.TotalSamples)
     {
         TRACE("New reverb buffer length: %u samples (%f sec)\n", totalSamples, totalSamples/(float)frequency);
@@ -350,6 +387,7 @@ static BOOL AllocLines(unsigned int frequency, IDirectSoundBufferImpl *dsb)
 
     /* Update all delays to reflect the new sample buffer. */
     RealizeLineOffset(dsb->eax.SampleBuffer, &dsb->eax.Delay);
+    RealizeLineOffset(dsb->eax.SampleBuffer, &dsb->eax.Decorrelator);
     for(index = 0; index < 4; index++)
     {
         RealizeLineOffset(dsb->eax.SampleBuffer, &dsb->eax.Early.Delay[index]);
@@ -390,6 +428,8 @@ static void ReverbUpdate(IDirectSoundBufferImpl *dsb)
     UpdateEarlyLines(dsb->device->eax.eax_props.flGain,
                     dsb->device->eax.eax_props.flReflectionsGain,
                     dsb->device->eax.eax_props.flLateReverbDelay, &dsb->eax);
+
+    UpdateDecorrelator(dsb->device->eax.eax_props.flDensity, dsb->device->pwfx->nSamplesPerSec, &dsb->eax);
 }
 
 static BOOL ReverbDeviceUpdate(DirectSoundDevice *dev)
@@ -428,6 +468,12 @@ void init_eax_buffer(IDirectSoundBufferImpl *dsb)
         dsb->eax.Early.Offset[index] = 0;
     }
 
+    dsb->eax.Decorrelator.Mask = 0;
+    dsb->eax.Decorrelator.Line = NULL;
+    dsb->eax.DecoTap[0] = 0;
+    dsb->eax.DecoTap[1] = 0;
+    dsb->eax.DecoTap[2] = 0;
+
     dsb->eax.Offset = 0;
 
     ReverbUpdate(dsb);
-- 
2.3.3

