aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorChris Robinson <[email protected]>2017-07-31 23:49:48 -0700
committerChris Robinson <[email protected]>2017-07-31 23:49:48 -0700
commit8a735d0ba9db9b4b992172dc197396cc655264d5 (patch)
tree870ee7ed74e6028d92f3fc431c9b3c3243e08070
parent88c0d22e7c72109fb5d7b2ee3276680d36971368 (diff)
Add a front-stablizer config option for surround sound modes
This improves a stereo (front-left + front-right) sound "image" by generating a front-center channel signal. Done correctly, it helps reduce the comb effects and phase errors associated with using only two speakers to simulate center sounds. Note that it shouldn't be used if the front-center channel is already included in the positional audio mix (the dialog effect is okay). In general, it may actually be better to exclude the front-center channel from the positional audio mix and use this to generate front-center output.
-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