Add a config compat option for NFC distance scaling

Currently there's no way for an application to specify the "real world" scale
for in-game units. If the game doesn't use one unit to represent what should be
perceived as one meter to the user, the NFC filter's effect will be too near or
too far sounding. This allows adjusting the unit scale as applied to the NFC
filters only, correcting the misalignment.

This also removes the influence of EFX's MetersPerUnit from the NFC filters,
since many games don't use it, and those that do won't know it also influences
the perceived wave curvature along with the air absorption strength.
master
Chris Robinson 2022-07-07 22:11:38 -07:00
parent edadd221ea
commit 1ad553b5a9
4 changed files with 20 additions and 8 deletions

View File

@ -1140,7 +1140,7 @@ void alc_initconfig(void)
compatflags.set(CompatFlags::ReverseY, checkflag("__ALSOFT_REVERSE_Y", "reverse-y"));
compatflags.set(CompatFlags::ReverseZ, checkflag("__ALSOFT_REVERSE_Z", "reverse-z"));
aluInit(compatflags);
aluInit(compatflags, ConfigValueFloat(nullptr, "game_compat", "nfc-scale").value_or(1.0f));
Voice::InitMixer(ConfigValueStr(nullptr, nullptr, "resampler"));
auto traperr = al::getenv("ALSOFT_TRAP_ERROR");

View File

@ -135,6 +135,9 @@ float XScale{1.0f};
float YScale{1.0f};
float ZScale{1.0f};
/* Source distance scale for NFC filters. */
float NfcScale{1.0f};
struct ChanMap {
Channel channel;
@ -241,12 +244,14 @@ inline ResamplerFunc SelectResampler(Resampler resampler, uint increment)
} // namespace
void aluInit(CompatFlagBitset flags)
void aluInit(CompatFlagBitset flags, const float nfcscale)
{
MixDirectHrtf = SelectHrtfMixer();
XScale = flags.test(CompatFlags::ReverseX) ? -1.0f : 1.0f;
YScale = flags.test(CompatFlags::ReverseY) ? -1.0f : 1.0f;
ZScale = flags.test(CompatFlags::ReverseZ) ? -1.0f : 1.0f;
NfcScale = clampf(nfcscale, 0.0001f, 10000.0f);
}
@ -787,7 +792,7 @@ void CalcPanningAndFilters(Voice *voice, const float xpos, const float ypos, con
/* Clamp the distance for really close sources, to prevent
* excessive bass.
*/
const float mdist{maxf(Distance, Device->AvgSpeakerDist/4.0f)};
const float mdist{maxf(Distance*NfcScale, Device->AvgSpeakerDist/4.0f)};
const float w0{SpeedOfSoundMetersPerSec / (mdist * Frequency)};
/* Only need to adjust the first channel of a B-Format source. */
@ -963,7 +968,7 @@ void CalcPanningAndFilters(Voice *voice, const float xpos, const float ypos, con
/* Get the HRIR coefficients and delays just once, for the given
* source direction.
*/
GetHrtfCoeffs(Device->mHrtf.get(), ev, az, Distance, Spread,
GetHrtfCoeffs(Device->mHrtf.get(), ev, az, Distance*NfcScale, Spread,
voice->mChans[0].mDryParams.Hrtf.Target.Coeffs,
voice->mChans[0].mDryParams.Hrtf.Target.Delay);
voice->mChans[0].mDryParams.Hrtf.Target.Gain = DryGain.Base;
@ -1041,7 +1046,7 @@ void CalcPanningAndFilters(Voice *voice, const float xpos, const float ypos, con
/* Clamp the distance for really close sources, to prevent
* excessive bass.
*/
const float mdist{maxf(Distance, Device->AvgSpeakerDist/4.0f)};
const float mdist{maxf(Distance*NfcScale, Device->AvgSpeakerDist/4.0f)};
const float w0{SpeedOfSoundMetersPerSec / (mdist * Frequency)};
/* Adjust NFC filters. */
@ -1513,8 +1518,7 @@ void CalcAttnSourceParams(Voice *voice, const VoiceProps *props, const ContextBa
spread = std::asin(props->Radius/Distance) * 2.0f;
CalcPanningAndFilters(voice, ToSource[0]*XScale, ToSource[1]*YScale, ToSource[2]*ZScale,
Distance*context->mParams.MetersPerUnit, spread, DryGain, WetGain, SendSlots, props,
context->mParams, Device);
Distance, spread, DryGain, WetGain, SendSlots, props, context->mParams, Device);
}
void CalcSourceParams(Voice *voice, ContextBase *context, bool force)

View File

@ -24,7 +24,7 @@ enum CompatFlags : uint8_t {
};
using CompatFlagBitset = std::bitset<CompatFlags::Count>;
void aluInit(CompatFlagBitset flags);
void aluInit(CompatFlagBitset flags, const float nfcscale);
/* aluInitRenderer
*

View File

@ -602,6 +602,14 @@
##
[game_compat]
## nfc-scale: (global)
# A meters-per-unit distance scale applied to NFC filters. If a game doesn't
# use real-world meters for in-game units, the filters may create a too-near
# or too-distant effect. For instance, if the game uses 1 foot per unit, a
# value of 0.3048 will correctly adjust the filters. Or if the game uses 1
# kilometer per unit, a value of 1000 will correctly adjust the filters.
#nfc-scale = 1
## reverse-x: (global)
# Reverses the local X (left-right) position of 3D sound sources.
#reverse-x = false