diff options
author | Chris Robinson <[email protected]> | 2017-07-31 23:49:48 -0700 |
---|---|---|
committer | Chris Robinson <[email protected]> | 2017-07-31 23:49:48 -0700 |
commit | 8a735d0ba9db9b4b992172dc197396cc655264d5 (patch) | |
tree | 870ee7ed74e6028d92f3fc431c9b3c3243e08070 | |
parent | 88c0d22e7c72109fb5d7b2ee3276680d36971368 (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.c | 3 | ||||
-rw-r--r-- | Alc/ALu.c | 59 | ||||
-rw-r--r-- | Alc/bformatdec.h | 8 | ||||
-rw-r--r-- | Alc/panning.c | 40 | ||||
-rw-r--r-- | OpenAL32/Include/alMain.h | 3 | ||||
-rw-r--r-- | alsoftrc.sample | 8 |
6 files changed, 121 insertions, 0 deletions
@@ -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); 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 |