diff options
author | Chris Robinson <[email protected]> | 2017-03-10 04:35:32 -0800 |
---|---|---|
committer | Chris Robinson <[email protected]> | 2017-03-10 04:35:32 -0800 |
commit | 583d431947be540a33843d68feb5f1456671c298 (patch) | |
tree | 0f85220f58dd446d9732cb3a70bf7917585619c8 /Alc/panning.c | |
parent | d9b1995e95ac3d838566500f1e5496ae71d832e0 (diff) |
Implement NFC filters for Ambisonic rendering
NFC filters currently only work when rendering to ambisonic buffers, which
includes HQ rendering and ambisonic output. There are two new config options:
'decoder/nfc' (default on) enables or disables use of NFC filters globally, and
'decoder/nfc-ref-delay' (default 0) specifies the reference delay parameter for
NFC-HOA rendering with ambisonic output (a value of 0 disables NFC).
Currently, NFC filters rely on having an appropriate value set for
AL_METERS_PER_UNIT to get the correct scaling. HQ rendering uses the averaged
speaker distances as a control/reference, and currently doesn't correct for
individual speaker distances (if the speakers are all equidistant, this is
fine, otherwise per-speaker correction should be done as well).
Diffstat (limited to 'Alc/panning.c')
-rw-r--r-- | Alc/panning.c | 172 |
1 files changed, 111 insertions, 61 deletions
diff --git a/Alc/panning.c b/Alc/panning.c index c4d3e43f..6315328a 100644 --- a/Alc/panning.c +++ b/Alc/panning.c @@ -487,6 +487,88 @@ static const ChannelMap MonoCfg[1] = { { BackRight, { 2.04124145e-1f, -1.08880247e-1f, 0.0f, -1.88586120e-1f, 1.29099444e-1f, 0.0f, 0.0f, 0.0f, 7.45355993e-2f, -3.73460789e-2f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.00000000e+0f } }, }; +static void InitNearFieldCtrl(ALCdevice *device, ALfloat ctrl_dist, ALsizei order, bool periphonic) +{ + const char *devname = al_string_get_cstr(device->DeviceName); + ALsizei i; + + if(GetConfigValueBool(devname, "decoder", "nfc", 1) && ctrl_dist > 0.0f) + { + /* NFC is only used when AvgSpeakerDist is greater than 0, and + * METERS_PER_UNIT is also greater than 0. In addition, NFC can only be + * used when rendering to an ambisonic buffer. + */ + device->AvgSpeakerDist = ctrl_dist; + + device->Dry.NumChannelsPerOrder[0] = 1; + if(periphonic) + for(i = 1;i < order+1;i++) + device->Dry.NumChannelsPerOrder[i] = (i+1)*(i+1) - i*i; + else + for(i = 1;i < order+1;i++) + device->Dry.NumChannelsPerOrder[i] = (i*2+1) - ((i-1)*2+1); + for(;i < MAX_AMBI_ORDER+1;i++) + device->Dry.NumChannelsPerOrder[i] = 0; + } +} + +static void InitDistanceComp(ALCdevice *device, const AmbDecConf *conf, const ALsizei speakermap[MAX_OUTPUT_CHANNELS]) +{ + const char *devname = al_string_get_cstr(device->DeviceName); + ALfloat maxdist = 0.0f; + ALsizei total = 0; + ALsizei i; + + for(i = 0;i < 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 < 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 + ); + + /* Round up to the next 4th sample, so each channel buffer starts + * 16-byte aligned. + */ + total += RoundUp(device->ChannelDelay[chan].Length, 4); + } + } + + if(total > 0) + { + device->ChannelDelay[0].Buffer = al_calloc(16, total * sizeof(ALfloat)); + for(i = 1;i < MAX_OUTPUT_CHANNELS;i++) + { + size_t len = RoundUp(device->ChannelDelay[i-1].Length, 4); + device->ChannelDelay[i].Buffer = device->ChannelDelay[i-1].Buffer + len; + } + } +} + static void InitPanning(ALCdevice *device) { const ChannelMap *chanmap = NULL; @@ -546,10 +628,12 @@ static void InitPanning(ALCdevice *device) if(device->FmtChans >= DevFmtAmbi1 && device->FmtChans <= DevFmtAmbi3) { + const char *devname = al_string_get_cstr(device->DeviceName); const ALsizei *acnmap = (device->AmbiLayout == AmbiLayout_FuMa) ? FuMa2ACN : ACN2ACN; const ALfloat *n3dscale = (device->AmbiScale == AmbiNorm_FuMa) ? FuMa2N3DScale : (device->AmbiScale == AmbiNorm_SN3D) ? SN3D2N3DScale : /*(device->AmbiScale == AmbiNorm_N3D) ?*/ UnitScale; + ALfloat nfc_delay = 0.0f; count = (device->FmtChans == DevFmtAmbi3) ? 16 : (device->FmtChans == DevFmtAmbi2) ? 9 : @@ -585,6 +669,16 @@ static void InitPanning(ALCdevice *device) ambiup_reset(device->AmbiUp, device); } + + if(ConfigValueFloat(devname, "decoder", "nfc-ref-delay", &nfc_delay)) + { + nfc_delay = clampf(nfc_delay, 0.001f, 1000.0f); + InitNearFieldCtrl(device, nfc_delay * SPEEDOFSOUNDMETRESPERSEC, + (device->FmtChans == DevFmtAmbi3) ? 3 : + (device->FmtChans == DevFmtAmbi2) ? 2 : 1, + true + ); + } } else { @@ -612,63 +706,6 @@ static void InitPanning(ALCdevice *device) device->RealOut.NumChannels = 0; } -static void InitDistanceComp(ALCdevice *device, const AmbDecConf *conf, const ALsizei speakermap[MAX_OUTPUT_CHANNELS]) -{ - const char *devname = al_string_get_cstr(device->DeviceName); - ALfloat maxdist = 0.0f; - ALsizei total = 0; - ALsizei i; - - for(i = 0;i < 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 < 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 - ); - - /* Round up to the next 4th sample, so each channel buffer starts - * 16-byte aligned. - */ - total += RoundUp(device->ChannelDelay[chan].Length, 4); - } - } - - if(total > 0) - { - device->ChannelDelay[0].Buffer = al_calloc(16, total * sizeof(ALfloat)); - for(i = 1;i < MAX_OUTPUT_CHANNELS;i++) - { - size_t len = RoundUp(device->ChannelDelay[i-1].Length, 4); - device->ChannelDelay[i].Buffer = device->ChannelDelay[i-1].Buffer + len; - } - } -} - static void InitCustomPanning(ALCdevice *device, const AmbDecConf *conf, const ALsizei speakermap[MAX_OUTPUT_CHANNELS]) { ChannelMap chanmap[MAX_OUTPUT_CHANNELS]; @@ -756,8 +793,9 @@ static void InitCustomPanning(ALCdevice *device, const AmbDecConf *conf, const A static void InitHQPanning(ALCdevice *device, const AmbDecConf *conf, const ALsizei speakermap[MAX_OUTPUT_CHANNELS]) { - size_t count; - size_t i; + ALfloat avg_dist; + ALsizei count; + ALsizei i; if((conf->ChanMask&AMBI_PERIPHONIC_MASK)) { @@ -825,6 +863,15 @@ static void InitHQPanning(ALCdevice *device, const AmbDecConf *conf, const ALsiz device->RealOut.NumChannels = ChannelsFromDevFmt(device->FmtChans); + avg_dist = 0.0f; + for(i = 0;i < conf->NumSpeakers;i++) + avg_dist += conf->Speakers[i].Distance; + avg_dist /= (ALfloat)conf->NumSpeakers; + InitNearFieldCtrl(device, avg_dist, + (conf->ChanMask > 0x1ff) ? 3 : (conf->ChanMask > 0xf) ? 2 : 1, + !!(conf->ChanMask&AMBI_PERIPHONIC_MASK) + ); + InitDistanceComp(device, conf, speakermap); } @@ -879,8 +926,8 @@ static void InitHrtfPanning(ALCdevice *device, bool hoa_mode) { { 1.43315266e-001f, 0.00000000e+000f, -1.90399923e-001f, 0.00000000e+000f, 0.00000000e+000f, 0.00000000e+000f, 1.18020996e-001f, 0.00000000e+000f, 0.00000000e+000f }, { 7.26741039e-002f, 0.00000000e+000f, -1.24646009e-001f, 0.00000000e+000f, 0.00000000e+000f, 0.00000000e+000f, 1.49618920e-001f, 0.00000000e+000f, 0.00000000e+000f } }, }; const ALfloat (*AmbiMatrix)[2][MAX_AMBI_COEFFS] = hoa_mode ? AmbiMatrixHOA : AmbiMatrixFOA; - size_t count = hoa_mode ? 9 : 4; - size_t i; + ALsizei count = hoa_mode ? 9 : 4; + ALsizei i; static_assert(9 <= COUNTOF(device->Hrtf.Coeffs), "ALCdevice::Hrtf.Values/Coeffs size is too small"); static_assert(COUNTOF(AmbiPoints) <= HRTF_AMBI_MAX_CHANNELS, "HRTF_AMBI_MAX_CHANNELS is too small"); @@ -960,7 +1007,10 @@ void aluInitRenderer(ALCdevice *device, ALint hrtf_id, enum HrtfRequestMode hrtf memset(&device->Dry.Ambi, 0, sizeof(device->Dry.Ambi)); device->Dry.CoeffCount = 0; device->Dry.NumChannels = 0; + for(i = 0;i < MAX_AMBI_ORDER+1;i++) + device->Dry.NumChannelsPerOrder[i] = 0; + device->AvgSpeakerDist = 0.0f; memset(device->ChannelDelay, 0, sizeof(device->ChannelDelay)); for(i = 0;i < MAX_OUTPUT_CHANNELS;i++) { |