diff options
-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 |