Skip to content
This repository has been archived by the owner on Jan 5, 2024. It is now read-only.

Commit

Permalink
Merge pull request #572 from cortex-command-community/fmod-custom-pan…
Browse files Browse the repository at this point in the history
…ning

Custom Pan Value for SoundContainers
  • Loading branch information
Causeless authored Dec 20, 2023
2 parents 6900ffa + 309ab6a commit 3367d0a
Show file tree
Hide file tree
Showing 7 changed files with 81 additions and 6 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
New `SoundContainer` INI and Lua (R/W) property `BusRouting`, which denotes which bus the SoundContainer routes to. Available busses: `SFX, UI, Music`. Defaults to `SFX`.
`Enum` binding for `SoundContainer.BusRouting`: `SFX = 0, UI = 1, MUSIC = 2`.
New `SoundContainer` INI and Lua (R/W) property `PanningStrengthMultiplier`, which will multiply the strength of 3D panning. This can be used to achieve for example a psuedo-Immobile effect where attenuation effects are still applied but the sound does not move from the center. Recommended to keep between 0.0 and 1.0.
New `SoundContainer` INI and Lua (R/W) property `CustomPanValue`, which hard-overrides the panning of a sound. Clamped between -1 and 1 for left and right panning. 0 disables the override and will re-enable default behavior. This should probably only be used on Immobile sounds, but it can be used on any sound. No guarantees.

- Tracy profiler integration.
You can now attach Tracy to builds of the game and see profiling information about various zones and how long they take.
Expand Down
8 changes: 8 additions & 0 deletions Entities/SoundContainer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ namespace RTE {
m_BusRouting = BusRouting::SFX;
m_Immobile = false;
m_AttenuationStartDistance = c_DefaultAttenuationStartDistance;
m_CustomPanValue = 0.0f;
m_PanningStrengthMultiplier = 1.0F;
m_Loops = 0;
m_SoundPropertiesUpToDate = false;
Expand All @@ -54,6 +55,7 @@ namespace RTE {
m_BusRouting = reference.m_BusRouting;
m_Immobile = reference.m_Immobile;
m_AttenuationStartDistance = reference.m_AttenuationStartDistance;
m_CustomPanValue = reference.m_CustomPanValue;
m_PanningStrengthMultiplier = reference.m_PanningStrengthMultiplier;
m_Loops = reference.m_Loops;

Expand Down Expand Up @@ -107,6 +109,10 @@ namespace RTE {
});
MatchProperty("Immobile", { reader >> m_Immobile; });
MatchProperty("AttenuationStartDistance", { reader >> m_AttenuationStartDistance; });
MatchProperty("CustomPanValue", {
reader >> m_CustomPanValue;
if (m_CustomPanValue < -1.0f || m_CustomPanValue > 1.0f) { reader.ReportError("SoundContainer CustomPanValue must be between -1 and 1."); }
});
MatchProperty("PanningStrengthMultiplier", { reader >> m_PanningStrengthMultiplier; });
MatchProperty("LoopSetting", { reader >> m_Loops; });
MatchProperty("Priority", {
Expand Down Expand Up @@ -146,6 +152,8 @@ namespace RTE {
writer << m_Immobile;
writer.NewProperty("AttenuationStartDistance");
writer << m_AttenuationStartDistance;
writer.NewProperty("CustomPanValue");
writer << m_CustomPanValue;
writer.NewProperty("PanningStrengthMultiplier");
writer << m_PanningStrengthMultiplier;
writer.NewProperty("LoopSetting");
Expand Down
13 changes: 13 additions & 0 deletions Entities/SoundContainer.h
Original file line number Diff line number Diff line change
Expand Up @@ -204,6 +204,18 @@ namespace RTE {
/// <param name="attenuationStartDistance">The new attenuation start distance.</param>
void SetAttenuationStartDistance(float attenuationStartDistance) { m_AttenuationStartDistance = (attenuationStartDistance < 0) ? c_DefaultAttenuationStartDistance : attenuationStartDistance; m_SoundPropertiesUpToDate = false; }

/// <summary>
/// Gets the custom pan value of this SoundContainer.
/// </summary>
/// <returns>A float with the custom pan value.</returns>
float GetCustomPanValue() const { return m_CustomPanValue; }

/// <summary>
/// Sets the custom pan value of this SoundContainer. Clamped between -1 and 1.
/// </summary>
/// <param name="customPanValue">The new custom pan value.</param>
void SetCustomPanValue(float customPanValue) { m_CustomPanValue = std::clamp(customPanValue, -1.0f, 1.0f); if (IsBeingPlayed()) { g_AudioMan.ChangeSoundContainerPlayingChannelsCustomPanValue(this); } }

/// <summary>
/// Gets the panning strength multiplier of this SoundContainer.
/// </summary>
Expand Down Expand Up @@ -395,6 +407,7 @@ namespace RTE {

bool m_Immobile; //!< Whether this SoundContainer's sounds should be treated as immobile, i.e. not affected by 3D sound effects.
float m_AttenuationStartDistance; //!< The distance away from the AudioSystem listener to start attenuating this sound. Attenuation follows FMOD 3D Inverse roll-off model.
float m_CustomPanValue; //!< Custom stereo pan value using a Pan DSP on top of the basic spatialization.
float m_PanningStrengthMultiplier; //!< Multiplier for panning strength.
int m_Loops; //!< Number of loops (repeats) the SoundContainer's sounds should play when played. 0 means it plays once, -1 means it plays until stopped.
bool m_SoundPropertiesUpToDate = false; //!< Whether this SoundContainer's sounds' modes and properties are up to date. Used primarily to handle discrepancies that can occur when loading from ini if the line ordering isn't ideal.
Expand Down
1 change: 1 addition & 0 deletions Lua/LuaBindingsEntities.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1389,6 +1389,7 @@ namespace RTE {
.property("BusRouting", &SoundContainer::GetBusRouting, &SoundContainer::SetBusRouting)
.property("Immobile", &SoundContainer::IsImmobile, &SoundContainer::SetImmobile)
.property("AttenuationStartDistance", &SoundContainer::GetAttenuationStartDistance, &SoundContainer::SetAttenuationStartDistance)
.property("CustomPanValue", &SoundContainer::GetCustomPanValue, &SoundContainer::SetCustomPanValue)
.property("PanningStrengthMultiplier", &SoundContainer::GetPanningStrengthMultiplier, &SoundContainer::SetPanningStrengthMultiplier)
.property("Loops", &SoundContainer::GetLoopSetting, &SoundContainer::SetLoopSetting)
.property("Priority", &SoundContainer::GetPriority, &SoundContainer::SetPriority)
Expand Down
45 changes: 39 additions & 6 deletions Managers/AudioMan.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -602,6 +602,11 @@ namespace RTE {
result = (result == FMOD_OK) ? channel->setPriority(soundContainer->GetPriority()) : result;
float pitchVariationMultiplier = pitchVariationFactor == 1.0F ? 1.0F : RandomNum(1.0F / pitchVariationFactor, 1.0F * pitchVariationFactor);
result = (result == FMOD_OK) ? channel->setPitch(soundContainer->GetPitch() * pitchVariationMultiplier) : result;

if (soundContainer->GetCustomPanValue() != 0.0f) {
result = (result == FMOD_OK) ? channel->setPan(soundContainer->GetCustomPanValue()) : result;
}

if (soundContainer->IsImmobile()) {
result = (result == FMOD_OK) ? channel->setVolume(soundContainer->GetVolume()) : result;
} else {
Expand Down Expand Up @@ -728,6 +733,28 @@ namespace RTE {
return result == FMOD_OK;
}

/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

bool AudioMan::ChangeSoundContainerPlayingChannelsCustomPanValue(const SoundContainer *soundContainer) {
if (!m_AudioEnabled || !soundContainer || !soundContainer->IsBeingPlayed()) {
return false;
}
if (m_IsInMultiplayerMode) { RegisterSoundEvent(-1, SOUND_SET_PITCH, soundContainer); }

FMOD_RESULT result = FMOD_OK;
FMOD::Channel *soundChannel;

const std::unordered_set<int> *playingChannels = soundContainer->GetPlayingChannels();
for (int channelIndex : *playingChannels) {
result = m_AudioSystem->getChannel(channelIndex, &soundChannel);
result = result == FMOD_OK ? soundChannel->setPan(soundContainer->GetCustomPanValue()) : result;
if (result != FMOD_OK) {
g_ConsoleMan.PrintString("ERROR: Could not update sound custom pan value for the sound being played on channel " + std::to_string(channelIndex) + " for SoundContainer " + soundContainer->GetPresetName() + ": " + std::string(FMOD_ErrorString(result)));
}
}
return result == FMOD_OK;
}

/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

bool AudioMan::StopSoundContainerPlayingChannels(SoundContainer *soundContainer, int player) {
Expand Down Expand Up @@ -807,7 +834,7 @@ namespace RTE {
void *userData;
result = result == FMOD_OK ? soundChannel->getUserData(&userData) : result;
const SoundContainer *soundContainer = static_cast<SoundContainer *>(userData);
if (sqrDistanceToPlayer < (m_MinimumDistanceForPanning * m_MinimumDistanceForPanning)) {
if (sqrDistanceToPlayer < (m_MinimumDistanceForPanning * m_MinimumDistanceForPanning) || soundContainer->GetCustomPanValue() != 0.0f) {
soundChannel->set3DLevel(0);
} else if (sqrDistanceToPlayer < (doubleMinimumDistanceForPanning * doubleMinimumDistanceForPanning)) {
soundChannel->set3DLevel(LERP(0, 1, 0, m_SoundPanningEffectStrength * soundContainer->GetPanningStrengthMultiplier(), channel3dLevel));
Expand All @@ -828,6 +855,11 @@ namespace RTE {

FMOD_RESULT AudioMan::UpdatePositionalEffectsForSoundChannel(FMOD::Channel *soundChannel, const FMOD_VECTOR *positionOverride) const {
FMOD_RESULT result = FMOD_OK;

void *userData;
result = result == FMOD_OK ? soundChannel->getUserData(&userData) : result;
const SoundContainer *channelSoundContainer = static_cast<SoundContainer *>(userData);

bool sceneWraps = g_SceneMan.SceneWrapsX();

FMOD_VECTOR channelPosition;
Expand Down Expand Up @@ -876,7 +908,7 @@ namespace RTE {
result = result == FMOD_OK ? soundChannel->get3DMinMaxDistance(&attenuationStartDistance, &soundMaxDistance) : result;

float attenuatedVolume = (shortestDistance <= attenuationStartDistance) ? 1.0F : attenuationStartDistance / shortestDistance;

// Lowpass as distance increases
FMOD::DSP *dsp_multibandeq;
result = (result == FMOD_OK) ? soundChannel->getDSP(0, &dsp_multibandeq) : result;
Expand All @@ -885,6 +917,10 @@ namespace RTE {
lowpassFrequency = std::clamp(lowpassFrequency, 350.0f, 22000.0f);
result = (result == FMOD_OK) ? dsp_multibandeq->setParameterFloat(1, lowpassFrequency) : result;

if (channelSoundContainer->GetCustomPanValue() != 0.0f) {
result = (result == FMOD_OK) ? soundChannel->setPan(channelSoundContainer->GetCustomPanValue()) : result;
}

float minimumAudibleDistance = m_SoundChannelMinimumAudibleDistances.at(soundChannelIndex);
if (shortestDistance >= soundMaxDistance) {
attenuatedVolume = 0.0F;
Expand All @@ -893,13 +929,10 @@ namespace RTE {
} else if (sqrLongestDistance < (minimumAudibleDistance * minimumAudibleDistance)) {
attenuatedVolume = 0.0F;
}

void *userData;
result = result == FMOD_OK ? soundChannel->getUserData(&userData) : result;

float panLevel;
result = result == FMOD_OK ? soundChannel->get3DLevel(&panLevel) : result;
if (result == FMOD_OK && (panLevel < 1.0F || attenuatedVolume == 0.0F)) {
const SoundContainer *channelSoundContainer = static_cast<SoundContainer *>(userData);
result = soundChannel->setVolume(attenuatedVolume * channelSoundContainer->GetVolume());
}

Expand Down
11 changes: 11 additions & 0 deletions Managers/AudioMan.h
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,8 @@ namespace RTE {
SOUND_STOP,
SOUND_SET_POSITION,
SOUND_SET_VOLUME,
SOUND_SET_CUSTOMPANVALUE,
SOUND_SET_PANNINGSTRENGTHMULTIPLIER,
SOUND_SET_PITCH,
SOUND_FADE_OUT
};
Expand All @@ -77,6 +79,8 @@ namespace RTE {
int Channel;
bool Immobile;
float AttenuationStartDistance;
float CustomPanValue;
float PanningStrengthMultiplier;
int Loops;
int Priority;
bool AffectedByGlobalPitch;
Expand Down Expand Up @@ -511,6 +515,13 @@ namespace RTE {
/// <returns>Whether the pitch was successfully updated.</returns>
bool ChangeSoundContainerPlayingChannelsPitch(const SoundContainer *soundContainer);

/// <summary>
/// Updates the custom pan value of a SoundContainer's playing sounds.
/// </summary>
/// <param name="soundContainer">A pointer to a SoundContainer object. Ownership IS NOT transferred!</param>
/// <returns>Whether the custom pan value was successfully updated.</returns>
bool ChangeSoundContainerPlayingChannelsCustomPanValue(const SoundContainer *soundContainer);

/// <summary>
/// Stops playing a SoundContainer's playing sounds for a certain player.
/// </summary>
Expand Down
8 changes: 8 additions & 0 deletions Managers/NetworkClient.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -656,6 +656,8 @@ namespace RTE {
soundContainerToHandle->GetTopLevelSoundSet().AddSound(filePathFromHash, false);
soundContainerToHandle->SetImmobile(soundDataPointer->Immobile);
soundContainerToHandle->SetAttenuationStartDistance(soundDataPointer->AttenuationStartDistance);
soundContainerToHandle->SetCustomPanValue(soundDataPointer->CustomPanValue);
soundContainerToHandle->SetPanningStrengthMultiplier(soundDataPointer->PanningStrengthMultiplier);
soundContainerToHandle->SetLoopSetting(soundDataPointer->Loops);
soundContainerToHandle->SetPriority(soundDataPointer->Priority);
soundContainerToHandle->SetAffectedByGlobalPitch(soundDataPointer->AffectedByGlobalPitch);
Expand All @@ -676,6 +678,12 @@ namespace RTE {
case AudioMan::SOUND_SET_VOLUME:
soundContainerToHandle->SetVolume(soundDataPointer->Volume);
break;
case AudioMan::SOUND_SET_CUSTOMPANVALUE:
soundContainerToHandle->SetCustomPanValue(soundDataPointer->CustomPanValue);
break;
case AudioMan::SOUND_SET_PANNINGSTRENGTHMULTIPLIER:
soundContainerToHandle->SetPanningStrengthMultiplier(soundDataPointer->PanningStrengthMultiplier);
break;
case AudioMan::SOUND_SET_PITCH:
soundContainerToHandle->SetPitch(soundDataPointer->Pitch);
break;
Expand Down

0 comments on commit 3367d0a

Please sign in to comment.