aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Alc/ALc.c20
-rw-r--r--Alc/ALu.c64
-rw-r--r--Alc/mixer.c55
-rw-r--r--Alc/panning.c172
-rw-r--r--OpenAL32/Include/alMain.h7
-rw-r--r--OpenAL32/Include/alu.h9
-rw-r--r--OpenAL32/alSource.c12
7 files changed, 262 insertions, 77 deletions
diff --git a/Alc/ALc.c b/Alc/ALc.c
index 882a8cb0..beec0c32 100644
--- a/Alc/ALc.c
+++ b/Alc/ALc.c
@@ -2310,6 +2310,24 @@ static ALCenum UpdateDeviceParams(ALCdevice *device, const ALCint *attrList)
}
}
AllocateVoices(context, context->MaxVoices, old_sends);
+ for(pos = 0;pos < context->VoiceCount;pos++)
+ {
+ ALvoice *voice = context->Voices[pos];
+ if(!voice->Source) continue;
+
+ if(device->AvgSpeakerDist > 0.0f)
+ {
+ /* Reinitialize the NFC filters for new parameters. */
+ ALfloat w1 = SPEEDOFSOUNDMETRESPERSEC /
+ (device->AvgSpeakerDist * device->Frequency);
+ for(i = 0;i < voice->NumChannels;i++)
+ {
+ NfcFilterCreate1(&voice->Direct.Params[i].NFCtrlFilter[0], 0.0f, w1);
+ NfcFilterCreate2(&voice->Direct.Params[i].NFCtrlFilter[1], 0.0f, w1);
+ NfcFilterCreate3(&voice->Direct.Params[i].NFCtrlFilter[2], 0.0f, w1);
+ }
+ }
+ }
UnlockUIntMapRead(&context->SourceMap);
UpdateListenerProps(context);
@@ -3755,6 +3773,7 @@ ALC_API ALCdevice* ALC_APIENTRY alcOpenDevice(const ALCchar *deviceName)
device->FOAOut.NumChannels = 0;
device->RealOut.Buffer = NULL;
device->RealOut.NumChannels = 0;
+ device->AvgSpeakerDist = 0.0f;
ATOMIC_INIT(&device->ContextList, NULL);
@@ -4271,6 +4290,7 @@ ALC_API ALCdevice* ALC_APIENTRY alcLoopbackOpenDeviceSOFT(const ALCchar *deviceN
device->FOAOut.NumChannels = 0;
device->RealOut.Buffer = NULL;
device->RealOut.NumChannels = 0;
+ device->AvgSpeakerDist = 0.0f;
ATOMIC_INIT(&device->ContextList, NULL);
diff --git a/Alc/ALu.c b/Alc/ALu.c
index 04b9c89d..90ca8ab1 100644
--- a/Alc/ALu.c
+++ b/Alc/ALu.c
@@ -496,6 +496,7 @@ static void CalcNonAttnSourceParams(ALvoice *voice, const struct ALsourceProps *
break;
}
+ voice->Flags &= ~(VOICE_IS_HRTF | VOICE_HAS_NFC);
if(isbformat)
{
ALfloat N[3], V[3], U[3];
@@ -535,6 +536,17 @@ static void CalcNonAttnSourceParams(ALvoice *voice, const struct ALsourceProps *
for(c = 0;c < num_channels;c++)
ComputeFirstOrderGains(Device->FOAOut, matrix.m[c], DryGain,
voice->Direct.Params[c].Gains.Target);
+ if(Device->AvgSpeakerDist > 0.0f)
+ {
+ /* NOTE: The NFCtrlFilters were created with a w0 of 0, which is
+ * what we want for FOA input. So there's nothing to adjust.
+ */
+ voice->Direct.ChannelsPerOrder[0] = 1;
+ voice->Direct.ChannelsPerOrder[1] = mini(voice->Direct.Channels-1, 3);
+ voice->Direct.ChannelsPerOrder[2] = 0;
+ voice->Direct.ChannelsPerOrder[3] = 0;
+ voice->Flags |= VOICE_HAS_NFC;
+ }
for(i = 0;i < NumSends;i++)
{
@@ -553,8 +565,6 @@ static void CalcNonAttnSourceParams(ALvoice *voice, const struct ALsourceProps *
voice->Send[i].Params[c].Gains.Target[j] = 0.0f;
}
}
-
- voice->IsHrtf = AL_FALSE;
}
else
{
@@ -592,8 +602,6 @@ static void CalcNonAttnSourceParams(ALvoice *voice, const struct ALsourceProps *
voice->Send[i].Params[c].Gains.Target[j] = 0.0f;
}
}
-
- voice->IsHrtf = AL_FALSE;
}
else if(Device->Render_Mode == HrtfRender)
{
@@ -647,7 +655,7 @@ static void CalcNonAttnSourceParams(ALvoice *voice, const struct ALsourceProps *
}
}
- voice->IsHrtf = AL_TRUE;
+ voice->Flags |= VOICE_IS_HRTF;
}
else
{
@@ -694,8 +702,6 @@ static void CalcNonAttnSourceParams(ALvoice *voice, const struct ALsourceProps *
voice->Send[i].Params[c].Gains.Target[j] = 0.0f;
}
}
-
- voice->IsHrtf = AL_FALSE;
}
}
@@ -1064,6 +1070,7 @@ static void CalcAttnSourceParams(ALvoice *voice, const struct ALsourceProps *pro
voice->Step = maxi(fastf2i(Pitch*FRACTIONONE + 0.5f), 1);
BsincPrepare(voice->Step, &voice->ResampleState.bsinc);
+ voice->Flags &= ~(VOICE_IS_HRTF | VOICE_HAS_NFC);
if(Device->Render_Mode == HrtfRender)
{
/* Full HRTF rendering. Skip the virtual channels and render to the
@@ -1115,7 +1122,7 @@ static void CalcAttnSourceParams(ALvoice *voice, const struct ALsourceProps *pro
voice->Send[i].Params[0].Gains.Target[j] = 0.0f;
}
- voice->IsHrtf = AL_TRUE;
+ voice->Flags |= VOICE_IS_HRTF;
}
else
{
@@ -1128,10 +1135,49 @@ static void CalcAttnSourceParams(ALvoice *voice, const struct ALsourceProps *pro
/* Get the localized direction, and compute panned gains. */
if(Distance > FLT_EPSILON)
{
+ if(Device->AvgSpeakerDist > 0.0f && MetersPerUnit > 0.0f)
+ {
+ ALfloat w0 = SPEEDOFSOUNDMETRESPERSEC /
+ (Distance*MetersPerUnit * (ALfloat)Device->Frequency);
+ ALfloat w1 = SPEEDOFSOUNDMETRESPERSEC /
+ (Device->AvgSpeakerDist * (ALfloat)Device->Frequency);
+ /* Clamp w0 for really close distances, to prevent excessive
+ * bass.
+ */
+ w0 = minf(w0, w1*4.0f);
+
+ NfcFilterAdjust1(&voice->Direct.Params[0].NFCtrlFilter[0], w0);
+ NfcFilterAdjust2(&voice->Direct.Params[0].NFCtrlFilter[1], w0);
+ NfcFilterAdjust3(&voice->Direct.Params[0].NFCtrlFilter[2], w0);
+
+ for(i = 0;i < MAX_AMBI_ORDER+1;i++)
+ voice->Direct.ChannelsPerOrder[i] = Device->Dry.NumChannelsPerOrder[i];
+ voice->Flags |= VOICE_HAS_NFC;
+ }
+
dir[0] = -SourceToListener.v[0];
dir[1] = -SourceToListener.v[1];
dir[2] = -SourceToListener.v[2] * ZScale;
}
+ else if(Device->AvgSpeakerDist > 0.0f)
+ {
+ /* If the source distance is 0, set w0 to w1 to act as a pass-
+ * through. We still want to pass the signal through the filters so
+ * they keep an appropriate history, in case the source moves away
+ * from the listener.
+ */
+ ALfloat w0 = SPEEDOFSOUNDMETRESPERSEC /
+ (Device->AvgSpeakerDist * (ALfloat)Device->Frequency);
+
+ NfcFilterAdjust1(&voice->Direct.Params[0].NFCtrlFilter[0], w0);
+ NfcFilterAdjust2(&voice->Direct.Params[0].NFCtrlFilter[1], w0);
+ NfcFilterAdjust3(&voice->Direct.Params[0].NFCtrlFilter[2], w0);
+
+ for(i = 0;i < MAX_AMBI_ORDER+1;i++)
+ voice->Direct.ChannelsPerOrder[i] = Device->Dry.NumChannelsPerOrder[i];
+ voice->Flags |= VOICE_HAS_NFC;
+ }
+
if(radius > Distance)
spread = F_TAU - Distance/radius*F_PI;
else if(Distance > FLT_EPSILON)
@@ -1160,8 +1206,6 @@ static void CalcAttnSourceParams(ALvoice *voice, const struct ALsourceProps *pro
for(j = 0;j < MAX_EFFECT_CHANNELS;j++)
voice->Send[i].Params[0].Gains.Target[j] = 0.0f;
}
-
- voice->IsHrtf = AL_FALSE;
}
{
diff --git a/Alc/mixer.c b/Alc/mixer.c
index 1c503a92..961c8f31 100644
--- a/Alc/mixer.c
+++ b/Alc/mixer.c
@@ -545,15 +545,60 @@ ALboolean MixSource(ALvoice *voice, ALsource *Source, ALCdevice *Device, ALsizei
&parms->LowPass, &parms->HighPass, Device->FilteredData,
ResampledData, DstBufferSize, parms->FilterType
);
- if(!voice->IsHrtf)
+ if(!(voice->Flags&VOICE_IS_HRTF))
{
if(!Counter)
memcpy(parms->Gains.Current, parms->Gains.Target,
sizeof(parms->Gains.Current));
- MixSamples(samples, voice->Direct.Channels, voice->Direct.Buffer,
- parms->Gains.Current, parms->Gains.Target, Counter, OutPos,
- DstBufferSize
- );
+ if(!(voice->Flags&VOICE_HAS_NFC))
+ MixSamples(samples, voice->Direct.Channels, voice->Direct.Buffer,
+ parms->Gains.Current, parms->Gains.Target, Counter, OutPos,
+ DstBufferSize
+ );
+ else
+ {
+ ALfloat *nfcsamples = Device->NFCtrlData;
+ ALsizei chanoffset = 0;
+ MixSamples(samples,
+ voice->Direct.ChannelsPerOrder[0], voice->Direct.Buffer,
+ parms->Gains.Current, parms->Gains.Target, Counter, OutPos,
+ DstBufferSize
+ );
+ chanoffset += voice->Direct.ChannelsPerOrder[0];
+ if(voice->Direct.ChannelsPerOrder[1] > 0)
+ {
+ NfcFilterUpdate1(&parms->NFCtrlFilter[0], nfcsamples, samples,
+ DstBufferSize);
+ MixSamples(nfcsamples,
+ voice->Direct.ChannelsPerOrder[1], voice->Direct.Buffer+chanoffset,
+ parms->Gains.Current+chanoffset, parms->Gains.Target+chanoffset,
+ Counter, OutPos, DstBufferSize
+ );
+ chanoffset += voice->Direct.ChannelsPerOrder[1];
+ }
+ if(voice->Direct.ChannelsPerOrder[2] > 0)
+ {
+ NfcFilterUpdate2(&parms->NFCtrlFilter[1], nfcsamples, samples,
+ DstBufferSize);
+ MixSamples(nfcsamples,
+ voice->Direct.ChannelsPerOrder[2], voice->Direct.Buffer+chanoffset,
+ parms->Gains.Current+chanoffset, parms->Gains.Target+chanoffset,
+ Counter, OutPos, DstBufferSize
+ );
+ chanoffset += voice->Direct.ChannelsPerOrder[2];
+ }
+ if(voice->Direct.ChannelsPerOrder[3] > 0)
+ {
+ NfcFilterUpdate3(&parms->NFCtrlFilter[2], nfcsamples, samples,
+ DstBufferSize);
+ MixSamples(nfcsamples,
+ voice->Direct.ChannelsPerOrder[3], voice->Direct.Buffer+chanoffset,
+ parms->Gains.Current+chanoffset, parms->Gains.Target+chanoffset,
+ Counter, OutPos, DstBufferSize
+ );
+ chanoffset += voice->Direct.ChannelsPerOrder[3];
+ }
+ }
}
else
{
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++)
{
diff --git a/OpenAL32/Include/alMain.h b/OpenAL32/Include/alMain.h
index cc30dcef..e4c5b94a 100644
--- a/OpenAL32/Include/alMain.h
+++ b/OpenAL32/Include/alMain.h
@@ -726,6 +726,7 @@ struct ALCdevice_struct
alignas(16) ALfloat SourceData[BUFFERSIZE];
alignas(16) ALfloat ResampledData[BUFFERSIZE];
alignas(16) ALfloat FilteredData[BUFFERSIZE];
+ alignas(16) ALfloat NFCtrlData[BUFFERSIZE];
/* The "dry" path corresponds to the main output. */
struct {
@@ -738,6 +739,7 @@ struct ALCdevice_struct
ALfloat (*Buffer)[BUFFERSIZE];
ALsizei NumChannels;
+ ALsizei NumChannelsPerOrder[MAX_AMBI_ORDER+1];
} Dry;
/* First-order ambisonics output, to be upsampled to the dry buffer if different. */
@@ -760,6 +762,11 @@ struct ALCdevice_struct
ALsizei NumChannels;
} RealOut;
+ /* The average speaker distance as determined by the ambdec configuration
+ * (or alternatively, by the NFC-HOA reference delay). Only used for NFC.
+ */
+ ALfloat AvgSpeakerDist;
+
/* Delay buffers used to compensate for speaker distances. */
DistanceComp ChannelDelay[MAX_OUTPUT_CHANNELS];
diff --git a/OpenAL32/Include/alu.h b/OpenAL32/Include/alu.h
index 03f0090d..6c374ebc 100644
--- a/OpenAL32/Include/alu.h
+++ b/OpenAL32/Include/alu.h
@@ -17,6 +17,7 @@
#include "hrtf.h"
#include "align.h"
+#include "nfcfilter.h"
#include "math_defs.h"
@@ -129,6 +130,8 @@ typedef struct DirectParams {
ALfilterState LowPass;
ALfilterState HighPass;
+ NfcFilter NFCtrlFilter[MAX_AMBI_ORDER];
+
struct {
HrtfParams Current;
HrtfParams Target;
@@ -152,6 +155,9 @@ typedef struct SendParams {
} Gains;
} SendParams;
+#define VOICE_IS_HRTF (1<<0)
+#define VOICE_HAS_NFC (1<<1)
+
typedef struct ALvoice {
struct ALsourceProps *Props;
@@ -182,7 +188,7 @@ typedef struct ALvoice {
/* If not 'moving', gain/coefficients are set directly without fading. */
ALboolean Moving;
- ALboolean IsHrtf;
+ ALuint Flags;
ALuint Offset; /* Number of output samples mixed since starting. */
@@ -195,6 +201,7 @@ typedef struct ALvoice {
ALfloat (*Buffer)[BUFFERSIZE];
ALsizei Channels;
+ ALsizei ChannelsPerOrder[MAX_AMBI_ORDER+1];
} Direct;
struct {
diff --git a/OpenAL32/alSource.c b/OpenAL32/alSource.c
index c4c0bfb1..55a4aae1 100644
--- a/OpenAL32/alSource.c
+++ b/OpenAL32/alSource.c
@@ -3084,6 +3084,7 @@ ALvoid SetSourceState(ALsource *Source, ALCcontext *Context, ALenum state)
voice->Step = 0;
voice->Moving = AL_FALSE;
+ voice->Flags = 0;
for(i = 0;i < MAX_INPUT_CHANNELS;i++)
{
ALsizei j;
@@ -3095,6 +3096,17 @@ ALvoid SetSourceState(ALsource *Source, ALCcontext *Context, ALenum state)
voice->Direct.Params[i].Hrtf.State.Values[j][1] = 0.0f;
}
}
+ if(device->AvgSpeakerDist > 0.0f)
+ {
+ ALfloat w1 = SPEEDOFSOUNDMETRESPERSEC /
+ (device->AvgSpeakerDist * device->Frequency);
+ for(i = 0;i < voice->NumChannels;i++)
+ {
+ NfcFilterCreate1(&voice->Direct.Params[i].NFCtrlFilter[0], 0.0f, w1);
+ NfcFilterCreate2(&voice->Direct.Params[i].NFCtrlFilter[1], 0.0f, w1);
+ NfcFilterCreate3(&voice->Direct.Params[i].NFCtrlFilter[2], 0.0f, w1);
+ }
+ }
ATOMIC_STORE(&voice->Source, Source, almemory_order_relaxed);
ATOMIC_STORE(&voice->Playing, true, almemory_order_release);