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.
This commit is contained in:
Chris Robinson
2017-07-31 23:49:48 -07:00
parent 88c0d22e7c
commit 8a735d0ba9
6 changed files with 121 additions and 0 deletions
+3
View File
@@ -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;
+59
View File
@@ -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);
+8
View File
@@ -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 */
+40
View File
@@ -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;
}
+3
View File
@@ -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
+8
View File
@@ -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