diff options
author | Chris Robinson <[email protected]> | 2016-03-26 17:33:49 -0700 |
---|---|---|
committer | Chris Robinson <[email protected]> | 2016-03-26 17:33:49 -0700 |
commit | 67db77452fcff2db20294c2e062ffb3684a0b710 (patch) | |
tree | 27c388b23cb4b66d9b26733ac25ca7a380cf9fad /Alc/bformatdec.c | |
parent | 7f4bd13f60836da8c112cef6badf5a5fe464d51f (diff) |
Add distance compensation to the HQ decoder
This only compensates for timing and gain differences caused by differences in
the physical speaker distances. It's not near-field compensation. This also
relies on having proper distance values defined in the ambdec definition file.
Diffstat (limited to 'Alc/bformatdec.c')
-rw-r--r-- | Alc/bformatdec.c | 111 |
1 files changed, 107 insertions, 4 deletions
diff --git a/Alc/bformatdec.c b/Alc/bformatdec.c index 3a07ac36..7c202228 100644 --- a/Alc/bformatdec.c +++ b/Alc/bformatdec.c @@ -161,6 +161,8 @@ static void init_encoder(void) } +#define MAX_DELAY_LENGTH 128 + /* NOTE: Low-frequency (LF) fields and BandSplitter filters are unused with * single-band decoding */ @@ -181,6 +183,14 @@ typedef struct BFormatDec { ALuint NumChannels; } UpSampler; + struct { + alignas(16) ALfloat Buffer[MAX_DELAY_LENGTH]; + ALfloat Gain; + ALuint Length; /* Valid range is [0...MAX_DELAY_LENGTH). */ + } Delay[MAX_OUTPUT_CHANNELS]; + + ALfloat ChannelMix[BUFFERSIZE]; + ALuint NumChannels; ALboolean DualBand; ALboolean Periphonic; @@ -229,7 +239,7 @@ void bformatdec_reset(BFormatDec *dec, const AmbDecConf *conf, ALuint chancount, 0, 1, 3, 4, 8, 9, 15 }; const ALfloat *coeff_scale = UnitScale; - ALfloat ratio; + ALfloat maxdist, ratio; ALuint i; al_free(dec->Samples); @@ -345,6 +355,45 @@ void bformatdec_reset(BFormatDec *dec, const AmbDecConf *conf, ALuint chancount, } } } + + + maxdist = 0.0f; + for(i = 0;i < conf->NumSpeakers;i++) + maxdist = maxf(maxdist, conf->Speakers[i].Distance); + + memset(dec->Delay, 0, sizeof(dec->Delay)); + if(maxdist > 0.0f) + { + for(i = 0;i < conf->NumSpeakers;i++) + { + ALuint chan = chanmap[i]; + ALfloat delay; + + /* Distance compensation only delays in steps of the sample rate. + * This is a bit less accurate since the delay time falls to the + * nearest sample time, but it's far simpler as it doesn't have to + * deal with phase offsets. This means at 48khz, for instance, the + * distance delay will be in steps of about 7 millimeters. + */ + delay = floorf((maxdist-conf->Speakers[i].Distance) / SPEEDOFSOUNDMETRESPERSEC * + (ALfloat)srate + 0.5f); + if(delay >= (ALfloat)MAX_DELAY_LENGTH) + ERR("Delay for speaker \"%s\" exceeds buffer length (%f >= %u)\n", + al_string_get_cstr(conf->Speakers[i].Name), delay, MAX_DELAY_LENGTH); + + dec->Delay[chan].Length = (ALuint)clampf(delay, 0.0f, (ALfloat)(MAX_DELAY_LENGTH-1)); + dec->Delay[chan].Gain = conf->Speakers[i].Distance / maxdist; + TRACE("Channel %u \"%s\" distance compensation: %u samples, %f gain\n", chan, + al_string_get_cstr(conf->Speakers[i].Name), dec->Delay[chan].Length, + dec->Delay[chan].Gain + ); + } + } + else + { + for(i = 0;i < conf->NumSpeakers;i++) + dec->Delay[chanmap[i]].Gain = 1.0f; + } } @@ -374,17 +423,71 @@ void bformatdec_process(struct BFormatDec *dec, ALfloat (*restrict OutBuffer)[BU for(chan = 0;chan < OutChannels;chan++) { - apply_row(OutBuffer[chan], dec->MatrixHF[chan], dec->SamplesHF, + memset(dec->ChannelMix, 0, SamplesToDo*sizeof(ALfloat)); + apply_row(dec->ChannelMix, dec->MatrixHF[chan], dec->SamplesHF, dec->NumChannels, SamplesToDo); - apply_row(OutBuffer[chan], dec->MatrixLF[chan], dec->SamplesLF, + apply_row(dec->ChannelMix, dec->MatrixLF[chan], dec->SamplesLF, dec->NumChannels, SamplesToDo); + + if(dec->Delay[chan].Length > 0) + { + const ALuint base = dec->Delay[chan].Length; + if(SamplesToDo >= base) + { + for(i = 0;i < base;i++) + OutBuffer[chan][i] += dec->Delay[chan].Buffer[i] * dec->Delay[chan].Gain; + for(;i < SamplesToDo;i++) + OutBuffer[chan][i] += dec->ChannelMix[i-base] * dec->Delay[chan].Gain; + memcpy(dec->Delay[chan].Buffer, &dec->ChannelMix[SamplesToDo-base], + base*sizeof(ALfloat)); + } + else + { + for(i = 0;i < SamplesToDo;i++) + OutBuffer[chan][i] += dec->Delay[chan].Buffer[i] * dec->Delay[chan].Gain; + memmove(dec->Delay[chan].Buffer, dec->Delay[chan].Buffer+SamplesToDo, + base - SamplesToDo); + memcpy(dec->Delay[chan].Buffer+base-SamplesToDo, dec->ChannelMix, + SamplesToDo*sizeof(ALfloat)); + } + } + else for(i = 0;i < SamplesToDo;i++) + OutBuffer[chan][i] += dec->ChannelMix[i] * dec->Delay[chan].Gain; } } else { for(chan = 0;chan < OutChannels;chan++) - apply_row(OutBuffer[chan], dec->MatrixHF[chan], InSamples, + { + memset(dec->ChannelMix, 0, SamplesToDo*sizeof(ALfloat)); + apply_row(dec->ChannelMix, dec->MatrixHF[chan], InSamples, dec->NumChannels, SamplesToDo); + + if(dec->Delay[chan].Length > 0) + { + const ALuint base = dec->Delay[chan].Length; + if(SamplesToDo >= base) + { + for(i = 0;i < base;i++) + OutBuffer[chan][i] += dec->Delay[chan].Buffer[i] * dec->Delay[chan].Gain; + for(;i < SamplesToDo;i++) + OutBuffer[chan][i] += dec->ChannelMix[i-base] * dec->Delay[chan].Gain; + memcpy(dec->Delay[chan].Buffer, &dec->ChannelMix[SamplesToDo-base], + base*sizeof(ALfloat)); + } + else + { + for(i = 0;i < SamplesToDo;i++) + OutBuffer[chan][i] += dec->Delay[chan].Buffer[i] * dec->Delay[chan].Gain; + memmove(dec->Delay[chan].Buffer, dec->Delay[chan].Buffer+SamplesToDo, + base - SamplesToDo); + memcpy(dec->Delay[chan].Buffer+base-SamplesToDo, dec->ChannelMix, + SamplesToDo*sizeof(ALfloat)); + } + } + else for(i = 0;i < SamplesToDo;i++) + OutBuffer[chan][i] += dec->ChannelMix[i] * dec->Delay[chan].Gain; + } } } |