From 213aef0ba57c925f48c36e6167584a4ee0b7587b Mon Sep 17 00:00:00 2001
From: Mark Harmstone <mark@harmstone.com>
Date: Fri, 27 Mar 2015 21:06:42 +0000
Subject: dsound: Allocate EAX delay lines.

---
 dlls/dsound/dsound_eax.h | 16 ++++++++
 dlls/dsound/eax.c        | 98 +++++++++++++++++++++++++++++++++++++++++++++++-
 2 files changed, 112 insertions(+), 2 deletions(-)

diff --git a/dlls/dsound/dsound_eax.h b/dlls/dsound/dsound_eax.h
index 2ee83b9..132f060 100644
--- a/dlls/dsound/dsound_eax.h
+++ b/dlls/dsound/dsound_eax.h
@@ -85,6 +85,9 @@ typedef struct {
 
 #define EAX_REVERBMIX_USEDISTANCE -1.0f
 
+#define AL_EAXREVERB_MAX_REFLECTIONS_DELAY       (0.3f)
+#define AL_EAXREVERB_MAX_LATE_REVERB_DELAY       (0.1f)
+
 typedef struct {
     float flDensity;
     float flDiffusion;
@@ -111,6 +114,12 @@ typedef struct {
     int   iDecayHFLimit;
 } EFXEAXREVERBPROPERTIES, *LPEFXEAXREVERBPROPERTIES;
 
+typedef struct DelayLine
+{
+    unsigned int Mask;
+    float *Line;
+} DelayLine;
+
 typedef struct {
     BOOL using_eax;
     unsigned long environment;
@@ -121,6 +130,13 @@ typedef struct {
 
 typedef struct {
     float reverb_mix;
+
+    float *SampleBuffer;
+    unsigned int TotalSamples;
+
+    DelayLine Delay;
+
+    unsigned int Offset;
 } eax_buffer_info;
 
 #ifdef __cplusplus
diff --git a/dlls/dsound/eax.c b/dlls/dsound/eax.c
index 4368594..babc2a7 100644
--- a/dlls/dsound/eax.c
+++ b/dlls/dsound/eax.c
@@ -92,14 +92,99 @@ 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 unsigned int fastf2u(float f)
+{
+    return (unsigned int)f;
+}
+
 void process_eax_buffer(IDirectSoundBufferImpl *dsb, float *buf, DWORD count)
 {
     /* stub */
 }
 
+static unsigned int NextPowerOf2(unsigned int value)
+{
+    if (value > 0)
+    {
+        value--;
+        value |= value>>1;
+        value |= value>>2;
+        value |= value>>4;
+        value |= value>>8;
+        value |= value>>16;
+    }
+    return value+1;
+}
+
+static unsigned int CalcLineLength(float length, unsigned int offset, unsigned int frequency, DelayLine *Delay)
+{
+    unsigned int samples;
+
+    /* All line lengths are powers of 2, calculated from their lengths, with
+     * an additional sample in case of rounding errors. */
+    samples = NextPowerOf2(fastf2u(length * frequency) + 1);
+    /* All lines share a single sample buffer. */
+    Delay->Mask = samples - 1;
+    Delay->Line = (float*)offset;
+    /* Return the sample count for accumulation. */
+    return samples;
+}
+
+static void RealizeLineOffset(float *sampleBuffer, DelayLine *Delay)
+{
+    Delay->Line = &sampleBuffer[(unsigned int)Delay->Line];
+}
+
+static BOOL AllocLines(unsigned int frequency, IDirectSoundBufferImpl *dsb)
+{
+    unsigned int totalSamples, index;
+    float length;
+    float *newBuffer = NULL;
+
+    /* All delay line lengths are calculated to accomodate the full range of
+     * lengths given their respective paramters. */
+    totalSamples = 0;
+
+    /* The initial delay is the sum of the reflections and late reverb
+     * delays. */
+    length = AL_EAXREVERB_MAX_REFLECTIONS_DELAY +
+             AL_EAXREVERB_MAX_LATE_REVERB_DELAY;
+    totalSamples += CalcLineLength(length, totalSamples, frequency,
+                                   &dsb->eax.Delay);
+
+    if (totalSamples != dsb->eax.TotalSamples)
+    {
+        TRACE("New reverb buffer length: %u samples (%f sec)\n", totalSamples, totalSamples/(float)frequency);
+
+        if (dsb->eax.SampleBuffer)
+            newBuffer = HeapReAlloc(GetProcessHeap(), 0, dsb->eax.SampleBuffer, sizeof(float) * totalSamples);
+        else
+            newBuffer = HeapAlloc(GetProcessHeap(), 0, sizeof(float) * totalSamples);
+
+        if (newBuffer == NULL)
+            return FALSE;
+        dsb->eax.SampleBuffer = newBuffer;
+        dsb->eax.TotalSamples = totalSamples;
+    }
+
+    /* Update all delays to reflect the new sample buffer. */
+    RealizeLineOffset(dsb->eax.SampleBuffer, &dsb->eax.Delay);
+
+    /* Clear the sample buffer. */
+    for (index = 0; index < dsb->eax.TotalSamples; index++)
+        dsb->eax.SampleBuffer[index] = 0.0f;
+
+    return TRUE;
+}
+
 static void ReverbUpdate(IDirectSoundBufferImpl *dsb)
 {
-    /* stub */
+    /* 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);
 }
 
 static BOOL ReverbDeviceUpdate(DirectSoundDevice *dev)
@@ -115,6 +200,14 @@ static BOOL ReverbDeviceUpdate(DirectSoundDevice *dev)
 
 void init_eax_buffer(IDirectSoundBufferImpl *dsb)
 {
+    dsb->eax.TotalSamples = 0;
+    dsb->eax.SampleBuffer = NULL;
+
+    dsb->eax.Delay.Mask = 0;
+    dsb->eax.Delay.Line = NULL;
+
+    dsb->eax.Offset = 0;
+
     ReverbUpdate(dsb);
 }
 
@@ -136,7 +229,8 @@ static void init_eax(DirectSoundDevice *dev)
 
 void free_eax_buffer(IDirectSoundBufferImpl *dsb)
 {
-    /* stub */
+    if (dsb->eax.SampleBuffer)
+        HeapFree(GetProcessHeap(), 0, dsb->eax.SampleBuffer);
 }
 
 HRESULT WINAPI EAX_Get(IDirectSoundBufferImpl *buf, REFGUID guidPropSet,
-- 
2.3.3