From 0ea55810ab2d1a500236a0b6483608838c56ba44 Mon Sep 17 00:00:00 2001 From: Jbleezy Date: Tue, 1 Oct 2024 14:29:28 -0700 Subject: [PATCH 1/3] Dump and load correct volume and pitch values for T6 sound aliases --- src/Common/Game/T6/CommonT6.h | 3 + .../T6/AssetLoaders/AssetLoaderSoundBank.cpp | 63 +++++++++++++++++-- .../T6/AssetDumpers/AssetDumperSndBank.cpp | 32 ++++++++-- 3 files changed, 88 insertions(+), 10 deletions(-) diff --git a/src/Common/Game/T6/CommonT6.h b/src/Common/Game/T6/CommonT6.h index f32c9d97..dd65a968 100644 --- a/src/Common/Game/T6/CommonT6.h +++ b/src/Common/Game/T6/CommonT6.h @@ -3,12 +3,15 @@ #include "T6.h" #include +#include namespace T6 { class Common { public: + static constexpr double AMP_RATIO = 100000.0 / std::numeric_limits::max(); + static constexpr int Com_HashKey(const char* str, const int maxLen) { if (str == nullptr) diff --git a/src/ObjLoading/Game/T6/AssetLoaders/AssetLoaderSoundBank.cpp b/src/ObjLoading/Game/T6/AssetLoaders/AssetLoaderSoundBank.cpp index 371187ab..22f62131 100644 --- a/src/ObjLoading/Game/T6/AssetLoaders/AssetLoaderSoundBank.cpp +++ b/src/ObjLoading/Game/T6/AssetLoaders/AssetLoaderSoundBank.cpp @@ -8,6 +8,7 @@ #include "Pool/GlobalAssetPool.h" #include "Utils/StringUtils.h" +#include #include #include #include @@ -113,7 +114,17 @@ unsigned int GetAliasSubListCount(const unsigned int startRow, const ParsedCsv& return count; } -bool LoadSoundAlias(MemoryManager* memory, SndAlias* alias, const ParsedCsvRow& row) +double DecibelsToAmp(double decibels) +{ + return std::pow(10.0, decibels / 20.0); +} + +double CentsToHertz(double cents) +{ + return std::numeric_limits::max() * std::pow(2, cents / 1200.0); +} + +bool LoadSoundAlias(MemoryManager* memory, SndAlias* alias, const ParsedCsvRow& row, const unsigned int rowNum) { memset(alias, 0, sizeof(SndAlias)); @@ -141,15 +152,55 @@ bool LoadSoundAlias(MemoryManager* memory, SndAlias* alias, const ParsedCsvRow& alias->duck = Common::SND_HashName(row.GetValue("duck").data()); - alias->volMin = row.GetValueIntvolMin)>("vol_min"); - alias->volMax = row.GetValueIntvolMax)>("vol_max"); + const auto volMinDecibels = row.GetValueFloat("vol_min"); + + if (volMinDecibels < 0.0f || volMinDecibels > 100.0f) + { + std::cerr << std::format("Invalid value for row {} col 'vol_min' - {} [0.0, 100.0]\n", rowNum + 1, volMinDecibels); + return false; + } + + const auto volMinAmp = static_cast(std::round(DecibelsToAmp(volMinDecibels) / T6::Common::AMP_RATIO)); + alias->volMin = volMinAmp; + + const auto volMaxDecibels = row.GetValueFloat("vol_max"); + + if (volMaxDecibels < 0.0f || volMaxDecibels > 100.0f) + { + std::cerr << std::format("Invalid value for row {} col 'vol_max' - {} [0.0, 100.0]\n", rowNum + 1, volMaxDecibels); + return false; + } + + const auto volMaxAmp = static_cast(std::round(DecibelsToAmp(volMaxDecibels) / T6::Common::AMP_RATIO)); + alias->volMax = volMaxAmp; + + const auto pitchMinCents = row.GetValueFloat("pitch_min"); + + if (pitchMinCents < -2400.0f || pitchMinCents > 1200.0f) + { + std::cerr << std::format("Invalid value for row {} col 'pitch_min' - {} [-2400.0, 1200.0]\n", rowNum + 1, pitchMinCents); + return false; + } + + const auto pitchMinHertz = static_cast(std::round(CentsToHertz(pitchMinCents))); + alias->pitchMin = pitchMinHertz; + + const auto pitchMaxCents = row.GetValueFloat("pitch_max"); + + if (pitchMaxCents < -2400.0f || pitchMaxCents > 1200.0f) + { + std::cerr << std::format("Invalid value for row {} col 'pitch_max' - {} [-2400.0, 1200.0]\n", rowNum + 1, pitchMaxCents); + return false; + } + + const auto pitchMaxHertz = static_cast(std::round(CentsToHertz(pitchMaxCents))); + alias->pitchMax = pitchMaxHertz; + alias->distMin = row.GetValueIntdistMin)>("dist_min"); alias->distMax = row.GetValueIntdistMax)>("dist_max"); alias->distReverbMax = row.GetValueIntdistReverbMax)>("dist_reverb_max"); alias->limitCount = row.GetValueIntlimitCount)>("limit_count"); alias->entityLimitCount = row.GetValueIntentityLimitCount)>("entity_limit_count"); - alias->pitchMin = row.GetValueIntpitchMin)>("pitch_min"); - alias->pitchMax = row.GetValueIntpitchMax)>("pitch_max"); alias->minPriority = row.GetValueIntminPriority)>("min_priority"); alias->maxPriority = row.GetValueIntmaxPriority)>("max_priority"); alias->minPriorityThreshold = row.GetValueIntminPriorityThreshold)>("min_priority_threshold"); @@ -292,7 +343,7 @@ bool LoadSoundAliasList( // list are next to each other in the file for (auto i = 0u; i < subListCount; i++) { - if (!LoadSoundAlias(memory, &sndBank->alias[listIndex].head[i], aliasCsv[row])) + if (!LoadSoundAlias(memory, &sndBank->alias[listIndex].head[i], aliasCsv[row], row)) return false; // if this asset is loaded instead of stream, increment the loaded count for later diff --git a/src/ObjWriting/Game/T6/AssetDumpers/AssetDumperSndBank.cpp b/src/ObjWriting/Game/T6/AssetDumpers/AssetDumperSndBank.cpp index d37b4905..e2617377 100644 --- a/src/ObjWriting/Game/T6/AssetDumpers/AssetDumperSndBank.cpp +++ b/src/ObjWriting/Game/T6/AssetDumpers/AssetDumperSndBank.cpp @@ -7,6 +7,7 @@ #include "Sound/WavWriter.h" #include "nlohmann/json.hpp" +#include #include #include #include @@ -225,6 +226,21 @@ class AssetDumperSndBank::Internal } } + static double AmpToDecibels(double amp) + { + if (amp == 0.0) + { + return 0.0; + } + + return 20.0 * std::log10(amp); + } + + static double HertzToCents(double hertz) + { + return 1200.0 * std::log2(hertz / std::numeric_limits::max()); + } + static void WriteAliasToFile(CsvOutputStream& stream, const SndAlias* alias, const std::optional maybeFormat, const SndBank* bank) { // name @@ -247,10 +263,14 @@ class AssetDumperSndBank::Internal stream.WriteColumn(SOUND_GROUPS[alias->flags.volumeGroup]); // vol_min - stream.WriteColumn(std::to_string(alias->volMin)); + const auto volMinAmp = alias->volMin * T6::Common::AMP_RATIO; + const auto volMinDecibels = static_cast(std::round(AmpToDecibels(volMinAmp))); + stream.WriteColumn(std::to_string(volMinDecibels)); // vol_max - stream.WriteColumn(std::to_string(alias->volMax)); + const auto volMaxAmp = alias->volMax * T6::Common::AMP_RATIO; + const auto volMaxDecibels = static_cast(std::round(AmpToDecibels(volMaxAmp))); + stream.WriteColumn(std::to_string(volMaxDecibels)); // team_vol_mod stream.WriteColumn(""); @@ -289,10 +309,14 @@ class AssetDumperSndBank::Internal stream.WriteColumn(SOUND_LIMIT_TYPES[alias->flags.entityLimitType]); // pitch_min - stream.WriteColumn(std::to_string(alias->pitchMin)); + const auto pitchMinHertz = alias->pitchMin; + const auto pitchMinCents = static_cast(std::round(HertzToCents(pitchMinHertz))); + stream.WriteColumn(std::to_string(pitchMinCents)); // pitch_max - stream.WriteColumn(std::to_string(alias->pitchMax)); + const auto pitchMaxHertz = alias->pitchMax; + const auto pitchMaxCents = static_cast(std::round(HertzToCents(pitchMaxHertz))); + stream.WriteColumn(std::to_string(pitchMaxCents)); // team_pitch_mod stream.WriteColumn(""); From 70bfd06fdccaed043b2d30677cbcf425720bdf34 Mon Sep 17 00:00:00 2001 From: Jbleezy Date: Tue, 1 Oct 2024 15:02:59 -0700 Subject: [PATCH 2/3] Fix linux build --- src/ObjLoading/Game/T6/AssetLoaders/AssetLoaderSoundBank.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/ObjLoading/Game/T6/AssetLoaders/AssetLoaderSoundBank.cpp b/src/ObjLoading/Game/T6/AssetLoaders/AssetLoaderSoundBank.cpp index 22f62131..08623db8 100644 --- a/src/ObjLoading/Game/T6/AssetLoaders/AssetLoaderSoundBank.cpp +++ b/src/ObjLoading/Game/T6/AssetLoaders/AssetLoaderSoundBank.cpp @@ -10,6 +10,7 @@ #include #include +#include #include #include #include From c1d3138b04f3637e34b5e4b51f7fe2edb7bfd68e Mon Sep 17 00:00:00 2001 From: Jan Date: Sun, 6 Oct 2024 17:59:36 +0200 Subject: [PATCH 3/3] fix: formula for dbspl and cent values --- src/Common/Game/T6/CommonT6.h | 3 - .../T6/AssetLoaders/AssetLoaderSoundBank.cpp | 88 ++++++++++--------- .../T6/AssetDumpers/AssetDumperSndBank.cpp | 53 ++++++----- 3 files changed, 75 insertions(+), 69 deletions(-) diff --git a/src/Common/Game/T6/CommonT6.h b/src/Common/Game/T6/CommonT6.h index dd65a968..f32c9d97 100644 --- a/src/Common/Game/T6/CommonT6.h +++ b/src/Common/Game/T6/CommonT6.h @@ -3,15 +3,12 @@ #include "T6.h" #include -#include namespace T6 { class Common { public: - static constexpr double AMP_RATIO = 100000.0 / std::numeric_limits::max(); - static constexpr int Com_HashKey(const char* str, const int maxLen) { if (str == nullptr) diff --git a/src/ObjLoading/Game/T6/AssetLoaders/AssetLoaderSoundBank.cpp b/src/ObjLoading/Game/T6/AssetLoaders/AssetLoaderSoundBank.cpp index 08623db8..32afda9e 100644 --- a/src/ObjLoading/Game/T6/AssetLoaders/AssetLoaderSoundBank.cpp +++ b/src/ObjLoading/Game/T6/AssetLoaders/AssetLoaderSoundBank.cpp @@ -115,14 +115,40 @@ unsigned int GetAliasSubListCount(const unsigned int startRow, const ParsedCsv& return count; } -double DecibelsToAmp(double decibels) +float DbsplToLinear(const float dbsplValue) { - return std::pow(10.0, decibels / 20.0); + return std::pow(10.0f, (dbsplValue - 100.0f) / 20.0f); } -double CentsToHertz(double cents) +float CentToHertz(const float cents) { - return std::numeric_limits::max() * std::pow(2, cents / 1200.0); + return std::pow(2.0f, cents / 1200.0f); +} + +bool ReadColumnVolumeDbspl(const float dbsplValue, const char* colName, const unsigned rowIndex, uint16_t& value) +{ + if (dbsplValue < 0.0f || dbsplValue > 100.0f) + { + std::cerr << std::format("Invalid value for row {} col '{}' - {} [0.0, 100.0]\n", rowIndex + 1, colName, dbsplValue); + return false; + } + + value = static_cast(DbsplToLinear(dbsplValue) * static_cast(std::numeric_limits::max())); + + return true; +} + +bool ReadColumnPitchCents(const float centValue, const char* colName, const unsigned rowIndex, uint16_t& value) +{ + if (centValue < -2400.0f || centValue > 1200.0f) + { + std::cerr << std::format("Invalid value for row {} col '{}' - {} [-2400.0, 1200.0]\n", rowIndex + 1, colName, centValue); + return false; + } + + value = static_cast(CentToHertz(centValue) * static_cast(std::numeric_limits::max())); + + return true; } bool LoadSoundAlias(MemoryManager* memory, SndAlias* alias, const ParsedCsvRow& row, const unsigned int rowNum) @@ -153,49 +179,17 @@ bool LoadSoundAlias(MemoryManager* memory, SndAlias* alias, const ParsedCsvRow& alias->duck = Common::SND_HashName(row.GetValue("duck").data()); - const auto volMinDecibels = row.GetValueFloat("vol_min"); - - if (volMinDecibels < 0.0f || volMinDecibels > 100.0f) - { - std::cerr << std::format("Invalid value for row {} col 'vol_min' - {} [0.0, 100.0]\n", rowNum + 1, volMinDecibels); + if (!ReadColumnVolumeDbspl(row.GetValueFloat("vol_min"), "vol_min", rowNum, alias->volMin)) return false; - } - const auto volMinAmp = static_cast(std::round(DecibelsToAmp(volMinDecibels) / T6::Common::AMP_RATIO)); - alias->volMin = volMinAmp; - - const auto volMaxDecibels = row.GetValueFloat("vol_max"); - - if (volMaxDecibels < 0.0f || volMaxDecibels > 100.0f) - { - std::cerr << std::format("Invalid value for row {} col 'vol_max' - {} [0.0, 100.0]\n", rowNum + 1, volMaxDecibels); + if (!ReadColumnVolumeDbspl(row.GetValueFloat("vol_max"), "vol_max", rowNum, alias->volMax)) return false; - } - - const auto volMaxAmp = static_cast(std::round(DecibelsToAmp(volMaxDecibels) / T6::Common::AMP_RATIO)); - alias->volMax = volMaxAmp; - - const auto pitchMinCents = row.GetValueFloat("pitch_min"); - if (pitchMinCents < -2400.0f || pitchMinCents > 1200.0f) - { - std::cerr << std::format("Invalid value for row {} col 'pitch_min' - {} [-2400.0, 1200.0]\n", rowNum + 1, pitchMinCents); + if (!ReadColumnVolumeDbspl(row.GetValueFloat("pitch_min"), "pitch_min", rowNum, alias->pitchMin)) return false; - } - - const auto pitchMinHertz = static_cast(std::round(CentsToHertz(pitchMinCents))); - alias->pitchMin = pitchMinHertz; - const auto pitchMaxCents = row.GetValueFloat("pitch_max"); - - if (pitchMaxCents < -2400.0f || pitchMaxCents > 1200.0f) - { - std::cerr << std::format("Invalid value for row {} col 'pitch_max' - {} [-2400.0, 1200.0]\n", rowNum + 1, pitchMaxCents); + if (!ReadColumnVolumeDbspl(row.GetValueFloat("pitch_max"), "pitch_max", rowNum, alias->pitchMax)) return false; - } - - const auto pitchMaxHertz = static_cast(std::round(CentsToHertz(pitchMaxCents))); - alias->pitchMax = pitchMaxHertz; alias->distMin = row.GetValueIntdistMin)>("dist_min"); alias->distMax = row.GetValueIntdistMax)>("dist_max"); @@ -208,11 +202,19 @@ bool LoadSoundAlias(MemoryManager* memory, SndAlias* alias, const ParsedCsvRow& alias->maxPriorityThreshold = row.GetValueIntmaxPriorityThreshold)>("max_priority_threshold"); alias->probability = row.GetValueIntprobability)>("probability"); alias->startDelay = row.GetValueIntstartDelay)>("start_delay"); - alias->reverbSend = row.GetValueIntreverbSend)>("reverb_send"); - alias->centerSend = row.GetValueIntcenterSend)>("center_send"); + + if (!ReadColumnVolumeDbspl(row.GetValueFloat("reverb_send"), "reverb_send", rowNum, alias->reverbSend)) + return false; + + if (!ReadColumnVolumeDbspl(row.GetValueFloat("center_send"), "center_send", rowNum, alias->centerSend)) + return false; + alias->envelopMin = row.GetValueIntenvelopMin)>("envelop_min"); alias->envelopMax = row.GetValueIntenvelopMax)>("envelop_max"); - alias->envelopPercentage = row.GetValueIntenvelopPercentage)>("envelop_percentage"); + + if (!ReadColumnVolumeDbspl(row.GetValueFloat("envelop_percentage"), "envelop_percentage", rowNum, alias->envelopPercentage)) + return false; + alias->occlusionLevel = row.GetValueIntocclusionLevel)>("occlusion_level"); alias->fluxTime = row.GetValueIntfluxTime)>("move_time"); alias->futzPatch = row.GetValueIntfutzPatch)>("futz"); diff --git a/src/ObjWriting/Game/T6/AssetDumpers/AssetDumperSndBank.cpp b/src/ObjWriting/Game/T6/AssetDumpers/AssetDumperSndBank.cpp index e2617377..d2afe546 100644 --- a/src/ObjWriting/Game/T6/AssetDumpers/AssetDumperSndBank.cpp +++ b/src/ObjWriting/Game/T6/AssetDumpers/AssetDumperSndBank.cpp @@ -226,19 +226,34 @@ class AssetDumperSndBank::Internal } } - static double AmpToDecibels(double amp) + static float LinearToDbspl(float linear) { - if (amp == 0.0) - { - return 0.0; - } + linear = std::max(linear, 0.0000152879f); + + const auto db = 20.0f * std::log10(linear); + if (db > -95.0f) + return db + 100.0f; + + return 0; + } - return 20.0 * std::log10(amp); + static float HertzToCents(const float hertz) + { + return 1200.0f * std::log2(hertz); + } + + static void WriteColumnVolumeLinear(CsvOutputStream& stream, const uint16_t value) + { + const auto linear = static_cast(value) / static_cast(std::numeric_limits::max()); + const auto dbSpl = std::clamp(LinearToDbspl(linear), 0.0f, 100.0f); + stream.WriteColumn(std::format("{:.3g}", dbSpl)); } - static double HertzToCents(double hertz) + static void WriteColumnPitchHertz(CsvOutputStream& stream, const uint16_t value) { - return 1200.0 * std::log2(hertz / std::numeric_limits::max()); + const auto hertz = static_cast(value) / static_cast(std::numeric_limits::max()); + const auto cents = std::clamp(HertzToCents(hertz), -2400.0f, 1200.0f); + stream.WriteColumn(std::format("{:.4g}", cents)); } static void WriteAliasToFile(CsvOutputStream& stream, const SndAlias* alias, const std::optional maybeFormat, const SndBank* bank) @@ -263,14 +278,10 @@ class AssetDumperSndBank::Internal stream.WriteColumn(SOUND_GROUPS[alias->flags.volumeGroup]); // vol_min - const auto volMinAmp = alias->volMin * T6::Common::AMP_RATIO; - const auto volMinDecibels = static_cast(std::round(AmpToDecibels(volMinAmp))); - stream.WriteColumn(std::to_string(volMinDecibels)); + WriteColumnVolumeLinear(stream, alias->volMin); // vol_max - const auto volMaxAmp = alias->volMax * T6::Common::AMP_RATIO; - const auto volMaxDecibels = static_cast(std::round(AmpToDecibels(volMaxAmp))); - stream.WriteColumn(std::to_string(volMaxDecibels)); + WriteColumnVolumeLinear(stream, alias->volMax); // team_vol_mod stream.WriteColumn(""); @@ -309,14 +320,10 @@ class AssetDumperSndBank::Internal stream.WriteColumn(SOUND_LIMIT_TYPES[alias->flags.entityLimitType]); // pitch_min - const auto pitchMinHertz = alias->pitchMin; - const auto pitchMinCents = static_cast(std::round(HertzToCents(pitchMinHertz))); - stream.WriteColumn(std::to_string(pitchMinCents)); + WriteColumnPitchHertz(stream, alias->pitchMin); // pitch_max - const auto pitchMaxHertz = alias->pitchMax; - const auto pitchMaxCents = static_cast(std::round(HertzToCents(pitchMaxHertz))); - stream.WriteColumn(std::to_string(pitchMaxCents)); + WriteColumnPitchHertz(stream, alias->pitchMax); // team_pitch_mod stream.WriteColumn(""); @@ -352,7 +359,7 @@ class AssetDumperSndBank::Internal stream.WriteColumn(std::to_string(alias->startDelay)); // reverb_send - stream.WriteColumn(std::to_string(alias->reverbSend)); + WriteColumnVolumeLinear(stream, alias->reverbSend); // duck stream.WriteColumn(FindNameForDuck(alias->duck, bank)); @@ -364,7 +371,7 @@ class AssetDumperSndBank::Internal stream.WriteColumn(alias->flags.panType == SA_PAN_2D ? "2d" : "3d"); // center_send - stream.WriteColumn(std::to_string(alias->centerSend)); + WriteColumnVolumeLinear(stream, alias->centerSend); // envelop_min stream.WriteColumn(std::to_string(alias->envelopMin)); @@ -373,7 +380,7 @@ class AssetDumperSndBank::Internal stream.WriteColumn(std::to_string(alias->envelopMax)); // envelop_percentage - stream.WriteColumn(std::to_string(alias->envelopPercentage)); + WriteColumnVolumeLinear(stream, alias->envelopPercentage); // occlusion_level stream.WriteColumn(std::to_string(alias->occlusionLevel));