#include "F3DAudio.h"
#include "FAudio_internal.h"
#include <math.h>
#include <float.h>
#if defined(_MSC_VER) && !defined(isnan)
#define isnan(x) _isnan(x)
#endif
#define PARAM_CHECK_OK 1
#define PARAM_CHECK_FAIL (!PARAM_CHECK_OK)
#define ARRAY_COUNT(x) (sizeof(x) / sizeof(x[0]))
#define LERP(a, x, y) ((1.0f - a) * x + a * y)
#define PARAM_CHECK(cond, msg) FAudio_assert(cond && msg)
#define POINTER_CHECK(p) \
PARAM_CHECK(p != NULL, "Pointer " #p " must be != NULL")
#define FLOAT_BETWEEN_CHECK(f, a, b) \
PARAM_CHECK(f >= a, "Value" #f " is too low"); \
PARAM_CHECK(f <= b, "Value" #f " is too big")
#define VECTOR_NORMAL_CHECK(v) \
PARAM_CHECK( \
FAudio_fabsf(VectorLength(v) - 1.0f) <= 1e-5f, \
"Vector " #v " isn't normal" \
)
#define VECTOR_BASE_CHECK(u, v) \
PARAM_CHECK( \
FAudio_fabsf(VectorDot(u, v)) <= 1e-5f, \
"Vector u and v have non-negligible dot product" \
)
#define SPEAKERMASK(Instance) *((uint32_t*) &Instance[0])
#define SPEAKERCOUNT(Instance) *((uint32_t*) &Instance[4])
#define SPEAKER_LF_INDEX(Instance) *((uint32_t*) &Instance[8])
#define SPEEDOFSOUND(Instance) *((float*) &Instance[12])
#define SPEEDOFSOUNDEPSILON(Instance) *((float*) &Instance[16])
F3DAUDIOAPI uint32_t F3DAudioCheckInitParams(
uint32_t SpeakerChannelMask,
float SpeedOfSound,
F3DAUDIO_HANDLE instance
) {
const uint32_t kAllowedSpeakerMasks[] =
{
SPEAKER_MONO,
SPEAKER_STEREO,
SPEAKER_2POINT1,
SPEAKER_QUAD,
SPEAKER_SURROUND,
SPEAKER_4POINT1,
SPEAKER_5POINT1,
SPEAKER_5POINT1_SURROUND,
SPEAKER_7POINT1,
SPEAKER_7POINT1_SURROUND,
};
uint8_t speakerMaskIsValid = 0;
uint32_t i;
POINTER_CHECK(instance);
for (i = 0; i < ARRAY_COUNT(kAllowedSpeakerMasks); i += 1)
{
if (SpeakerChannelMask == kAllowedSpeakerMasks[i])
{
speakerMaskIsValid = 1;
break;
}
}
PARAM_CHECK(
speakerMaskIsValid == 1,
"SpeakerChannelMask is invalid. Needs to be one of"
" MONO, STEREO, QUAD, 2POINT1, 4POINT1, 5POINT1, 7POINT1,"
" SURROUND, 5POINT1_SURROUND, or 7POINT1_SURROUND."
);
PARAM_CHECK(SpeedOfSound >= FLT_MIN, "SpeedOfSound needs to be >= FLT_MIN");
return PARAM_CHECK_OK;
}
void F3DAudioInitialize(
uint32_t SpeakerChannelMask,
float SpeedOfSound,
F3DAUDIO_HANDLE Instance
) {
F3DAudioInitialize8(SpeakerChannelMask, SpeedOfSound, Instance);
}
uint32_t F3DAudioInitialize8(
uint32_t SpeakerChannelMask,
float SpeedOfSound,
F3DAUDIO_HANDLE Instance
) {
union
{
float f;
uint32_t i;
} epsilonHack;
uint32_t speakerCount = 0;
if (!F3DAudioCheckInitParams(SpeakerChannelMask, SpeedOfSound, Instance))
{
return FAUDIO_E_INVALID_CALL;
}
SPEAKERMASK(Instance) = SpeakerChannelMask;
SPEEDOFSOUND(Instance) = SpeedOfSound;
epsilonHack.f = SpeedOfSound;
epsilonHack.i -= 1;
SPEEDOFSOUNDEPSILON(Instance) = epsilonHack.f;
SPEAKER_LF_INDEX(Instance) = 0xFFFFFFFF;
if (SpeakerChannelMask & SPEAKER_LOW_FREQUENCY)
{
if (SpeakerChannelMask & SPEAKER_FRONT_CENTER)
{
SPEAKER_LF_INDEX(Instance) = 3;
}
else
{
SPEAKER_LF_INDEX(Instance) = 2;
}
}
while (SpeakerChannelMask)
{
speakerCount += 1;
SpeakerChannelMask &= SpeakerChannelMask - 1;
}
SPEAKERCOUNT(Instance) = speakerCount;
return 0;
}
static inline F3DAUDIO_VECTOR Vec(float x, float y, float z)
{
F3DAUDIO_VECTOR res;
res.x = x;
res.y = y;
res.z = z;
return res;
}
#define VectorAdd(u, v) Vec(u.x + v.x, u.y + v.y, u.z + v.z)
#define VectorSub(u, v) Vec(u.x - v.x, u.y - v.y, u.z - v.z)
#define VectorScale(u, s) Vec(u.x * s, u.y * s, u.z * s)
#define VectorCross(u, v) Vec( \
(u.y * v.z) - (u.z * v.y), \
(u.z * v.x) - (u.x * v.z), \
(u.x * v.y) - (u.y * v.x) \
)
#define VectorLength(v) FAudio_sqrtf( \
(v.x * v.x) + (v.y * v.y) + (v.z * v.z) \
)
#define VectorDot(u, v) ((u.x * v.x) + (u.y * v.y) + (u.z * v.z))
typedef struct F3DAUDIO_BASIS
{
F3DAUDIO_VECTOR front;
F3DAUDIO_VECTOR right;
F3DAUDIO_VECTOR top;
} F3DAUDIO_BASIS;
static inline uint8_t CheckCone(F3DAUDIO_CONE *pCone)
{
if (!pCone)
{
return PARAM_CHECK_OK;
}
FLOAT_BETWEEN_CHECK(pCone->InnerAngle, 0.0f, F3DAUDIO_2PI);
FLOAT_BETWEEN_CHECK(pCone->OuterAngle, pCone->InnerAngle, F3DAUDIO_2PI);
FLOAT_BETWEEN_CHECK(pCone->InnerVolume, 0.0f, 2.0f);
FLOAT_BETWEEN_CHECK(pCone->OuterVolume, 0.0f, 2.0f);
FLOAT_BETWEEN_CHECK(pCone->InnerLPF, 0.0f, 1.0f);
FLOAT_BETWEEN_CHECK(pCone->OuterLPF, 0.0f, 1.0f);
FLOAT_BETWEEN_CHECK(pCone->InnerReverb, 0.0f, 2.0f);
FLOAT_BETWEEN_CHECK(pCone->OuterReverb, 0.0f, 2.0f);
return PARAM_CHECK_OK;
}
static inline uint8_t CheckCurve(F3DAUDIO_DISTANCE_CURVE *pCurve)
{
F3DAUDIO_DISTANCE_CURVE_POINT *points;
uint32_t i;
if (!pCurve)
{
return PARAM_CHECK_OK;
}
points = pCurve->pPoints;
POINTER_CHECK(points);
PARAM_CHECK(pCurve->PointCount >= 2, "Invalid number of points for curve");
for (i = 0; i < pCurve->PointCount; i += 1)
{
FLOAT_BETWEEN_CHECK(points[i].Distance, 0.0f, 1.0f);
}
PARAM_CHECK(
points[0].Distance == 0.0f,
"First point in the curve must be at distance 0.0f"
);
PARAM_CHECK(
points[pCurve->PointCount - 1].Distance == 1.0f,
"Last point in the curve must be at distance 1.0f"
);
for (i = 0; i < (pCurve->PointCount - 1); i += 1)
{
PARAM_CHECK(
points[i].Distance < points[i + 1].Distance,
"Curve points must be in strict ascending order"
);
}
return PARAM_CHECK_OK;
}
F3DAUDIOAPI uint8_t F3DAudioCheckCalculateParams(
const F3DAUDIO_HANDLE Instance,
const F3DAUDIO_LISTENER *pListener,
const F3DAUDIO_EMITTER *pEmitter,
uint32_t Flags,
F3DAUDIO_DSP_SETTINGS *pDSPSettings
) {
uint32_t i, ChannelCount;
POINTER_CHECK(Instance);
POINTER_CHECK(pListener);
POINTER_CHECK(pEmitter);
POINTER_CHECK(pDSPSettings);
if (Flags & F3DAUDIO_CALCULATE_MATRIX)
{
POINTER_CHECK(pDSPSettings->pMatrixCoefficients);
}
if (Flags & F3DAUDIO_CALCULATE_ZEROCENTER)
{
const uint32_t isCalculateMatrix = (Flags & F3DAUDIO_CALCULATE_MATRIX);
const uint32_t hasCenter = SPEAKERMASK(Instance) & SPEAKER_FRONT_CENTER;
PARAM_CHECK(
isCalculateMatrix && hasCenter,
"F3DAUDIO_CALCULATE_ZEROCENTER is only valid for matrix"
" calculations with an output format that has a center channel"
);
}
if (Flags & F3DAUDIO_CALCULATE_REDIRECT_TO_LFE)
{
const uint32_t isCalculateMatrix = (Flags & F3DAUDIO_CALCULATE_MATRIX);
const uint32_t hasLF = SPEAKERMASK(Instance) & SPEAKER_LOW_FREQUENCY;
PARAM_CHECK(
isCalculateMatrix && hasLF,
"F3DAUDIO_CALCULATE_REDIRECT_TO_LFE is only valid for matrix"
" calculations with an output format that has a low-frequency"
" channel"
);
}
ChannelCount = SPEAKERCOUNT(Instance);
PARAM_CHECK(
pDSPSettings->DstChannelCount == ChannelCount,
"Invalid channel count, DSP settings and speaker configuration must agree"
);
PARAM_CHECK(
pDSPSettings->SrcChannelCount == pEmitter->ChannelCount,
"Invalid channel count, DSP settings and emitter must agree"
);
if (pListener->pCone)
{
PARAM_CHECK(
CheckCone(pListener->pCone) == PARAM_CHECK_OK,
"Invalid listener cone"
);
}
VECTOR_NORMAL_CHECK(pListener->OrientFront);
VECTOR_NORMAL_CHECK(pListener->OrientTop);
VECTOR_BASE_CHECK(pListener->OrientFront, pListener->OrientTop);
if (pEmitter->pCone)
{
VECTOR_NORMAL_CHECK(pEmitter->OrientFront);
PARAM_CHECK(
CheckCone(pEmitter->pCone) == PARAM_CHECK_OK,
"Invalid emitter cone"
);
}
else if (Flags & F3DAUDIO_CALCULATE_EMITTER_ANGLE)
{
VECTOR_NORMAL_CHECK(pEmitter->OrientFront);
}
if (pEmitter->ChannelCount > 1)
{
VECTOR_NORMAL_CHECK(pEmitter->OrientFront);
VECTOR_NORMAL_CHECK(pEmitter->OrientTop);
VECTOR_BASE_CHECK(pEmitter->OrientFront, pEmitter->OrientTop);
}
FLOAT_BETWEEN_CHECK(pEmitter->InnerRadius, 0.0f, FLT_MAX);
FLOAT_BETWEEN_CHECK(pEmitter->InnerRadiusAngle, 0.0f, F3DAUDIO_2PI / 4.0f);
PARAM_CHECK(
pEmitter->ChannelCount > 0,
"Invalid channel count for emitter"
);
PARAM_CHECK(
pEmitter->ChannelRadius >= 0.0f,
"Invalid channel radius for emitter"
);
if (pEmitter->ChannelCount > 1)
{
PARAM_CHECK(
pEmitter->pChannelAzimuths != NULL,
"Invalid channel azimuths for multi-channel emitter"
);
if (pEmitter->pChannelAzimuths)
{
for (i = 0; i < pEmitter->ChannelCount; i += 1)
{
float currentAzimuth = pEmitter->pChannelAzimuths[i];
FLOAT_BETWEEN_CHECK(currentAzimuth, 0.0f, F3DAUDIO_2PI);
if (currentAzimuth == F3DAUDIO_2PI)
{
PARAM_CHECK(
!(Flags & F3DAUDIO_CALCULATE_REDIRECT_TO_LFE),
"F3DAUDIO_CALCULATE_REDIRECT_TO_LFE valid only for"
" matrix calculations with emitters that have no LFE"
" channel"
);
}
}
}
}
FLOAT_BETWEEN_CHECK(pEmitter->CurveDistanceScaler, FLT_MIN, FLT_MAX);
FLOAT_BETWEEN_CHECK(pEmitter->DopplerScaler, 0.0f, FLT_MAX);
PARAM_CHECK(
CheckCurve(pEmitter->pVolumeCurve) == PARAM_CHECK_OK,
"Invalid Volume curve"
);
PARAM_CHECK(
CheckCurve(pEmitter->pLFECurve) == PARAM_CHECK_OK,
"Invalid LFE curve"
);
PARAM_CHECK(
CheckCurve(pEmitter->pLPFDirectCurve) == PARAM_CHECK_OK,
"Invalid LPFDirect curve"
);
PARAM_CHECK(
CheckCurve(pEmitter->pLPFReverbCurve) == PARAM_CHECK_OK,
"Invalid LPFReverb curve"
);
PARAM_CHECK(
CheckCurve(pEmitter->pReverbCurve) == PARAM_CHECK_OK,
"Invalid Reverb curve"
);
return PARAM_CHECK_OK;
}
static inline float ComputeDistanceAttenuation(
float normalizedDistance,
F3DAUDIO_DISTANCE_CURVE *pCurve
) {
float res;
float alpha;
uint32_t n_points;
size_t i;
if (pCurve)
{
F3DAUDIO_DISTANCE_CURVE_POINT* points = pCurve->pPoints;
n_points = pCurve->PointCount;
for (i = 1; (i < n_points) && (normalizedDistance >= points[i].Distance); i += 1);
if (i == n_points)
{
res = points[n_points - 1].DSPSetting;
}
else
{
alpha = (points[i].Distance - normalizedDistance) / (points[i].Distance - points[i - 1].Distance);
res = LERP(alpha, points[i].DSPSetting, points[i - 1].DSPSetting);
}
}
else
{
res = 1.0f;
if (normalizedDistance >= 1.0f)
{
res /= normalizedDistance;
}
}
return res;
}
static inline float ComputeConeParameter(
float distance,
float angle,
float innerAngle,
float outerAngle,
float innerParam,
float outerParam
) {
#define CONE_NULL_DISTANCE_TOLERANCE 1e-7
float halfInnerAngle, halfOuterAngle, alpha;
if (innerAngle == 0.0f && outerAngle == 0.0f)
{
return outerParam;
}
if (innerAngle == F3DAUDIO_2PI && outerAngle == F3DAUDIO_2PI)
{
return innerParam;
}
halfInnerAngle = innerAngle / 2.0f;
if (distance <= CONE_NULL_DISTANCE_TOLERANCE || angle <= halfInnerAngle)
{
return innerParam;
}
halfOuterAngle = outerAngle / 2.0f;
if (angle <= halfOuterAngle)
{
alpha = (angle - halfInnerAngle) / (halfOuterAngle - halfInnerAngle);
return LERP(alpha, innerParam, outerParam);
}
return outerParam;
}
#if 0
static F3DAUDIO_DISTANCE_CURVE_POINT DefaultVolumeCurvePoints[] =
{
{ 0.0f, 1.0f },
{ 1.0f, 0.0f }
};
static F3DAUDIO_DISTANCE_CURVE DefaultVolumeCurve =
{
DefaultVolumeCurvePoints,
ARRAY_COUNT(DefaultVolumeCurvePoints)
};
#endif
typedef struct
{
float azimuth;
uint32_t matrixIdx;
} SpeakerInfo;
typedef struct
{
uint32_t configMask;
const SpeakerInfo *speakers;
uint32_t numNonLFSpeakers;
int32_t LFSpeakerIdx;
} ConfigInfo;
#define SPEAKER_AZIMUTH_CENTER 0.0f
#define SPEAKER_AZIMUTH_FRONT_RIGHT_OF_CENTER (F3DAUDIO_PI * 1.0f / 8.0f)
#define SPEAKER_AZIMUTH_FRONT_RIGHT (F3DAUDIO_PI * 1.0f / 4.0f)
#define SPEAKER_AZIMUTH_SIDE_RIGHT (F3DAUDIO_PI * 1.0f / 2.0f)
#define SPEAKER_AZIMUTH_BACK_RIGHT (F3DAUDIO_PI * 3.0f / 4.0f)
#define SPEAKER_AZIMUTH_BACK_CENTER F3DAUDIO_PI
#define SPEAKER_AZIMUTH_BACK_LEFT (F3DAUDIO_PI * 5.0f / 4.0f)
#define SPEAKER_AZIMUTH_SIDE_LEFT (F3DAUDIO_PI * 3.0f / 2.0f)
#define SPEAKER_AZIMUTH_FRONT_LEFT (F3DAUDIO_PI * 7.0f / 4.0f)
#define SPEAKER_AZIMUTH_FRONT_LEFT_OF_CENTER (F3DAUDIO_PI * 15.0f / 8.0f)
const SpeakerInfo kMonoConfigSpeakers[] =
{
{ SPEAKER_AZIMUTH_CENTER, 0 },
};
const SpeakerInfo kStereoConfigSpeakers[] =
{
{ SPEAKER_AZIMUTH_SIDE_RIGHT, 1 },
{ SPEAKER_AZIMUTH_SIDE_LEFT, 0 },
};
const SpeakerInfo k2Point1ConfigSpeakers[] =
{
{ SPEAKER_AZIMUTH_SIDE_RIGHT, 1 },
{ SPEAKER_AZIMUTH_SIDE_LEFT, 0 },
};
const SpeakerInfo kSurroundConfigSpeakers[] =
{
{ SPEAKER_AZIMUTH_CENTER, 2 },
{ SPEAKER_AZIMUTH_FRONT_RIGHT, 1 },
{ SPEAKER_AZIMUTH_BACK_CENTER, 3 },
{ SPEAKER_AZIMUTH_FRONT_LEFT, 0 },
};
const SpeakerInfo kQuadConfigSpeakers[] =
{
{ SPEAKER_AZIMUTH_FRONT_RIGHT, 1 },
{ SPEAKER_AZIMUTH_BACK_RIGHT, 3 },
{ SPEAKER_AZIMUTH_BACK_LEFT, 2 },
{ SPEAKER_AZIMUTH_FRONT_LEFT, 0 },
};
const SpeakerInfo k4Point1ConfigSpeakers[] =
{
{ SPEAKER_AZIMUTH_FRONT_RIGHT, 1 },
{ SPEAKER_AZIMUTH_BACK_RIGHT, 4 },
{ SPEAKER_AZIMUTH_BACK_LEFT, 3 },
{ SPEAKER_AZIMUTH_FRONT_LEFT, 0 },
};
const SpeakerInfo k5Point1ConfigSpeakers[] =
{
{ SPEAKER_AZIMUTH_CENTER, 2 },
{ SPEAKER_AZIMUTH_FRONT_RIGHT, 1 },
{ SPEAKER_AZIMUTH_BACK_RIGHT, 5 },
{ SPEAKER_AZIMUTH_BACK_LEFT, 4 },
{ SPEAKER_AZIMUTH_FRONT_LEFT, 0 },
};
const SpeakerInfo k7Point1ConfigSpeakers[] =
{
{ SPEAKER_AZIMUTH_CENTER, 2 },
{ SPEAKER_AZIMUTH_FRONT_RIGHT_OF_CENTER, 7 },
{ SPEAKER_AZIMUTH_FRONT_RIGHT, 1 },
{ SPEAKER_AZIMUTH_BACK_RIGHT, 5 },
{ SPEAKER_AZIMUTH_BACK_LEFT, 4 },
{ SPEAKER_AZIMUTH_FRONT_LEFT, 0 },
{ SPEAKER_AZIMUTH_FRONT_LEFT_OF_CENTER, 6 },
};
const SpeakerInfo k5Point1SurroundConfigSpeakers[] =
{
{ SPEAKER_AZIMUTH_CENTER, 2 },
{ SPEAKER_AZIMUTH_FRONT_RIGHT, 1 },
{ SPEAKER_AZIMUTH_SIDE_RIGHT, 5 },
{ SPEAKER_AZIMUTH_SIDE_LEFT, 4 },
{ SPEAKER_AZIMUTH_FRONT_LEFT, 0 },
};
const SpeakerInfo k7Point1SurroundConfigSpeakers[] =
{
{ SPEAKER_AZIMUTH_CENTER, 2 },
{ SPEAKER_AZIMUTH_FRONT_RIGHT, 1 },
{ SPEAKER_AZIMUTH_SIDE_RIGHT, 7 },
{ SPEAKER_AZIMUTH_BACK_RIGHT, 5 },
{ SPEAKER_AZIMUTH_BACK_LEFT, 4 },
{ SPEAKER_AZIMUTH_SIDE_LEFT, 6 },
{ SPEAKER_AZIMUTH_FRONT_LEFT, 0 },
};
const ConfigInfo kSpeakersConfigInfo[] =
{
{ SPEAKER_MONO, kMonoConfigSpeakers, ARRAY_COUNT(kMonoConfigSpeakers), -1 },
{ SPEAKER_STEREO, kStereoConfigSpeakers, ARRAY_COUNT(kStereoConfigSpeakers), -1 },
{ SPEAKER_2POINT1, k2Point1ConfigSpeakers, ARRAY_COUNT(k2Point1ConfigSpeakers), 2 },
{ SPEAKER_SURROUND, kSurroundConfigSpeakers, ARRAY_COUNT(kSurroundConfigSpeakers), -1 },
{ SPEAKER_QUAD, kQuadConfigSpeakers, ARRAY_COUNT(kQuadConfigSpeakers), -1 },
{ SPEAKER_4POINT1, k4Point1ConfigSpeakers, ARRAY_COUNT(k4Point1ConfigSpeakers), 2 },
{ SPEAKER_5POINT1, k5Point1ConfigSpeakers, ARRAY_COUNT(k5Point1ConfigSpeakers), 3 },
{ SPEAKER_7POINT1, k7Point1ConfigSpeakers, ARRAY_COUNT(k7Point1ConfigSpeakers), 3 },
{ SPEAKER_5POINT1_SURROUND, k5Point1SurroundConfigSpeakers, ARRAY_COUNT(k5Point1SurroundConfigSpeakers), 3 },
{ SPEAKER_7POINT1_SURROUND, k7Point1SurroundConfigSpeakers, ARRAY_COUNT(k7Point1SurroundConfigSpeakers), 3 },
};
static const ConfigInfo* GetConfigInfo(uint32_t speakerConfigMask)
{
uint32_t i;
for (i = 0; i < ARRAY_COUNT(kSpeakersConfigInfo); i += 1)
{
if (kSpeakersConfigInfo[i].configMask == speakerConfigMask)
{
return &kSpeakersConfigInfo[i];
}
}
FAudio_assert(0 && "Config info not found!");
return NULL;
}
static inline void FindSpeakerAzimuths(
const ConfigInfo* config,
float emitterAzimuth,
uint8_t skipCenter,
const SpeakerInfo **speakerInfo
) {
uint32_t i, nexti = 0;
float a0 = 0.0f, a1 = 0.0f;
FAudio_assert(config != NULL);
for (i = 0; i < config->numNonLFSpeakers; i += 1)
{
a0 = config->speakers[i].azimuth;
nexti = (i + 1) % config->numNonLFSpeakers;
a1 = config->speakers[nexti].azimuth;
if (a0 < a1)
{
if (emitterAzimuth >= a0 && emitterAzimuth < a1)
{
break;
}
}
else
{
if (emitterAzimuth >= a0 || emitterAzimuth < a1)
{
break;
}
}
}
FAudio_assert(emitterAzimuth >= a0 || emitterAzimuth < a1);
if (skipCenter)
{
if (a0 == 0.0f)
{
if (i == 0)
{
i = config->numNonLFSpeakers - 1;
}
else
{
i -= 1;
}
}
else if (a1 == 0.0f)
{
nexti += 1;
if (nexti >= config->numNonLFSpeakers)
{
nexti -= config->numNonLFSpeakers;
}
}
}
speakerInfo[0] = &config->speakers[i];
speakerInfo[1] = &config->speakers[nexti];
}
#define DIFFUSION_SPEAKERS_ALL 0
#define DIFFUSION_SPEAKERS_MATCHING 1
#define DIFFUSION_SPEAKERS_OPPOSITE 2
typedef float DiffusionSpeakerFactors[3];
static inline void ComputeInnerRadiusDiffusionFactors(
float radialDistance,
float InnerRadius,
DiffusionSpeakerFactors diffusionFactors
) {
#define DIFFUSION_LERP_MIDPOINT_VALUE 0.707107f
#define DIFFUSION_DISTANCE_MINIMUM_INNER_RADIUS 4e-7f
float actualInnerRadius = FAudio_max(InnerRadius, DIFFUSION_DISTANCE_MINIMUM_INNER_RADIUS);
float normalizedRadialDist;
float a, ms, os;
normalizedRadialDist = radialDistance / actualInnerRadius;
#define DIFFUSION_DISTANCE_EQUAL_ENERGY 1e-7f
if (radialDistance <= DIFFUSION_DISTANCE_EQUAL_ENERGY)
{
a = 1.0f;
ms = 0.0f;
os = 0.0f;
}
else if (normalizedRadialDist <= 0.5f)
{
a = 1.0f - 2.0f * normalizedRadialDist;
ms = LERP(2.0f * normalizedRadialDist, 0.0f, DIFFUSION_LERP_MIDPOINT_VALUE);
os = 1.0f - a - ms;
}
else if (normalizedRadialDist <= 1.0f)
{
a = 0.0f;
ms = LERP(2.0f * (normalizedRadialDist - 0.5f), DIFFUSION_LERP_MIDPOINT_VALUE, 1.0f);
os = 1.0f - a - ms;
}
else
{
a = 0.0f;
ms = 1.0f;
os = 0.0f;
}
diffusionFactors[DIFFUSION_SPEAKERS_ALL] = a;
diffusionFactors[DIFFUSION_SPEAKERS_MATCHING] = ms;
diffusionFactors[DIFFUSION_SPEAKERS_OPPOSITE] = os;
}
static inline void ComputeEmitterChannelCoefficients(
const ConfigInfo *curConfig,
const F3DAUDIO_BASIS *listenerBasis,
float innerRadius,
F3DAUDIO_VECTOR channelPosition,
float attenuation,
float LFEattenuation,
uint32_t flags,
uint32_t currentChannel,
uint32_t numSrcChannels,
float *pMatrixCoefficients
) {
float elevation, radialDistance;
F3DAUDIO_VECTOR projTopVec, projPlane;
uint8_t skipCenter = (flags & F3DAUDIO_CALCULATE_ZEROCENTER) ? 1 : 0;
DiffusionSpeakerFactors diffusionFactors = { 0.0f };
float x, y;
float emitterAzimuth;
float energyPerChannel;
float totalEnergy;
uint32_t nChannelsToDiffuseTo;
uint32_t iS, centerChannelIdx = -1;
const SpeakerInfo* infos[2];
float a0, a1, val;
uint32_t i0, i1;
elevation = VectorDot(listenerBasis->top, channelPosition);
projTopVec = VectorScale(listenerBasis->top, elevation);
projPlane = VectorSub(channelPosition, projTopVec);
radialDistance = VectorLength(projPlane);
ComputeInnerRadiusDiffusionFactors(
radialDistance,
innerRadius,
diffusionFactors
);
if (diffusionFactors[DIFFUSION_SPEAKERS_ALL] > 0.0f)
{
nChannelsToDiffuseTo = curConfig->numNonLFSpeakers;
totalEnergy = diffusionFactors[DIFFUSION_SPEAKERS_ALL] * attenuation;
if (skipCenter)
{
nChannelsToDiffuseTo -= 1;
FAudio_assert(curConfig->speakers[0].azimuth == SPEAKER_AZIMUTH_CENTER);
centerChannelIdx = curConfig->speakers[0].matrixIdx;
}
energyPerChannel = totalEnergy / nChannelsToDiffuseTo;
for (iS = 0; iS < curConfig->numNonLFSpeakers; iS += 1)
{
const uint32_t curSpeakerIdx = curConfig->speakers[iS].matrixIdx;
if (skipCenter && curSpeakerIdx == centerChannelIdx)
{
continue;
}
pMatrixCoefficients[curSpeakerIdx * numSrcChannels + currentChannel] += energyPerChannel;
}
}
if (diffusionFactors[DIFFUSION_SPEAKERS_MATCHING] > 0.0f)
{
const float totalEnergy = diffusionFactors[DIFFUSION_SPEAKERS_MATCHING] * attenuation;
x = VectorDot(listenerBasis->front, projPlane);
y = VectorDot(listenerBasis->right, projPlane);
emitterAzimuth = FAudio_atan2f(y, x);
if (emitterAzimuth < 0.0f)
{
emitterAzimuth += F3DAUDIO_2PI;
}
FindSpeakerAzimuths(curConfig, emitterAzimuth, skipCenter, infos);
a0 = infos[0]->azimuth;
a1 = infos[1]->azimuth;
if (a0 > a1)
{
if (emitterAzimuth >= a0)
{
emitterAzimuth -= F3DAUDIO_2PI;
}
a0 -= F3DAUDIO_2PI;
}
FAudio_assert(emitterAzimuth >= a0 && emitterAzimuth <= a1);
val = (emitterAzimuth - a0) / (a1 - a0);
i0 = infos[0]->matrixIdx;
i1 = infos[1]->matrixIdx;
pMatrixCoefficients[i0 * numSrcChannels + currentChannel] += (1.0f - val) * totalEnergy;
pMatrixCoefficients[i1 * numSrcChannels + currentChannel] += ( val) * totalEnergy;
}
if (diffusionFactors[DIFFUSION_SPEAKERS_OPPOSITE] > 0.0f)
{
const float totalEnergy = diffusionFactors[DIFFUSION_SPEAKERS_OPPOSITE] * attenuation;
x = VectorDot(listenerBasis->front, projPlane);
y = VectorDot(listenerBasis->right, projPlane);
emitterAzimuth = FAudio_atan2f(y, x);
emitterAzimuth += F3DAUDIO_PI;
if (emitterAzimuth < 0.0f)
{
emitterAzimuth += F3DAUDIO_2PI;
}
else if (emitterAzimuth > F3DAUDIO_2PI)
{
emitterAzimuth -= F3DAUDIO_2PI;
}
FindSpeakerAzimuths(curConfig, emitterAzimuth, skipCenter, infos);
a0 = infos[0]->azimuth;
a1 = infos[1]->azimuth;
if (a0 > a1)
{
if (emitterAzimuth >= a0)
{
emitterAzimuth -= F3DAUDIO_2PI;
}
a0 -= F3DAUDIO_2PI;
}
FAudio_assert(emitterAzimuth >= a0 && emitterAzimuth <= a1);
val = (emitterAzimuth - a0) / (a1 - a0);
i0 = infos[0]->matrixIdx;
i1 = infos[1]->matrixIdx;
pMatrixCoefficients[i0 * numSrcChannels + currentChannel] += (1.0f - val) * totalEnergy;
pMatrixCoefficients[i1 * numSrcChannels + currentChannel] += ( val) * totalEnergy;
}
if (flags & F3DAUDIO_CALCULATE_REDIRECT_TO_LFE)
{
FAudio_assert(curConfig->LFSpeakerIdx != -1);
pMatrixCoefficients[curConfig->LFSpeakerIdx * numSrcChannels + currentChannel] += LFEattenuation / numSrcChannels;
}
}
static inline void CalculateMatrix(
uint32_t ChannelMask,
uint32_t Flags,
const F3DAUDIO_LISTENER *pListener,
const F3DAUDIO_EMITTER *pEmitter,
uint32_t SrcChannelCount,
uint32_t DstChannelCount,
F3DAUDIO_VECTOR emitterToListener,
float eToLDistance,
float normalizedDistance,
float* MatrixCoefficients
) {
uint32_t iEC;
float curEmAzimuth;
const ConfigInfo* curConfig = GetConfigInfo(ChannelMask);
float attenuation = ComputeDistanceAttenuation(
normalizedDistance,
pEmitter->pVolumeCurve
);
float LFEattenuation = ComputeDistanceAttenuation(
normalizedDistance,
pEmitter->pLFECurve
);
F3DAUDIO_VECTOR listenerToEmitter;
F3DAUDIO_VECTOR listenerToEmChannel;
F3DAUDIO_BASIS listenerBasis;
if (pListener->pCone)
{
const float angle = -FAudio_acosf(
VectorDot(pListener->OrientFront, emitterToListener) /
eToLDistance
);
const float listenerConeParam = ComputeConeParameter(
eToLDistance,
angle,
pListener->pCone->InnerAngle,
pListener->pCone->OuterAngle,
pListener->pCone->InnerVolume,
pListener->pCone->OuterVolume
);
attenuation *= listenerConeParam;
LFEattenuation *= listenerConeParam;
}
if (pEmitter->pCone && pEmitter->ChannelCount == 1)
{
const float angle = FAudio_acosf(
VectorDot(pEmitter->OrientFront, emitterToListener) /
eToLDistance
);
const float emitterConeParam = ComputeConeParameter(
eToLDistance,
angle,
pEmitter->pCone->InnerAngle,
pEmitter->pCone->OuterAngle,
pEmitter->pCone->InnerVolume,
pEmitter->pCone->OuterVolume
);
attenuation *= emitterConeParam;
}
FAudio_zero(MatrixCoefficients, sizeof(float) * SrcChannelCount * DstChannelCount);
if (DstChannelCount == 1)
{
for (iEC = 0; iEC < pEmitter->ChannelCount; iEC += 1)
{
curEmAzimuth = 0.0f;
if (pEmitter->pChannelAzimuths)
{
curEmAzimuth = pEmitter->pChannelAzimuths[iEC];
}
if (curEmAzimuth != F3DAUDIO_2PI)
{
MatrixCoefficients[iEC] = attenuation;
}
}
}
else if (curConfig != NULL)
{
listenerToEmitter = VectorScale(emitterToListener, -1.0f);
listenerBasis.front = pListener->OrientFront;
listenerBasis.right = VectorCross(pListener->OrientTop, pListener->OrientFront);
listenerBasis.top = pListener->OrientTop;
if (pEmitter->ChannelCount == 1)
{
listenerToEmChannel = listenerToEmitter;
ComputeEmitterChannelCoefficients(
curConfig,
&listenerBasis,
pEmitter->InnerRadius,
listenerToEmChannel,
attenuation,
LFEattenuation,
Flags,
0 ,
1 ,
MatrixCoefficients
);
}
else
{
const F3DAUDIO_VECTOR emitterRight = VectorCross(pEmitter->OrientTop, pEmitter->OrientFront);
for (iEC = 0; iEC < pEmitter->ChannelCount; iEC += 1)
{
const float emChAzimuth = pEmitter->pChannelAzimuths[iEC];
if (emChAzimuth == F3DAUDIO_2PI)
{
MatrixCoefficients[curConfig->LFSpeakerIdx * pEmitter->ChannelCount + iEC] = LFEattenuation;
}
else
{
const F3DAUDIO_VECTOR emitterBaseToChannel = VectorAdd(
VectorScale(pEmitter->OrientFront, pEmitter->ChannelRadius * FAudio_cosf(emChAzimuth)),
VectorScale(emitterRight, pEmitter->ChannelRadius * FAudio_sinf(emChAzimuth))
);
listenerToEmChannel = VectorAdd(
listenerToEmitter,
emitterBaseToChannel
);
ComputeEmitterChannelCoefficients(
curConfig,
&listenerBasis,
pEmitter->InnerRadius,
listenerToEmChannel,
attenuation,
LFEattenuation,
Flags,
iEC,
pEmitter->ChannelCount,
MatrixCoefficients
);
}
}
}
}
else
{
FAudio_assert(0 && "Config info not found!");
}
}
static inline void CalculateDoppler(
float SpeedOfSound,
const F3DAUDIO_LISTENER* pListener,
const F3DAUDIO_EMITTER* pEmitter,
F3DAUDIO_VECTOR emitterToListener,
float eToLDistance,
float* listenerVelocityComponent,
float* emitterVelocityComponent,
float* DopplerFactor
) {
float scaledSpeedOfSound;
*DopplerFactor = 1.0f;
if (eToLDistance != 0.0f)
{
*listenerVelocityComponent =
VectorDot(emitterToListener, pListener->Velocity) / eToLDistance;
*emitterVelocityComponent =
VectorDot(emitterToListener, pEmitter->Velocity) / eToLDistance;
}
else
{
*listenerVelocityComponent = 0.0f;
*emitterVelocityComponent = 0.0f;
}
if (pEmitter->DopplerScaler > 0.0f)
{
scaledSpeedOfSound = SpeedOfSound / pEmitter->DopplerScaler;
*listenerVelocityComponent = FAudio_min(
*listenerVelocityComponent,
scaledSpeedOfSound
);
*emitterVelocityComponent = FAudio_min(
*emitterVelocityComponent,
scaledSpeedOfSound
);
*DopplerFactor = (
SpeedOfSound - pEmitter->DopplerScaler * *listenerVelocityComponent
) / (
SpeedOfSound - pEmitter->DopplerScaler * *emitterVelocityComponent
);
if (isnan(*DopplerFactor))
{
*DopplerFactor = 1.0f;
}
*DopplerFactor = FAudio_clamp(
*DopplerFactor,
0.5f,
4.0f
);
}
}
void F3DAudioCalculate(
const F3DAUDIO_HANDLE Instance,
const F3DAUDIO_LISTENER *pListener,
const F3DAUDIO_EMITTER *pEmitter,
uint32_t Flags,
F3DAUDIO_DSP_SETTINGS *pDSPSettings
) {
uint32_t i;
F3DAUDIO_VECTOR emitterToListener;
float eToLDistance, normalizedDistance, dp;
#define DEFAULT_POINTS(name, x1, y1, x2, y2) \
static F3DAUDIO_DISTANCE_CURVE_POINT name##Points[2] = \
{ \
{ x1, y1 }, \
{ x2, y2 } \
}; \
static F3DAUDIO_DISTANCE_CURVE name##Default = \
{ \
(F3DAUDIO_DISTANCE_CURVE_POINT*) &name##Points[0], 2 \
};
DEFAULT_POINTS(lpfDirect, 0.0f, 1.0f, 1.0f, 0.75f)
DEFAULT_POINTS(lpfReverb, 0.0f, 0.75f, 1.0f, 0.75f)
DEFAULT_POINTS(reverb, 0.0f, 1.0f, 1.0f, 0.0f)
#undef DEFAULT_POINTS
emitterToListener = VectorSub(pListener->Position, pEmitter->Position);
eToLDistance = VectorLength(emitterToListener);
pDSPSettings->EmitterToListenerDistance = eToLDistance;
F3DAudioCheckCalculateParams(Instance, pListener, pEmitter, Flags, pDSPSettings);
normalizedDistance = eToLDistance / pEmitter->CurveDistanceScaler;
if (Flags & F3DAUDIO_CALCULATE_MATRIX)
{
CalculateMatrix(
SPEAKERMASK(Instance),
Flags,
pListener,
pEmitter,
pDSPSettings->SrcChannelCount,
pDSPSettings->DstChannelCount,
emitterToListener,
eToLDistance,
normalizedDistance,
pDSPSettings->pMatrixCoefficients
);
}
if (Flags & F3DAUDIO_CALCULATE_LPF_DIRECT)
{
pDSPSettings->LPFDirectCoefficient = ComputeDistanceAttenuation(
normalizedDistance,
(pEmitter->pLPFDirectCurve != NULL) ?
pEmitter->pLPFDirectCurve :
&lpfDirectDefault
);
}
if (Flags & F3DAUDIO_CALCULATE_LPF_REVERB)
{
pDSPSettings->LPFReverbCoefficient = ComputeDistanceAttenuation(
normalizedDistance,
(pEmitter->pLPFReverbCurve != NULL) ?
pEmitter->pLPFReverbCurve :
&lpfReverbDefault
);
}
if (Flags & F3DAUDIO_CALCULATE_REVERB)
{
pDSPSettings->ReverbLevel = ComputeDistanceAttenuation(
normalizedDistance,
(pEmitter->pReverbCurve != NULL) ?
pEmitter->pReverbCurve :
&reverbDefault
);
}
if (Flags & F3DAUDIO_CALCULATE_DOPPLER)
{
CalculateDoppler(
SPEEDOFSOUND(Instance),
pListener,
pEmitter,
emitterToListener,
eToLDistance,
&pDSPSettings->ListenerVelocityComponent,
&pDSPSettings->EmitterVelocityComponent,
&pDSPSettings->DopplerFactor
);
}
if (Flags & F3DAUDIO_CALCULATE_EMITTER_ANGLE)
{
#define EMITTER_ANGLE_NULL_DISTANCE 1.2e-7
if (eToLDistance < EMITTER_ANGLE_NULL_DISTANCE)
{
pDSPSettings->EmitterToListenerAngle = F3DAUDIO_PI / 2.0f;
}
else
{
dp = VectorDot(emitterToListener, pEmitter->OrientFront) / eToLDistance;
pDSPSettings->EmitterToListenerAngle = FAudio_acosf(dp);
}
}
if ( (Flags & F3DAUDIO_CALCULATE_DELAY) &&
SPEAKERMASK(Instance) == SPEAKER_STEREO )
{
for (i = 0; i < pDSPSettings->DstChannelCount; i += 1)
{
pDSPSettings->pDelayTimes[i] = 0.0f;
}
FAudio_assert(0 && "DELAY not implemented!");
}
}