Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
0ae370b
Add sound distance to world sound event
iManGaaX Sep 6, 2026
03fb587
Integrate CClientWorldSoundManager into CClientGame
iManGaaX Sep 6, 2026
419c6df
Add CClientWorldSoundManager to CClientGame
iManGaaX Sep 6, 2026
59c7ab9
Implement ValidateSound function for audio validation
iManGaaX Sep 6, 2026
cda584a
Add ValidateSound method to CClientSoundManager
iManGaaX Sep 6, 2026
74dfcb5
Add CClientWorldSoundManager for sound replacement
iManGaaX Sep 6, 2026
bda3f3f
Add CClientWorldSoundManager header file
iManGaaX Sep 6, 2026
a804736
Add world sound management functions to CLuaEngineDefs
iManGaaX Sep 6, 2026
0c7cdeb
Add new Lua engine sound replacement functions
iManGaaX Sep 6, 2026
cc4ff71
Add fMaxDistance member to CAudioEngine struct
iManGaaX Sep 6, 2026
7375abd
Add audio hardware bank loader and slot functions
iManGaaX Sep 6, 2026
26c0552
Add new methods for sound info and patching
iManGaaX Sep 6, 2026
b97eeaf
Implement PCM decoding in CClientSoundManager
iManGaaX Sep 6, 2026
6e1361c
Add DecodeToPcm method to CClientSoundManager
iManGaaX Sep 6, 2026
974cfee
Refactor sound replacement logic in CClientWorldSoundManager
iManGaaX Sep 6, 2026
4808320
Enhance CClientWorldSoundManager with native sound support
iManGaaX Sep 6, 2026
f5a815c
Add new methods for sound bank management
iManGaaX Sep 6, 2026
b56d4b8
Add GetChannelFrequencyScalingFactors method
iManGaaX Sep 6, 2026
5d92852
Add GetChannelFrequencyScalingFactors method
iManGaaX Sep 6, 2026
4d5270d
Add new constants for audio hardware offsets
iManGaaX Sep 6, 2026
7785107
Refactor sound replacement logic in CClientWorldSoundManager
iManGaaX Sep 6, 2026
6705351
Update purpose comment in CClientWorldSoundManager.cpp
iManGaaX Sep 7, 2026
c9700f9
Update purpose comment in CClientWorldSoundManager.h
iManGaaX Sep 7, 2026
7463beb
Add new audio functions for sound management
iManGaaX Sep 7, 2026
95e5b7b
Add new audio-related Lua declarations
iManGaaX Sep 7, 2026
a85ad27
Remove world sound management functions
iManGaaX Sep 7, 2026
00f53f2
Remove unused world sound Lua engine declarations
iManGaaX Sep 7, 2026
b2e0753
Remove unused includes in CLuaEngineDefs.cpp
iManGaaX Sep 7, 2026
6e4455b
Refactor audio functions to use optional parameters
iManGaaX Sep 7, 2026
2116510
Add static methods for world sound management
iManGaaX Sep 7, 2026
605a8a3
Add IsWorldSoundStillActive method to CAudioEngineSA
iManGaaX Sep 7, 2026
6face62
Enhance sound management with looping support
iManGaaX Sep 7, 2026
85bec49
Enhance sound structure with group and index fields
iManGaaX Sep 7, 2026
8ec657e
Add loop flag and IsWorldSoundStillActive method
iManGaaX Sep 7, 2026
63c5b72
Add member variable for channel sound table
iManGaaX Sep 7, 2026
76080e5
Refactor CAudioEngineSA.h structure and variables
iManGaaX Sep 7, 2026
70c939d
Refactor world-sound replacement
iManGaaX Sep 8, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
241 changes: 241 additions & 0 deletions Client/game_sa/CAEAudioHardwareSA.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -53,3 +53,244 @@ void CAEAudioHardwareSA::LoadSoundBank(short wSoundBankID, short wSoundBankSlotI
}
// clang-format on
}

namespace
{
constexpr DWORD NUM_AudioHardwareBankLoaderOffset = 0xD98;

constexpr DWORD NUM_AudioHardwareNumChannelsOffset = 0x8E;
constexpr DWORD NUM_AudioHardwareFreqScalingOffset = 0x310;

constexpr DWORD NUM_BankLoaderSlotsOffset = 0x00;
constexpr DWORD NUM_BankLoaderBankLkupsOffset = 0x04;
constexpr DWORD NUM_BankLoaderSlotCountOffset = 0x0C;
constexpr DWORD NUM_BankLoaderBankLkupCntOffset = 0x0E;
constexpr DWORD NUM_BankLoaderBufferSizeOffset = 0x18;
constexpr DWORD NUM_BankLoaderBufferOffset = 0x1C;

constexpr DWORD NUM_BankSlotSize = 0x12D4;
constexpr DWORD NUM_BankSlotOffsetBytesOffset = 0x00;
constexpr DWORD NUM_BankSlotNumBytesOffset = 0x04;
constexpr DWORD NUM_BankSlotBankIdOffset = 0x10;
constexpr DWORD NUM_BankSlotNumSoundsOffset = 0x12;
constexpr DWORD NUM_BankSlotSoundsArrayOffset = 0x14;

constexpr DWORD NUM_BankSlotItemSize = 0x0C;
constexpr DWORD NUM_BankSlotItemBufferOffsetOffset = 0x00;
constexpr DWORD NUM_BankSlotItemLoopOffsetOffset = 0x04;
constexpr DWORD NUM_BankSlotItemSampleFreqOffset = 0x08;

constexpr DWORD NUM_BankLookupSize = 0x0C;
constexpr DWORD NUM_BankLookupNumBytesOffset = 0x08;

constexpr uint NUM_MaxBankSounds = 400;

const BYTE* GetBankLoader()
{
const BYTE* pAudioHardware = reinterpret_cast<const BYTE*>(CLASS_CAEAudioHardware);
return *reinterpret_cast<BYTE* const*>(pAudioHardware + NUM_AudioHardwareBankLoaderOffset);
}

const BYTE* GetBankSlot(uint usBankSlot)
{
const BYTE* pBankLoader = GetBankLoader();
if (!pBankLoader)
return nullptr;

const ushort usSlotCount = *reinterpret_cast<const ushort*>(pBankLoader + NUM_BankLoaderSlotCountOffset);
if (usBankSlot >= usSlotCount)
return nullptr;

const BYTE* pBankSlots = *reinterpret_cast<BYTE* const*>(pBankLoader + NUM_BankLoaderSlotsOffset);
if (!pBankSlots)
return nullptr;

return pBankSlots + usBankSlot * NUM_BankSlotSize;
}

const BYTE* GetBankSlotData(const BYTE* pBankSlot)
{
const BYTE* pBankLoader = GetBankLoader();
if (!pBankLoader)
return nullptr;

const BYTE* pBuffer = *reinterpret_cast<BYTE* const*>(pBankLoader + NUM_BankLoaderBufferOffset);
if (!pBuffer)
return nullptr;

const uint uiBufferSize = *reinterpret_cast<const uint*>(pBankLoader + NUM_BankLoaderBufferSizeOffset);
const uint uiOffsetBytes = *reinterpret_cast<const uint*>(pBankSlot + NUM_BankSlotOffsetBytesOffset);
const uint uiNumBytes = *reinterpret_cast<const uint*>(pBankSlot + NUM_BankSlotNumBytesOffset);

if (uiOffsetBytes + uiNumBytes > uiBufferSize || uiNumBytes == 0)
return nullptr;

return pBuffer + uiOffsetBytes;
}

const BYTE* GetBankSlotItem(const BYTE* pBankSlot, uint usIndex)
{
return pBankSlot + NUM_BankSlotSoundsArrayOffset + usIndex * NUM_BankSlotItemSize;
}

uint GetBankPcmSize(const BYTE* pBankSlot)
{
const BYTE* pBankLoader = GetBankLoader();
if (!pBankLoader)
return 0;

const BYTE* pBankLkups = *reinterpret_cast<BYTE* const*>(pBankLoader + NUM_BankLoaderBankLkupsOffset);
if (!pBankLkups)
return 0;

const ushort usBankLkupCnt = *reinterpret_cast<const ushort*>(pBankLoader + NUM_BankLoaderBankLkupCntOffset);
const short sBankId = *reinterpret_cast<const short*>(pBankSlot + NUM_BankSlotBankIdOffset);
if (sBankId < 0 || sBankId >= static_cast<short>(usBankLkupCnt))
return 0;

return *reinterpret_cast<const uint*>(pBankLkups + sBankId * NUM_BankLookupSize + NUM_BankLookupNumBytesOffset);
}
}

bool CAEAudioHardwareSA::GetLoadedSoundInfo(unsigned short usBankSlot, unsigned short usIndex, void*& pOutPcmData, unsigned int& uiOutPcmSize,
unsigned int& uiOutSampleRate, int& iOutLoopStartOffset) const
{
pOutPcmData = nullptr;
uiOutPcmSize = 0;
uiOutSampleRate = 0;
iOutLoopStartOffset = -1;

const BYTE* pBankSlot = GetBankSlot(usBankSlot);
if (!pBankSlot)
return false;

const short sNumSounds = *reinterpret_cast<const short*>(pBankSlot + NUM_BankSlotNumSoundsOffset);
if (sNumSounds < 0 || usIndex >= static_cast<uint>(sNumSounds) || usIndex >= NUM_MaxBankSounds)
return false;

const BYTE* pSlotData = GetBankSlotData(pBankSlot);
if (!pSlotData)
return false;

const uint uiNumBytes = *reinterpret_cast<const uint*>(pBankSlot + NUM_BankSlotNumBytesOffset);

const BYTE* pItem = GetBankSlotItem(pBankSlot, usIndex);
const uint uiBufferOffset = *reinterpret_cast<const uint*>(pItem + NUM_BankSlotItemBufferOffsetOffset);

uint uiSize = 0;
if (usIndex + 1 < static_cast<uint>(sNumSounds))
{
const uint uiNextOffset = *reinterpret_cast<const uint*>(GetBankSlotItem(pBankSlot, usIndex + 1) + NUM_BankSlotItemBufferOffsetOffset);
if (uiNextOffset <= uiBufferOffset)
return false;
uiSize = uiNextOffset - uiBufferOffset;
}
else
{
const uint uiBankPcmSize = GetBankPcmSize(pBankSlot);
if (uiBankPcmSize > 0)
{
if (uiBufferOffset >= uiBankPcmSize)
return false;
uiSize = uiBankPcmSize - uiBufferOffset;
}
else
{
if (uiBufferOffset >= uiNumBytes)
return false;
uiSize = uiNumBytes - uiBufferOffset;
}
}

const BYTE* pPcmData = pSlotData + uiBufferOffset;
if (pPcmData + uiSize > pSlotData + uiNumBytes)
return false;

pOutPcmData = const_cast<BYTE*>(pPcmData);
uiOutPcmSize = uiSize;
uiOutSampleRate = *reinterpret_cast<const ushort*>(pItem + NUM_BankSlotItemSampleFreqOffset);
iOutLoopStartOffset = *reinterpret_cast<const int*>(pItem + NUM_BankSlotItemLoopOffsetOffset);
return true;
}

uint CAEAudioHardwareSA::GetNumSoundsInBankSlot(unsigned short usBankSlot) const
{
const BYTE* pBankSlot = GetBankSlot(usBankSlot);
if (!pBankSlot)
return 0;

const short sNumSounds = *reinterpret_cast<const short*>(pBankSlot + NUM_BankSlotNumSoundsOffset);
if (sNumSounds <= 0)
return 0;

return static_cast<uint>(std::min<short>(sNumSounds, NUM_MaxBankSounds));
}

void CAEAudioHardwareSA::GetChannelFrequencyScalingFactors(float* pOutFactors, unsigned int uiMax) const
{
if (!pOutFactors || uiMax == 0)
return;

const BYTE* pBase = reinterpret_cast<const BYTE*>(m_pInterface);
const ushort usNumChannels = *reinterpret_cast<const ushort*>(pBase + NUM_AudioHardwareNumChannelsOffset);
const uint uiCount = std::min<uint>(uiMax, usNumChannels);
const float* pFactors = reinterpret_cast<const float*>(pBase + NUM_AudioHardwareFreqScalingOffset);

for (uint i = 0; i < uiCount; ++i)
pOutFactors[i] = pFactors[i];
}

bool CAEAudioHardwareSA::SetSoundSampleRate(unsigned short usBankSlot, unsigned short usIndex, unsigned short usSampleRate)
{
const BYTE* pBankSlot = GetBankSlot(usBankSlot);
if (!pBankSlot)
return false;

const short sNumSounds = *reinterpret_cast<const short*>(pBankSlot + NUM_BankSlotNumSoundsOffset);
if (sNumSounds < 0 || usIndex >= static_cast<uint>(sNumSounds) || usIndex >= NUM_MaxBankSounds)
return false;

BYTE* pItem = const_cast<BYTE*>(GetBankSlotItem(pBankSlot, usIndex));
if (!pItem)
return false;

*reinterpret_cast<unsigned short*>(pItem + NUM_BankSlotItemSampleFreqOffset) = usSampleRate;
return true;
}

bool CAEAudioHardwareSA::PatchSoundBuffer(unsigned short usBankSlot, unsigned short usIndex, const void* pPcmData, unsigned int uiDataSize)
{
void* pPcmDataDst = nullptr;
uint uiPcmSize = 0;
uint uiSampleRate = 0;
int iLoopStartOffset = -1;

if (!GetLoadedSoundInfo(usBankSlot, usIndex, pPcmDataDst, uiPcmSize, uiSampleRate, iLoopStartOffset))
return false;

if (!pPcmData || uiDataSize > uiPcmSize)
return false;

memcpy(pPcmDataDst, pPcmData, uiDataSize);
if (uiDataSize < uiPcmSize)
{
BYTE* pPad = static_cast<BYTE*>(pPcmDataDst) + uiDataSize;
uint uiPadSize = uiPcmSize - uiDataSize;
if (iLoopStartOffset >= 0)
{
while (uiPadSize > 0)
{
const uint uiCopy = std::min(uiPadSize, uiDataSize);
memcpy(pPad, pPcmData, uiCopy);
pPad += uiCopy;
uiPadSize -= uiCopy;
}
}
else
{
memset(pPad, 0, uiPadSize);
}
}

return true;
}
7 changes: 7 additions & 0 deletions Client/game_sa/CAEAudioHardwareSA.h
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,13 @@ class CAEAudioHardwareSA : public CAEAudioHardware
bool IsSoundBankLoaded(short wSoundBankID, short wSoundBankSlotID);
void LoadSoundBank(short wSoundBankID, short wSoundBankSlotID);

bool GetLoadedSoundInfo(unsigned short usBankSlot, unsigned short usIndex, void*& pOutPcmData, unsigned int& uiOutPcmSize, unsigned int& uiOutSampleRate,
int& iOutLoopStartOffset) const override;
uint GetNumSoundsInBankSlot(unsigned short usBankSlot) const override;
bool PatchSoundBuffer(unsigned short usBankSlot, unsigned short usIndex, const void* pPcmData, unsigned int uiDataSize) override;
bool SetSoundSampleRate(unsigned short usBankSlot, unsigned short usIndex, unsigned short usSampleRate) override;
void GetChannelFrequencyScalingFactors(float* pOutFactors, unsigned int uiMax) const override;

private:
CAEAudioHardwareSAInterface* m_pInterface;
};
1 change: 1 addition & 0 deletions Client/game_sa/CAESoundManagerSA.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@

class CAESoundManagerSAInterface
{
public:
int16_t m_wNumAvailableChannels; // + 0x0000 // = CAEAudioHardware::GetNumAvailableChannels(...), [10, 300]
int16_t m_wChannel; // + 0x0002 // = CAEAudioHardware::AllocateChannels(...), could be -1
CAESound m_aSound[300]; // + 0x0004
Expand Down
48 changes: 48 additions & 0 deletions Client/game_sa/CAudioEngineSA.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
#include "CAEAudioHardwareSA.h"
#include "CAudioEngineSA.h"
#include "CGameSA.h"
#include "CPadSA.h"
#include "CPhysicalSA.h"
#include "CSettingsSA.h"

Expand Down Expand Up @@ -559,6 +560,9 @@ bool CAudioEngineSA::OnWorldSound(CAESound* pAESound)
pAESound->usIndex,
pGameEntity,
pAESound->m_vCurrPosn,
pAESound->m_fSoundDistance,
pAESound->usGroup == BANKSLOT_HORNS || pAESound->m_nLoopCounter < 0 || pAESound->m_nLoopCounter > 1,
pAESound,
};

return m_pWorldSoundHandler(event);
Expand All @@ -567,6 +571,50 @@ bool CAudioEngineSA::OnWorldSound(CAESound* pAESound)
return true;
}

void CAudioEngineSA::SetWorldSoundMaxDistance(CAESound* pAESound, float fMaxDistance)
{
if (!pAESound || fMaxDistance <= 0.0f)
return;

pAESound->m_fSoundDistance = fMaxDistance;
}

bool CAudioEngineSA::IsWorldSoundStillActive(uint uiGroup, uint uiIndex, CEntitySAInterface* pEntity) const
{
if (uiGroup == BANKSLOT_HORNS)
{
auto* pPad = dynamic_cast<CPadSA*>(pGame ? pGame->GetPad() : nullptr);
if (!pPad)
return false;

const auto* pPadInterface = pPad->GetInterface();
for (uint uiHistoryIndex = 0; uiHistoryIndex < MAX_HORN_HISTORY; ++uiHistoryIndex)
{
if (pPadInterface->bHornHistory[uiHistoryIndex])
return true;
}
return false;
}

const auto* pSoundManager = reinterpret_cast<const CAESoundManagerSAInterface*>(CLASS_CAESoundManager);
if (!pSoundManager)
return false;

for (const CAESound& sound : pSoundManager->m_aSound)
{
if (sound.m_nIsUsed != 0 && sound.usGroup == uiGroup && sound.usIndex == uiIndex)
{
if (!pEntity)
return true;
if (sound.pGameEntity == pEntity)
return true;
if (sound.pAudioEntity && sound.pAudioEntity->pEntity == pEntity)
return true;
}
}
return false;
}

////////////////////////////////////////////////////////////////////////////////////////////////
// Return false to skip sound
static bool _cdecl OnRequestNewSound(CAESound* pAESound)
Expand Down
29 changes: 15 additions & 14 deletions Client/game_sa/CAudioEngineSA.h
Original file line number Diff line number Diff line change
Expand Up @@ -75,11 +75,9 @@ class CAESound
float m_fCurrCamDist; // +72
float m_fPrevCamDist; // +76
float m_fTimeScale; // +80
char unk2; // +84 = 31488
char unk3; // +85 = 1005
union
{
unsigned short m_wEnvironmentFlags;
unsigned short m_wEnvironmentFlags; // +84
struct
{
unsigned short m_bFrontEnd : 1;
Expand All @@ -97,17 +95,18 @@ class CAESound
unsigned short m_bForcedFront : 1;
};
};
unsigned short m_wIsUsed; // +88
short unk4; // +90 = 1005
short m_wCurrentPlayPosition; // +92
short unk5; // +94 = 0
float m_fFinalVolume; // +96
float m_fFrequency; // +100
short m_wPlayingState; // +104
char unk6[2]; // +106
float m_fSoundHeadRoom; // +108
short m_wSoundLength; // +112
short unk8; // +114
short m_nLoopCounter; // +86 -1 = loop forever, >1 = replay N times, 0/1 = single play
short m_nMaxNumReplays; // +88
short m_nIsUsed; // +90 1 while the sound pool slot is in use
short m_wCurrentPlayPosition; // +92
short m_wLastPlayPosition; // +94
float m_fFinalVolume; // +96
float m_fFrequency; // +100
short m_wPlayingState; // +104
char unk6[2]; // +106
float m_fSoundHeadRoom; // +108
short m_wSoundLength; // +112
short unk8; // +114
};
static_assert(sizeof(CAESound) == 0x74, "Invalid size for CAESound");

Expand Down Expand Up @@ -138,8 +137,10 @@ class CAudioEngineSA : public CAudioEngine
bool IsWorldSoundEnabled(uint uiGroup, uint uiIndex);
void ResetWorldSounds();
void SetWorldSoundHandler(WorldSoundHandler* pHandler);
void SetWorldSoundMaxDistance(CAESound* pAESound, float fMaxDistance);
void ReportBulletHit(CEntity* pEntity, unsigned char ucSurfaceType, CVector* pvecPosition, float f_2);
void ReportWeaponEvent(int iEvent, eWeaponType weaponType, CPhysical* pPhysical);
bool IsWorldSoundStillActive(uint uiGroup, uint uiIndex, CEntitySAInterface* pEntity) const;

void UpdateAmbientSoundSettings();
bool OnWorldSound(CAESound* pAESound);
Expand Down
Loading
Loading