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:
@@ -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;
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user