aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorChris Robinson <[email protected]>2016-03-26 17:33:49 -0700
committerChris Robinson <[email protected]>2016-03-26 17:33:49 -0700
commit67db77452fcff2db20294c2e062ffb3684a0b710 (patch)
tree27c388b23cb4b66d9b26733ac25ca7a380cf9fad
parent7f4bd13f60836da8c112cef6badf5a5fe464d51f (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.
-rw-r--r--Alc/bformatdec.c111
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;
+ }
}
}