aboutsummaryrefslogtreecommitdiffstats
path: root/Alc
diff options
context:
space:
mode:
Diffstat (limited to 'Alc')
-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;
+ }
}
}