diff options
author | Chris Robinson <[email protected]> | 2017-02-19 22:47:59 -0800 |
---|---|---|
committer | Chris Robinson <[email protected]> | 2017-02-19 22:47:59 -0800 |
commit | 3761336e6cf49a7cd5075424b3c8c4a1f5e5b226 (patch) | |
tree | 109b4c30187680c8216186445b728c1592fad823 | |
parent | 9da152a9c8b9f3a6f019c77e0324772344fc9156 (diff) |
Apply distance compensation when writing to the output
-rw-r--r-- | Alc/ALu.c | 51 | ||||
-rw-r--r-- | Alc/bformatdec.c | 115 | ||||
-rw-r--r-- | Alc/bformatdec.h | 2 | ||||
-rw-r--r-- | Alc/panning.c | 48 | ||||
-rw-r--r-- | OpenAL32/Include/alMain.h | 12 |
5 files changed, 106 insertions, 122 deletions
@@ -1373,15 +1373,39 @@ static inline ALubyte aluF2UB(ALfloat val) { return aluF2B(val)+128; } #define DECL_TEMPLATE(T, func) \ -static void Write_##T(ALfloatBUFFERSIZE *InBuffer, ALvoid *OutBuffer, \ - ALsizei SamplesToDo, ALsizei numchans) \ +static void Write_##T(const ALfloatBUFFERSIZE *InBuffer, ALvoid *OutBuffer, \ + DistanceComp *distcomp, ALsizei SamplesToDo, \ + ALsizei numchans) \ { \ ALsizei i, j; \ for(j = 0;j < numchans;j++) \ { \ const ALfloat *in = InBuffer[j]; \ T *restrict out = (T*)OutBuffer + j; \ - for(i = 0;i < SamplesToDo;i++) \ + const ALfloat gain = distcomp[j].Gain; \ + const ALsizei base = distcomp[j].Length; \ + if(base > 0 || gain != 1.0f) \ + { \ + if(SamplesToDo >= base) \ + { \ + for(i = 0;i < base;i++) \ + out[i*numchans] = func(distcomp[j].Buffer[i]*gain); \ + for(;i < SamplesToDo;i++) \ + out[i*numchans] = func(in[i-base]*gain); \ + memcpy(distcomp[j].Buffer, &in[SamplesToDo-base], \ + base*sizeof(ALfloat)); \ + } \ + else \ + { \ + for(i = 0;i < SamplesToDo;i++) \ + out[i*numchans] = func(distcomp[j].Buffer[i]*gain); \ + memmove(distcomp[j].Buffer, distcomp[j].Buffer+SamplesToDo, \ + (base-SamplesToDo)*sizeof(ALfloat)); \ + memcpy(distcomp[j].Buffer+base-SamplesToDo, in, \ + SamplesToDo*sizeof(ALfloat)); \ + } \ + } \ + else for(i = 0;i < SamplesToDo;i++) \ out[i*numchans] = func(in[i]); \ } \ } @@ -1564,33 +1588,34 @@ void aluMixData(ALCdevice *device, ALvoid *buffer, ALsizei size) { ALfloat (*OutBuffer)[BUFFERSIZE] = device->RealOut.Buffer; ALsizei OutChannels = device->RealOut.NumChannels; + DistanceComp *DistComp = device->ChannelDelay; -#define WRITE(T, a, b, c, d) do { \ - Write_##T((a), (b), (c), (d)); \ - buffer = (T*)buffer + (c)*(d); \ +#define WRITE(T, a, b, c, d, e) do { \ + Write_##T(SAFE_CONST(ALfloatBUFFERSIZE*,(a)), (b), (c), (d), (e)); \ + buffer = (T*)buffer + (d)*(e); \ } while(0) switch(device->FmtType) { case DevFmtByte: - WRITE(ALbyte, OutBuffer, buffer, SamplesToDo, OutChannels); + WRITE(ALbyte, OutBuffer, buffer, DistComp, SamplesToDo, OutChannels); break; case DevFmtUByte: - WRITE(ALubyte, OutBuffer, buffer, SamplesToDo, OutChannels); + WRITE(ALubyte, OutBuffer, buffer, DistComp, SamplesToDo, OutChannels); break; case DevFmtShort: - WRITE(ALshort, OutBuffer, buffer, SamplesToDo, OutChannels); + WRITE(ALshort, OutBuffer, buffer, DistComp, SamplesToDo, OutChannels); break; case DevFmtUShort: - WRITE(ALushort, OutBuffer, buffer, SamplesToDo, OutChannels); + WRITE(ALushort, OutBuffer, buffer, DistComp, SamplesToDo, OutChannels); break; case DevFmtInt: - WRITE(ALint, OutBuffer, buffer, SamplesToDo, OutChannels); + WRITE(ALint, OutBuffer, buffer, DistComp, SamplesToDo, OutChannels); break; case DevFmtUInt: - WRITE(ALuint, OutBuffer, buffer, SamplesToDo, OutChannels); + WRITE(ALuint, OutBuffer, buffer, DistComp, SamplesToDo, OutChannels); break; case DevFmtFloat: - WRITE(ALfloat, OutBuffer, buffer, SamplesToDo, OutChannels); + WRITE(ALfloat, OutBuffer, buffer, DistComp, SamplesToDo, OutChannels); break; } #undef WRITE diff --git a/Alc/bformatdec.c b/Alc/bformatdec.c index 549cd42c..5b848ee0 100644 --- a/Alc/bformatdec.c +++ b/Alc/bformatdec.c @@ -156,10 +156,6 @@ static void init_bformatdec(void) } -#define MAX_DELAY_LENGTH 128 - -#define INVALID_UPSAMPLE_INDEX INT_MAX - /* NOTE: BandSplitter filters are unused with single-band decoding */ typedef struct BFormatDec { ALboolean Enabled[MAX_OUTPUT_CHANNELS]; @@ -179,11 +175,6 @@ typedef struct BFormatDec { alignas(16) ALfloat ChannelMix[BUFFERSIZE]; struct { - alignas(16) ALfloat Buffer[MAX_DELAY_LENGTH]; - ALsizei Length; /* Valid range is [0...MAX_DELAY_LENGTH). */ - } Delay[MAX_OUTPUT_CHANNELS]; - - struct { BandSplitter XOver; ALfloat Gains[FB_Max]; } UpSampler[4]; @@ -235,14 +226,13 @@ int bformatdec_isPeriphonic(const struct BFormatDec *dec) return dec->Periphonic; } -void bformatdec_reset(BFormatDec *dec, const AmbDecConf *conf, ALsizei chancount, ALuint srate, const ALsizei chanmap[MAX_OUTPUT_CHANNELS], int flags) +void bformatdec_reset(BFormatDec *dec, const AmbDecConf *conf, ALsizei chancount, ALuint srate, const ALsizei chanmap[MAX_OUTPUT_CHANNELS]) { static const ALsizei map2DTo3D[MAX_AMBI2D_COEFFS] = { 0, 1, 3, 4, 8, 9, 15 }; const ALfloat *coeff_scale = UnitScale; - ALfloat distgain[MAX_OUTPUT_CHANNELS]; - ALfloat maxdist, ratio; + ALfloat ratio; ALsizei i; al_free(dec->Samples); @@ -300,41 +290,6 @@ void bformatdec_reset(BFormatDec *dec, const AmbDecConf *conf, ALsizei chancount dec->UpSampler[3].Gains[FB_LowFreq] = 0.0f; } - maxdist = 0.0f; - for(i = 0;i < conf->NumSpeakers;i++) - { - maxdist = maxf(maxdist, conf->Speakers[i].Distance); - distgain[i] = 1.0f; - } - - memset(dec->Delay, 0, sizeof(dec->Delay)); - if((flags&BFDF_DistanceComp) && maxdist > 0.0f) - { - for(i = 0;i < conf->NumSpeakers;i++) - { - ALsizei 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)); - distgain[i] = conf->Speakers[i].Distance / maxdist; - TRACE("Channel %u \"%s\" distance compensation: %d samples, %f gain\n", chan, - al_string_get_cstr(conf->Speakers[i].Name), dec->Delay[chan].Length, distgain[i] - ); - } - } - memset(&dec->Matrix, 0, sizeof(dec->Matrix)); if(conf->FreqBands == 1) { @@ -356,7 +311,7 @@ void bformatdec_reset(BFormatDec *dec, const AmbDecConf *conf, ALsizei chancount else if(j == 5) gain = conf->HFOrderGain[3]; if((conf->ChanMask&(1<<l))) dec->Matrix.Single[chan][j] = conf->HFMatrix[i][k++] / coeff_scale[l] * - gain * distgain[i]; + gain; } } else @@ -369,7 +324,7 @@ void bformatdec_reset(BFormatDec *dec, const AmbDecConf *conf, ALsizei chancount else if(j == 9) gain = conf->HFOrderGain[3]; if((conf->ChanMask&(1<<j))) dec->Matrix.Single[chan][j] = conf->HFMatrix[i][k++] / coeff_scale[j] * - gain * distgain[i]; + gain; } } } @@ -400,8 +355,7 @@ void bformatdec_reset(BFormatDec *dec, const AmbDecConf *conf, ALsizei chancount else if(j == 5) gain = conf->HFOrderGain[3] * ratio; if((conf->ChanMask&(1<<l))) dec->Matrix.Dual[chan][FB_HighFreq][j] = conf->HFMatrix[i][k++] / - coeff_scale[l] * gain * - distgain[i]; + coeff_scale[l] * gain; } for(j = 0,k = 0;j < MAX_AMBI2D_COEFFS;j++) { @@ -412,8 +366,7 @@ void bformatdec_reset(BFormatDec *dec, const AmbDecConf *conf, ALsizei chancount else if(j == 5) gain = conf->LFOrderGain[3] / ratio; if((conf->ChanMask&(1<<l))) dec->Matrix.Dual[chan][FB_LowFreq][j] = conf->LFMatrix[i][k++] / - coeff_scale[l] * gain * - distgain[i]; + coeff_scale[l] * gain; } } else @@ -426,8 +379,7 @@ void bformatdec_reset(BFormatDec *dec, const AmbDecConf *conf, ALsizei chancount else if(j == 9) gain = conf->HFOrderGain[3] * ratio; if((conf->ChanMask&(1<<j))) dec->Matrix.Dual[chan][FB_HighFreq][j] = conf->HFMatrix[i][k++] / - coeff_scale[j] * gain * - distgain[i]; + coeff_scale[j] * gain; } for(j = 0,k = 0;j < MAX_AMBI_COEFFS;j++) { @@ -437,8 +389,7 @@ void bformatdec_reset(BFormatDec *dec, const AmbDecConf *conf, ALsizei chancount else if(j == 9) gain = conf->LFOrderGain[3] / ratio; if((conf->ChanMask&(1<<j))) dec->Matrix.Dual[chan][FB_LowFreq][j] = conf->LFMatrix[i][k++] / - coeff_scale[j] * gain * - distgain[i]; + coeff_scale[j] * gain; } } } @@ -471,29 +422,7 @@ void bformatdec_process(struct BFormatDec *dec, ALfloat (*restrict OutBuffer)[BU SamplesToDo ); - if(dec->Delay[chan].Length > 0) - { - const ALsizei base = dec->Delay[chan].Length; - if(SamplesToDo >= base) - { - for(i = 0;i < base;i++) - OutBuffer[chan][i] += dec->Delay[chan].Buffer[i]; - for(;i < SamplesToDo;i++) - OutBuffer[chan][i] += dec->ChannelMix[i-base]; - 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]; - 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++) + for(i = 0;i < SamplesToDo;i++) OutBuffer[chan][i] += dec->ChannelMix[i]; } } @@ -508,29 +437,7 @@ void bformatdec_process(struct BFormatDec *dec, ALfloat (*restrict OutBuffer)[BU MixMatrixRow(dec->ChannelMix, dec->Matrix.Single[chan], InSamples, dec->NumChannels, 0, SamplesToDo); - if(dec->Delay[chan].Length > 0) - { - const ALsizei base = dec->Delay[chan].Length; - if(SamplesToDo >= base) - { - for(i = 0;i < base;i++) - OutBuffer[chan][i] += dec->Delay[chan].Buffer[i]; - for(;i < SamplesToDo;i++) - OutBuffer[chan][i] += dec->ChannelMix[i-base]; - 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]; - 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++) + for(i = 0;i < SamplesToDo;i++) OutBuffer[chan][i] += dec->ChannelMix[i]; } } @@ -570,6 +477,8 @@ void bformatdec_upSample(struct BFormatDec *dec, ALfloat (*restrict OutBuffer)[B } +#define INVALID_UPSAMPLE_INDEX INT_MAX + static ALsizei GetACNIndex(const BFChannelConfig *chans, ALsizei numchans, ALsizei acn) { ALsizei i; diff --git a/Alc/bformatdec.h b/Alc/bformatdec.h index 5f6e230d..97f36d0e 100644 --- a/Alc/bformatdec.h +++ b/Alc/bformatdec.h @@ -33,7 +33,7 @@ struct BFormatDec *bformatdec_alloc(); void bformatdec_free(struct BFormatDec *dec); int bformatdec_getOrder(const struct BFormatDec *dec); int bformatdec_isPeriphonic(const struct BFormatDec *dec); -void bformatdec_reset(struct BFormatDec *dec, const struct AmbDecConf *conf, ALsizei chancount, ALuint srate, const ALsizei chanmap[MAX_OUTPUT_CHANNELS], int flags); +void bformatdec_reset(struct BFormatDec *dec, const struct AmbDecConf *conf, ALsizei chancount, ALuint srate, const ALsizei chanmap[MAX_OUTPUT_CHANNELS]); /* Decodes the ambisonic input to the given output channels. */ void bformatdec_process(struct BFormatDec *dec, ALfloat (*restrict OutBuffer)[BUFFERSIZE], ALsizei OutChannels, const ALfloat (*restrict InSamples)[BUFFERSIZE], ALsizei SamplesToDo); diff --git a/Alc/panning.c b/Alc/panning.c index eb2ec0c8..80532bc4 100644 --- a/Alc/panning.c +++ b/Alc/panning.c @@ -693,13 +693,10 @@ static void InitCustomPanning(ALCdevice *device, const AmbDecConf *conf, const A static void InitHQPanning(ALCdevice *device, const AmbDecConf *conf, const ALsizei speakermap[MAX_OUTPUT_CHANNELS]) { const char *devname; - int decflags = 0; size_t count; size_t i; devname = al_string_get_cstr(device->DeviceName); - if(GetConfigValueBool(devname, "decoder", "distance-comp", 1)) - decflags |= BFDF_DistanceComp; if((conf->ChanMask&AMBI_PERIPHONIC_MASK)) { @@ -731,8 +728,7 @@ static void InitHQPanning(ALCdevice *device, const AmbDecConf *conf, const ALsiz (conf->ChanMask > 0xf) ? (conf->ChanMask > 0x1ff) ? "third" : "second" : "first", (conf->ChanMask&AMBI_PERIPHONIC_MASK) ? " periphonic" : "" ); - bformatdec_reset(device->AmbiDecoder, conf, count, device->Frequency, - speakermap, decflags); + bformatdec_reset(device->AmbiDecoder, conf, count, device->Frequency, speakermap); if(bformatdec_getOrder(device->AmbiDecoder) < 2) { @@ -759,6 +755,41 @@ static void InitHQPanning(ALCdevice *device, const AmbDecConf *conf, const ALsiz } device->FOAOut.CoeffCount = 0; } + + ALfloat maxdist = 0.0f; + for(i = 0;i < (size_t)conf->NumSpeakers;i++) + maxdist = maxf(maxdist, conf->Speakers[i].Distance); + + if(GetConfigValueBool(devname, "decoder", "distance-comp", 1) && maxdist > 0.0f) + { + ALfloat srate = (ALfloat)device->Frequency; + for(i = 0;i < (size_t)conf->NumSpeakers;i++) + { + ALsizei chan = speakermap[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 * + 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); + + device->ChannelDelay[chan].Length = (ALsizei)clampf( + delay, 0.0f, (ALfloat)(MAX_DELAY_LENGTH-1) + ); + device->ChannelDelay[chan].Gain = conf->Speakers[i].Distance / maxdist; + TRACE("Channel %u \"%s\" distance compensation: %d samples, %f gain\n", chan, + al_string_get_cstr(conf->Speakers[i].Name), device->ChannelDelay[chan].Length, + device->ChannelDelay[chan].Gain + ); + } + } } static void InitHrtfPanning(ALCdevice *device, bool hoa_mode) @@ -887,6 +918,13 @@ void aluInitRenderer(ALCdevice *device, ALint hrtf_id, enum HrtfRequestMode hrtf device->Dry.CoeffCount = 0; device->Dry.NumChannels = 0; + memset(device->ChannelDelay, 0, sizeof(device->ChannelDelay)); + for(i = 0;i < MAX_OUTPUT_CHANNELS;i++) + { + device->ChannelDelay[i].Gain = 1.0f; + device->ChannelDelay[i].Length = 0; + } + if(device->FmtChans != DevFmtStereo) { ALsizei speakermap[MAX_OUTPUT_CHANNELS]; diff --git a/OpenAL32/Include/alMain.h b/OpenAL32/Include/alMain.h index 947a16ba..bb8a57ce 100644 --- a/OpenAL32/Include/alMain.h +++ b/OpenAL32/Include/alMain.h @@ -603,6 +603,15 @@ typedef struct HrtfEntry { TYPEDEF_VECTOR(HrtfEntry, vector_HrtfEntry) +/* Maximum delay in samples for speaker distance compensation. */ +#define MAX_DELAY_LENGTH 128 + +typedef struct DistanceComp { + ALfloat Gain; + ALsizei Length; /* Valid range is [0...MAX_DELAY_LENGTH). */ + alignas(16) ALfloat Buffer[MAX_DELAY_LENGTH]; +} DistanceComp; + /* Size for temporary storage of buffer data, in ALfloats. Larger values need * more memory, while smaller values may need more iterations. The value needs * to be a sensible size, however, as it constrains the max stepping value used @@ -723,6 +732,9 @@ struct ALCdevice_struct ALsizei NumChannels; } RealOut; + /* Delay buffers used to compensate for speaker distances. */ + DistanceComp ChannelDelay[MAX_OUTPUT_CHANNELS]; + /* Running count of the mixer invocations, in 31.1 fixed point. This * actually increments *twice* when mixing, first at the start and then at * the end, so the bottom bit indicates if the device is currently mixing |