aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Alc/ALc.c3
-rw-r--r--Alc/ALu.c59
-rw-r--r--Alc/bformatdec.h8
-rw-r--r--Alc/panning.c40
-rw-r--r--OpenAL32/Include/alMain.h3
-rw-r--r--alsoftrc.sample8
6 files changed, 121 insertions, 0 deletions
diff --git a/Alc/ALc.c b/Alc/ALc.c
index 8c032ddf..b367dd1d 100644
--- a/Alc/ALc.c
+++ b/Alc/ALc.c
@@ -2433,6 +2433,9 @@ static ALCvoid FreeDevice(ALCdevice *device)
ambiup_free(device->AmbiUp);
device->AmbiUp = NULL;
+ al_free(device->Stablizer);
+ device->Stablizer = NULL;
+
al_free(device->Limiter);
device->Limiter = NULL;
diff --git a/Alc/ALu.c b/Alc/ALu.c
index fa9634b8..5230d4c6 100644
--- a/Alc/ALu.c
+++ b/Alc/ALu.c
@@ -1483,6 +1483,54 @@ static void UpdateContextSources(ALCcontext *ctx, const struct ALeffectslotArray
}
+static void ApplyStablizer(FrontStablizer *Stablizer, ALfloat (*restrict Buffer)[BUFFERSIZE],
+ int lidx, int ridx, int cidx, ALsizei SamplesToDo,
+ ALsizei NumChannels)
+{
+ ALfloat (*restrict lsplit)[BUFFERSIZE] = ASSUME_ALIGNED(Stablizer->LSplit, 16);
+ ALfloat (*restrict rsplit)[BUFFERSIZE] = ASSUME_ALIGNED(Stablizer->RSplit, 16);
+ ALsizei i;
+
+ /* Apply an all-pass to all channels, except the front-left and front-
+ * right, so they maintain the same relative phase.
+ */
+ for(i = 0;i < NumChannels;i++)
+ {
+ if(i == lidx || i == ridx)
+ continue;
+ splitterap_process(&Stablizer->APFilter[i], Buffer[i], SamplesToDo);
+ }
+
+ bandsplit_process(&Stablizer->LFilter, lsplit[1], lsplit[0], Buffer[lidx], SamplesToDo);
+ bandsplit_process(&Stablizer->RFilter, rsplit[1], rsplit[0], Buffer[ridx], SamplesToDo);
+
+ for(i = 0;i < SamplesToDo;i++)
+ {
+ ALfloat lfsum, hfsum;
+ ALfloat m, s, c;
+
+ lfsum = lsplit[0][i] + rsplit[0][i];
+ hfsum = lsplit[1][i] + rsplit[1][i];
+ s = lsplit[0][i] + lsplit[1][i] - rsplit[0][i] - rsplit[1][i];
+
+ /* This pans the separate low- and high-frequency sums between being on
+ * the center channel and the left/right channels. The low-frequency
+ * sum is 1/3rd toward center (2/3rds on left/right) and the high-
+ * frequency sum is 1/4th toward center (3/4ths on left/right). These
+ * values can be tweaked.
+ */
+ m = lfsum*cosf(1.0f/3.0f * F_PI_2) + hfsum*cosf(1.0f/4.0f * F_PI_2);
+ c = lfsum*sinf(1.0f/3.0f * F_PI_2) + hfsum*sinf(1.0f/4.0f * F_PI_2);
+
+ /* The generated center channel signal adds to the existing signal,
+ * while the modified left and right channels replace.
+ */
+ Buffer[lidx][i] = (m + s) * 0.5f;
+ Buffer[ridx][i] = (m - s) * 0.5f;
+ Buffer[cidx][i] += c * 0.5f;
+ }
+}
+
static void ApplyDistanceComp(ALfloatBUFFERSIZE *restrict Samples, DistanceComp *distcomp,
ALfloat *restrict Values, ALsizei SamplesToDo, ALsizei numchans)
{
@@ -1756,6 +1804,17 @@ void aluMixData(ALCdevice *device, ALvoid *OutBuffer, ALsizei NumSamples)
ALfloat (*Buffer)[BUFFERSIZE] = device->RealOut.Buffer;
ALsizei Channels = device->RealOut.NumChannels;
+ if(device->Stablizer)
+ {
+ int lidx = GetChannelIdxByName(device->RealOut, FrontLeft);
+ int ridx = GetChannelIdxByName(device->RealOut, FrontRight);
+ int cidx = GetChannelIdxByName(device->RealOut, FrontCenter);
+ assert(lidx >= 0 && ridx >= 0 && cidx >= 0);
+
+ ApplyStablizer(device->Stablizer, Buffer, lidx, ridx, cidx,
+ SamplesToDo, Channels);
+ }
+
/* Use NFCtrlData for temp value storage. */
ApplyDistanceComp(Buffer, device->ChannelDelay, device->NFCtrlData,
SamplesToDo, Channels);
diff --git a/Alc/bformatdec.h b/Alc/bformatdec.h
index 8f44fc2a..baaecf40 100644
--- a/Alc/bformatdec.h
+++ b/Alc/bformatdec.h
@@ -72,4 +72,12 @@ void splitterap_init(SplitterAllpass *splitter, ALfloat freq_mult);
void splitterap_clear(SplitterAllpass *splitter);
void splitterap_process(SplitterAllpass *splitter, ALfloat *restrict samples, ALsizei count);
+
+typedef struct FrontStablizer {
+ SplitterAllpass APFilter[MAX_OUTPUT_CHANNELS];
+ BandSplitter LFilter, RFilter;
+ alignas(16) ALfloat LSplit[2][BUFFERSIZE];
+ alignas(16) ALfloat RSplit[2][BUFFERSIZE];
+} FrontStablizer;
+
#endif /* BFORMATDEC_H */
diff --git a/Alc/panning.c b/Alc/panning.c
index d1cb6df4..fb510f1e 100644
--- a/Alc/panning.c
+++ b/Alc/panning.c
@@ -1006,6 +1006,9 @@ void aluInitRenderer(ALCdevice *device, ALint hrtf_id, enum HrtfRequestMode hrtf
device->ChannelDelay[i].Length = 0;
}
+ al_free(device->Stablizer);
+ device->Stablizer = NULL;
+
if(device->FmtChans != DevFmtStereo)
{
ALsizei speakermap[MAX_OUTPUT_CHANNELS];
@@ -1084,6 +1087,43 @@ void aluInitRenderer(ALCdevice *device, ALint hrtf_id, enum HrtfRequestMode hrtf
else
InitCustomPanning(device, pconf, speakermap);
+ /* Enable the stablizer only for formats that have front-left, front-
+ * right, and front-center outputs.
+ */
+ switch(device->FmtChans)
+ {
+ case DevFmtX51:
+ case DevFmtX51Rear:
+ case DevFmtX61:
+ case DevFmtX71:
+ if(GetConfigValueBool(devname, NULL, "front-stablizer", 0))
+ {
+ /* Initialize band-splitting filters for the front-left and
+ * front-right channels, with a crossover at 5khz (could be
+ * higher).
+ */
+ ALfloat scale = (ALfloat)(5000.0 / device->Frequency);
+ FrontStablizer *stablizer = al_calloc(16, sizeof(*stablizer));
+
+ bandsplit_init(&stablizer->LFilter, scale);
+ stablizer->RFilter = stablizer->LFilter;
+
+ /* Initialize all-pass filters for all other channels. */
+ splitterap_init(&stablizer->APFilter[0], scale);
+ for(i = 1;i < (size_t)device->RealOut.NumChannels;i++)
+ stablizer->APFilter[i] = stablizer->APFilter[0];
+
+ device->Stablizer = stablizer;
+ }
+ break;
+ case DevFmtMono:
+ case DevFmtStereo:
+ case DevFmtQuad:
+ case DevFmtAmbi3D:
+ break;
+ }
+ TRACE("Front stablizer %s\n", device->Stablizer ? "enabled" : "disabled");
+
ambdec_deinit(&conf);
return;
}
diff --git a/OpenAL32/Include/alMain.h b/OpenAL32/Include/alMain.h
index ca97bff0..f24eb000 100644
--- a/OpenAL32/Include/alMain.h
+++ b/OpenAL32/Include/alMain.h
@@ -380,6 +380,7 @@ extern "C" {
struct Hrtf;
struct HrtfEntry;
+struct FrontStablizer;
struct Compressor;
@@ -777,6 +778,8 @@ struct ALCdevice_struct
ALsizei NumChannels;
} RealOut;
+ struct FrontStablizer *Stablizer;
+
struct Compressor *Limiter;
/* The average speaker distance as determined by the ambdec configuration
diff --git a/alsoftrc.sample b/alsoftrc.sample
index 3e7d0eec..dcf4756c 100644
--- a/alsoftrc.sample
+++ b/alsoftrc.sample
@@ -181,6 +181,14 @@
# than the default has no effect.
#sends = 16
+## front-stablizer:
+# Applies filters to "stablize" front sound imaging. A psychoacoustic method
+# is used to generate a front-center channel signal from the front-left and
+# front-right channels, improving the front response by reducing the combing
+# artifacts and phase errors. Consequently, it will only work with channel
+# configurations that include front-left, front-right, and front-center.
+#front-stablizer = false
+
## output-limiter:
# Applies a gain limiter on the final mixed output. This reduces the volume
# when the output samples would otherwise clamp, avoiding excessive clipping