THE AUDIO MANAGER

preview


VIDEO


When the pipeline is completed this is the kind of result we get shown in the video!

( Use headphones for best experience )

Table of content

We will be going thorugh how I go from concept and design audio, how i set up the FMOD project.

We will go though how the AudioManager is set up in header file and cpp file.

After that we wil look at some functions the we will use outside in the gamecode.

And ofcourse we will look at some examples on how we utilize the AudioManager and its functions.

Lastly i will give some thought on what i feel i want to improve and why some things are set up like it is right now.
But I will also give some insight in some problems i had and how i solved them.


You can navigate the chapters using the dropdown in the top right!

OBS!
Keep in mind that TGA ( the school i am currently student at ) does not teach anything about audio programming, nor does anyone at our school in stockholm know how any audio API operates. so with this I am self tought.
And considering that Audio is nothing we get grades on and other things do, other areas takes priority. I can only iterate and improve the AudioManager when time approves.

( I have become the "Audio Caveman" all other programmers go to for help with FMOD )

Pre-Prod

Fisrt I get the concept of what will have an audio sample. I get a decription of the feel we want to achieve, in this example I use the Hand Cannons, they ought to sound like handhelt space canons with alot of punch.
With the concept art I can more easily get the idea of the power level that is reasonable.

I start right away to mix the audio sample i found on Soundsnap.com. the audio sample is an M1 Grand, but it is no "Cannon" feel in that sample so i have to adjust the Low frequency, fall off and implement the sci-fi elemt, I created a lazer effect with the sample and combined that with the other mixed sample that had the "bass punch"

Setup in header file

		enum class eDescriptions
		{
			eLocalAmbience = 1,
			ePlayer = 2,
			eEnemyOne = 3,
			eOST = 4,
			eUI = 5,
			eGlobalAmbience = 6,
			eProjectiles = 7
		};

		enum class eEventParameterName
		{
			eLocalAmbience_WorldObject,
			ePlayer_PlayerActions,
			ePlayer_PlayerCore,
			ePlayer_PlayerAffect,
			ePlayer_PlayerKey,
			eEnemyOne_EnemyActions,
			eEnemyOne_EnemyCore,
			eEnemyOne_EnemyTalking,
			eOST_MusicTrack,
			eOST_PauseGame,
			eOST_SwitchTrack,
			eOST_TempMusic,
			eUI_UIActions,
			eGlobalAmbience_World,
			eProjectiles_PlayerProjectiles,
			eProjectiles_EnemyProjectiles
		}

After the first iteration of the Mixed sample or in an earlier test with unmixed audio sample I set up the FMOD project with all the Events and parameters that I in hindsight believe we will have in the game, this so i can iterate later and remove excess or add small add ons if needed.

I start in the header to add enums.

eDescription will be used to bind Events wityh the GUID later, And eEventParameterName will be used to easier get the FMOD parameter strings.

const char* myLocalAmbience_WorldObject;
const char* myPlayer_PlayerActions;
const char* myPlayer_PlayerCore;
const char* myPlayer_PlayerAffect;
const char* myPlayer_PlayerKey;
const char* myEnemyOne_EnemyActions;
const char* myEnemyOne_EnemyCore;
const char* myEnemyOne_EnemyTalking;
const char* myOST_MusicTrack;
const char* myOST_PauseGame;
const char* myOST_SwitchTrack;
const char* myOST_TempMusic;
const char* myUI_UIActions;
const char* myGlobalAmbience_World;
const char* myProjectiles_PlayerProjectiles;
const char* myProjectiles_EnemyProjectiles

For the eParameterName I add const char* member variables to write manually what the parameternames are named inside the FMOD project

FMOD::Studio::Bank* myMaster = nullptr;
FMOD::Studio::Bank* myStrings = nullptr;
FMOD::Studio::Bank* myLocalAmbience = nullptr;
FMOD::Studio::Bank* myPlayer = nullptr;
FMOD::Studio::Bank* myEnemyOne = nullptr;
FMOD::Studio::Bank* myMusic = nullptr;
FMOD::Studio::Bank* myUI = nullptr;
FMOD::Studio::Bank* myGlobalAmbience = nullptr;
FMOD::Studio::Bank* myProjectiles = nullptr

I set up Bank* buffers and assign them to nullptr, however I do not use them more other than as "buffers" in the Event bindings

AudioContext myAudioContext;
FMOD_3D_ATTRIBUTES myPlayerTransform;
std::map <int, FMOD_3D_ATTRIBUTES*  > myTransforms;
std::map<eVolumeMix, FMOD::Studio::Bus*> myVolumeMixers;
std::map<int, FMOD::Studio::EventInstance*> myEvents;
std::map<int, FMOD::Studio::EventInstance*> myGlobalEvents;
std::map<eDescriptions, FMOD::Studio::EventDescription* > myEventDescription;
std::map<eEventParameterName, const char*> myEventParameter

Here is the core essentials for this manager setup:

The AudioContext is a struct to contain the FMOD syetem API.

The Player is seperated from all other Game objects with its own 3D_ATTRIBUTES mainy for failsafe and to easier access that without searching for it later in containers.

myVolumeMixers is the first map that will hold all volume mixing, i bind the corresponding Buses with correct enum value.

myEvents is the bulk map that will hold in similar fashion created instaces paired with a gameobjects unique ID as a key.

myGlobal events is a copy in logic but seperated to be able to manage audio seperate from the game worlds 3D space.

myEventDescription is another map that will hold One event at dessignated eDescription enum Key with corresponding name.

myeventParameterName will be for accessing the Parameter string.

	struct AudioContext
	{
		FMOD::Studio::System* myFmodSystem;
	}

This is the struct tht holds the FMOD systems API.

Initialization in cpp file

		//Init FMOD Parameter name
		myLocalAmbience_WorldObject = { "WorldObject" };
		myPlayer_PlayerActions = { "PlayerAction" };
		myPlayer_PlayerCore = { "Player Core" };
		myPlayer_PlayerAffect = { "Player Reaction" };
		myPlayer_PlayerKey = { "Key" };

		myEnemyOne_EnemyActions = { "EnemyActions" };
		myEnemyOne_EnemyCore = { "EnemyCore" };
		myEnemyOne_EnemyTalking = { "EnemyTalking" };

		myOST_MusicTrack = { "MusicTrack" };
		myOST_PauseGame = { "PauseGame" };
		myOST_SwitchTrack = { "SwitchTrack" };
		myOST_TempMusic = { "TempMusic" };

		myUI_UIActions = { "UIActions" };

		myGlobalAmbience_World = { "World" };
		myProjectiles_PlayerProjectiles = { "PlayerProjectiles" };
		myProjectiles_EnemyProjectiles = { "EnemyProjectiles" }

In the Initialization function called Init all that follows in this chapter runs once there in startup.

I assign the strings for the parameternames first due to the manual implementation and to find it stright away for ease of use.

		// Init volume mixer Channel keys
		myChannels.insert({ eVolumeMixer::eMaster,/*--*/ nullptr });
		myVolumeMixers.insert({ eVolumeMixer::eSFX,/*-----*/ nullptr });
		myVolumeMixers.insert({ eVolumeMixer::eAmbience,/**/ nullptr });
		myVolumeMixers.insert({ eVolumeMixer::eOST,/*-----*/ nullptr });

		// Init EventDescription keys
		myEventDescription.insert({ eDescriptions::eLocalAmbience, nullptr });
		myEventDescription.insert({ eDescriptions::ePlayer, nullptr });
		myEventDescription.insert({ eDescriptions::eEnemyOne, nullptr });
		myEventDescription.insert({ eDescriptions::eOST, nullptr });
		myEventDescription.insert({ eDescriptions::eUI, nullptr });
		myEventDescription.insert({ eDescriptions::eGlobalAmbience, nullptr });
		myEventDescription.insert({ eDescriptions::eProjectiles, nullptr })

Next i Prepare the mixers and decription maps so there is no issues later with memory issues I ahve it set up like this for readability and to make it easy to implement new mixing channels and descriptions.

All enums are allocated to their part in the map with nullptr as a buffer to be replaced later

		// Init System.
		result = FMOD::Studio::System::create(&myAudioContext.myFmodSystem);
		result = myAudioContext.myFmodSystem->initialize(512, FMOD_STUDIO_INIT_NORMAL, FMOD_INIT_NORMAL, nullptr)

And before I start allocate anything I need to create and Initialize the FMOD API

		// Init Banks.
		result = myAudioContext.myFmodSystem->loadBankFile("Assets/Fmod/Banks/Master.bank", FMOD_STUDIO_LOAD_BANK_NORMAL, &myMaster);
		result = myAudioContext.myFmodSystem->loadBankFile("Assets/Fmod/Banks/Master.strings.bank", FMOD_STUDIO_LOAD_BANK_NORMAL, &myStrings);
		result = myAudioContext.myFmodSystem->loadBankFile("Assets/Fmod/Banks/OST.bank", FMOD_STUDIO_LOAD_BANK_NORMAL, &myMusic);
		result = myAudioContext.myFmodSystem->loadBankFile("Assets/Fmod/Banks/LocalAmbience.bank", FMOD_STUDIO_LOAD_BANK_NORMAL, &myLocalAmbience);
		result = myAudioContext.myFmodSystem->loadBankFile("Assets/Fmod/Banks/Player.bank", FMOD_STUDIO_LOAD_BANK_NORMAL, &myPlayer);
		result = myAudioContext.myFmodSystem->loadBankFile("Assets/Fmod/Banks/EnemyOne.bank", FMOD_STUDIO_LOAD_BANK_NORMAL, &myEnemyOne);
		result = myAudioContext.myFmodSystem->loadBankFile("Assets/Fmod/Banks/UI.bank", FMOD_STUDIO_LOAD_BANK_NORMAL, &myUI);
		result = myAudioContext.myFmodSystem->loadBankFile("Assets/Fmod/Banks/GlobalAmbience.bank", FMOD_STUDIO_LOAD_BANK_NORMAL, &myGlobalAmbience);
		result = myAudioContext.myFmodSystem->loadBankFile("Assets/Fmod/Banks/Projectile.bank", FMOD_STUDIO_LOAD_BANK_NORMAL, &myProjectiles)

I get the generated .bank files from the folder I build them to from the FMOD project, and here i also use thos Bank* "buffers" they will not be used other than here, the audio will not het loaded properly otherwise

	// Init Bus For Mastering Volume Channesls.
	result = myAudioContext.myFmodSystem->getBusByID(&FSPRO::Bus::Master_Bus, &myChannels.at(eChannels::eMaster));
	result = myAudioContext.myFmodSystem->getBusByID(&FSPRO::Bus::Music, &myChannels.at(eChannels::eOST));
	result = myAudioContext.myFmodSystem->getBusByID(&FSPRO::Bus::SFX, &myChannels.at(eChannels::eSFX));
	result = myAudioContext.myFmodSystem->getBusByID(&FSPRO::Bus::Ambience, &myChannels.at(eChannels::eAmbience))

And so in order next up is the Volume mixers, at each key i allocate corresponding Bus

		// Init Base Volume at startup
		result = myChannels.at(eChannels::eMaster)->setVolume(1.0f);
		result = myChannels.at(eChannels::eAmbience)->setVolume(1.0f);
		result = myChannels.at(eChannels::eOST)->setVolume(1.0f);
		result = myChannels.at(eChannels::eSFX)->setVolume(1.0f)

And per default I assign each bus to be set to 1.0f wich is the max in this case.

I do volume mixing in the FMOD project so that no audio sample peaks above any other, but the player lower the buses as he/she pleases later in game.

		// Assign Event ID To myEventDescriptionMap
		result = myAudioContext.myFmodSystem->getEventByID(&FSPRO::Event::Core_Events_Player, &myEventDescription.at(eDescriptions::ePlayer));
		result = myAudioContext.myFmodSystem->getEventByID(&FSPRO::Event::Core_Events_LocalAmbience, &myEventDescription.at(eDescriptions::eLocalAmbience));
		result = myAudioContext.myFmodSystem->getEventByID(&FSPRO::Event::Core_Events_EnemyOne, &myEventDescription.at(eDescriptions::eEnemyOne));
		result = myAudioContext.myFmodSystem->getEventByID(&FSPRO::Event::Core_Events_OST, &myEventDescription.at(eDescriptions::eOST));
		result = myAudioContext.myFmodSystem->getEventByID(&FSPRO::Event::Core_Events_UI, &myEventDescription.at(eDescriptions::eUI));
		result = myAudioContext.myFmodSystem->getEventByID(&FSPRO::Event::Core_Events_GlobalAmbience, &myEventDescription.at(eDescriptions::eGlobalAmbience));
		result = myAudioContext.myFmodSystem->getEventByID(&FSPRO::Event::Core_Events_Projectiles, &myEventDescription.at(eDescriptions::eProjectiles))

And next is the perhaps one of the major important maps; the myEventDecription. This map pair the relevant Event to corresponding enum value, and this will be important later when creating instances, for if the gameobject can't know where it Event are in the map, the game object will not be able to find it. In my wrapper i have made it so that the game object basicly tell the wrapper who the object is via it's unique ID and after that It can request how it want to sound like via the description enum value.
we will look at how this works further down!

		//Init FMOD Parameter description
		myEventParameter.insert({ eEventParameterName::eLocalAmbience_WorldObject ,myLocalAmbience_WorldObject });
		myEventParameter.insert({ eEventParameterName::ePlayer_PlayerActions ,myPlayer_PlayerActions });
		myEventParameter.insert({ eEventParameterName::ePlayer_PlayerCore ,myPlayer_PlayerCore });
		myEventParameter.insert({ eEventParameterName::ePlayer_PlayerAffect ,myPlayer_PlayerAffect });
		myEventParameter.insert({ eEventParameterName::ePlayer_PlayerKey ,myPlayer_PlayerKey });

		myEventParameter.insert({ eEventParameterName::eEnemyOne_EnemyActions , myEnemyOne_EnemyActions });
		myEventParameter.insert({ eEventParameterName::eEnemyOne_EnemyCore ,myEnemyOne_EnemyCore });
		myEventParameter.insert({ eEventParameterName::eEnemyOne_EnemyTalking ,myEnemyOne_EnemyTalking });

		myEventParameter.insert({ eEventParameterName::eOST_MusicTrack ,myOST_MusicTrack });
		myEventParameter.insert({ eEventParameterName::eOST_PauseGame ,myOST_PauseGame });
		myEventParameter.insert({ eEventParameterName::eOST_SwitchTrack ,myOST_SwitchTrack });
		myEventParameter.insert({ eEventParameterName::eOST_TempMusic ,myOST_TempMusic });

		myEventParameter.insert({ eEventParameterName::eUI_UIActions ,myUI_UIActions });
		myEventParameter.insert({ eEventParameterName::eGlobalAmbience_World ,myGlobalAmbience_World });
		myEventParameter.insert({ eEventParameterName::eProjectiles_PlayerProjectiles ,myProjectiles_PlayerProjectiles });
		myEventParameter.insert({ eEventParameterName::eProjectiles_EnemyProjectiles ,myProjectiles_EnemyProjectiles })

The last map will be so bind the Events parameter name with an enum value and the loaded const char*.
It may be alot of text for this, But it's all for readability and definition.

		// Set default Listener transform
		myPlayerTransform.up = { 0,1,0 };
		myPlayerTransform.forward = { 0,0,1 };
		myPlayerTransform.position = { 0,0,0 };
		myPlayerTransform.velocity = { 0,0,0 }

Lastly we set the myPlayerTransform to a default value.

This is the end of Init and all of this only runs once when the game start, there is never any need to reload anything in this Init function. everything is neatly packed on a well structured shelf for the user of this wrapper to use.


Next we look on some of the functions we utilize

Some Functions

	void AudioManager::InitPlayer(eDescriptions aDescribtion, const int anID, FOC::Vector3f aTransform)
	{
		InitNewInstance3D(aDescribtion, anID, aTransform);
		PlayEvent(anID);
	}
	void AudioManager::InitNewInstance3D(eDescriptions aDescribtion, const int anID, Vector3f aTransform)
	{
				FMOD::Studio::EventInstance* newEvent;
		if (aDescribtion != eDescriptions::ePlayer)
		{
			FMOD_3D_ATTRIBUTES* newTransform = new FMOD_3D_ATTRIBUTES{};
			newTransform->up = { 0,1,0 };
			newTransform->forward = { 0,0,1 };
			newTransform->position = { aTransform.x, aTransform.y, aTransform.z };
			newTransform->velocity = { 0,0,0 };
			myTransforms.insert({ anID, newTransform });
		}
		myEvents.insert({ anID, newEvent });
		myEventDescription.at(aDescribtion)->createInstance(&myEvents.at(anID));
	}

For ease of use of the AudioManager I created an InitPlayer to not have any confusions and to make it modular.
The player still use MyEvents Map like all other game object.

I'll talk in the end about why PlayEvent is here.

(1)
This is perhaps the most important Function to explain;


the values we pass is WHAT, WHO and WHERE?

WHAT:
Is what are we? how do we want to sound like? in our example we want to sound like:
eDescription:: ePlayer,

WHO:
Is who we are, the player as a game object have an unique ID, perhaps 233 and that will be our key in myEvents.

WHERE:
Is were do we initially start with our audio, one can use any position but usually we just use our game objects position.


(2)
When we know what is requested we create a new instance and unless it's the player we create a new 3D_ATTRIBUTES aswell, and allocate it in a transform map for update later later.

After we create a new EventInstance and allocted positions we insert the newEvent into the myEvents map, however the new instnce have yet no clue what Event it want to use, So we use the myEventDecribtion at our requested enum key and thus we find the correct Event, with our describtion we create the instance with out newly empy EventInstance at our game objects key value. So now when we call the myEvents. at(anID) we can utilize all the functions of the specific Event!


	void AudioManager::InitGlobalAudio(eDescriptions aDescribtion, int anID)
	{
		FMOD::Studio::EventInstance* newEvent;
		myGlobalEvents.insert({ anID, newEvent });
		myEventDescription.at(aDescribtion)->createInstance(&myGlobalEvents.at(anID));
	}

The global events use the same logic as the previous function but without any position.
However considering that we don't have any ID system for things like music we are currently as for now to assign that hard coded. ill talk about this in the end.

	void AudioManager::UpdatePosition(int anID, Vector3f aTransform)
	{
		if (myEvents.size() != 0)
		{
			myTransforms.at(anID)->position = { aTransform.x, aTransform.y, aTransform.z };
			myEvents.at(anID)->set3DAttributes(myTransforms.at(anID));
		}
	}
	void AudioManager::UpdatePlayerPosition(Vector3f& aTransform, int anID, const Vector3f& aRotation, const Vector3f& anUp)
	{
		if (myEvents.size() != 0)
		{
			myPlayerTransform.position = { aTransform.x, aTransform.y, aTransform.z };
			myPlayerTransform.forward = { aRotation.x, aRotation.y, aRotation.z };
			myPlayerTransform.up = { anUp.x, anUp.y, anUp.z };
			myEvents.at(anID)->set3DAttributes({ &myPlayerTransform });
		}
	}

Here are two functions that use similar logic, we just update the position, but considering the player needs more information bacause of the camera matrix i split the player out from the UpdatePosition, mainly because doing that we can skip going thou extra checks to see if it is the player or not and in the game code it is easier to find the player and everything else is default to be all other game objects.

	void AudioManager::SetChannelVolume(const eChannels aChannelID, float aVolume)
	{
		auto& bus = myChannels.at(aChannelID);
		bus->setMute(false);
		bus->setVolume(aVolume);
	}
	const float AudioManager::GetChannelVolume(const eChannels aChannelID) const
	{
		auto& bus = myChannels.at(aChannelID);
		float volume;
		bus->getVolume(&volume);
		const float returnfloat = volume;
		return returnfloat;
	}

Pretty stright forward:
getter and setter for the volume.
We ask for what volume mixer channel we want to use and update the volume value

	void AudioManager::EditParameter(int anID, const char* aParameterName, float aParameterValue)
	{

		if (myEvents.size() != 0 && myEvents.count(anID) > 0)
		{
			myEvents.at(anID)->setParameterByName(aParameterName, aParameterValue);

		}
	}
	void AudioManager::EditGlobalParameter(int anID, const char* aParameterName, float aParameterValue)
	{
		if (myGlobalEvents.size() != 0 && myGlobalEvents.count(anID) > 0)
		{
			myGlobalEvents.at(anID)->setParameterByName(aParameterName, aParameterValue);
		}
	}

Therse two work the same but likewise the player I have split these two apart but the reason is that they do not use the same map.

The setParameterByname only approves some kind of string and float.

But we show our ID key and then we use the function GetParameterName and use that to pass the requested string, but One can by design choose to type that manually if one wishes.

Then we pass a value of that parametername.

we can easily se the power of the structure here in how the myEvents find correct Event just by an ID. the user of this manager dont need to think about finding correct Event via this design choise.

we also for failsafe assure that the map have this ID and is not empty.

	void AudioManager::PlayEvent(int anID)
	{
		if (myEvents.size() != 0 && myEvents.count(anID) > 0)
		{
			myEvents.at(anID)->start();
		}
	}
	void AudioManager::PlayGlobalEvent(int anID)
	{
		myGlobalEvents.at(anID)->start();
	}

Here we can Easily play the game objects Event using the map myEvents.

we still do failsafe checks.

	const char* AudioManager::GetParameterName(eEventParameterName anEventParameterName)
	{
		const char* temp{};
		temp = myEventParameter.at(anEventParameterName);
		return temp;
	}

This function is to return the const char* we have allocated at corresponding enum value for the editParameter function.

	void AudioManager::Update()
	{
		myAudioContext.myFmodSystem->setListenerAttributes(0, &myPlayerTransform);
		myAudioContext.myFmodSystem->update();
	}

And lastly we update the listeners position and finally we update the whole FMOD system always last.

and the game loop continue

Next we will look on how we use this manager in the game code

How it is used

Engine::GetInstance()->GetAudioManager()->InitPlayer(FOC::AudioManager::eDescriptions::ePlayer, myGameObject->GetID(), myGameObject->myTransform.myPosition)

After we have run Init in the Game engine at start up, we can access the AudioManager from the game engine Instance

We GetAudioManager and access InitPlayer.
we fetch WHAT, WHO, WHERE.

DONE! all is allocated and we know where

auto audio = Engine::GetInstance()->GetAudioManager()
		if (myDirection != Vector3f::Zero && !myMovementComponent->GetLockedMovement())
		{
			audio->EditParameter(myGameObject->GetID(), audio->GetParameterName(FOC::AudioManager::eEventParameterName::ePlayer_PlayerCore), 1);
		}
		else
		{
			audio->EditParameter(myGameObject->GetID(), audio->GetParameterName(FOC::AudioManager::eEventParameterName::ePlayer_PlayerCore), 0);
		}

for less syntax, we do an auto and call it audio

we want to edit some parameters to the Event; we want to play walk and pause it
I call all basic player behaviour for "core" and in all paramets i use 0 as "mute".
We just change the parameter to 1 and we now have a walking audio for the player when the player is walking

switch (anEvent)
{
case FOC::eInputEvent::eForward:
	matrix.SetPosition({ 0.f, 0.f, 1.f });
	isWalking = true;
	break;
case FOC::eInputEvent::eBack:
	matrix.SetPosition({ 0.f, 0.f, -1.f });
	isWalking = true;
	break;
case FOC::eInputEvent::eRight:
	matrix.SetPosition({ 1.f, 0.f, 0.f });
	isWalking = true;
	break;
case FOC::eInputEvent::eLeft:
	matrix.SetPosition({ -1.f, 0.f, 0.f });
	isWalking = true;
	break;
case FOC::eInputEvent::eFire:
	if (myIsReloading || myIsShooting || myIsUpgrading)
		return;

	if (anAction == eInputAction::ePressed)
	{
		myWeaponCode = weaponC->Fire();
		if (myWeaponCode.type == (int)WeaponReturnCode::Reloading)
		{
			myIsReloading = true;
			myAnimPlayer->SetSpeedMultiplier(myReloadSpeed);
			if (myWeaponComponent->GetLeftSlot().isEnabled)
			{
				myAnimPlayer->PlayAnimation("TwoReload");
			}
			else
			{
				myAnimPlayer->PlayAnimation("OneReload");
			}
			audio->EditParameter(myGameObject->GetID(), audio->GetParameterName(FOC::AudioManager::eEventParameterName::ePlayer_PlayerActions), 3);
			audio->EditParameter(myGameObject->GetID(), audio->GetParameterName(FOC::AudioManager::eEventParameterName::ePlayer_PlayerActions), 2);
			audio->EditParameter(myGameObject->GetID(), audio->GetParameterName(FOC::AudioManager::eEventParameterName::ePlayer_PlayerKey), 0);
		}
		else
		{
			audio->EditParameter(myGameObject->GetID(), audio->GetParameterName(FOC::AudioManager::eEventParameterName::ePlayer_PlayerKey), 1);
			audio->EditParameter(myGameObject->GetID(), audio->GetParameterName(FOC::AudioManager::eEventParameterName::ePlayer_PlayerActions), 1);
			audio->EditParameter(myGameObject->GetID(), audio->GetParameterName(FOC::AudioManager::eEventParameterName::ePlayer_PlayerActions), 0);
			audio->EditParameter(myGameObject->GetID(), audio->GetParameterName(FOC::AudioManager::eEventParameterName::ePlayer_PlayerKey), 2);
			audio->EditParameter(myGameObject->GetID(), audio->GetParameterName(FOC::AudioManager::eEventParameterName::ePlayer_PlayerKey), 0);
			myIsShooting = true;
			if (myWeaponComponent->GetLeftSlot().isEnabled)
			{
				if (myWeaponComponent->GetCurrentAmmo() % 2 == 0)
				{
					myAnimPlayer->PlayAnimation("TwoShootRight");
	            }
				else
				{
					myAnimPlayer->PlayAnimation("TwoShootLeft");
				}
			}
			else
			{
				myAnimPlayer->PlayAnimation("OneShoot");
			}
		}
	}
	break;
case FOC::eInputEvent::eReload:
	if (myIsShooting || myIsReloading || myIsUpgrading)
		return;

	myIsReloading = true;
	if (myWeaponComponent->GetLeftSlot().isEnabled)
	{
		myAnimPlayer->PlayAnimation("TwoReload");
	}
	else
	{
		myAnimPlayer->PlayAnimation("OneReload");
	}
	audio->EditParameter(myGameObject->GetID(), audio->GetParameterName(FOC::AudioManager::eEventParameterName::ePlayer_PlayerActions), 2);
audio->GetParameterName(FOC::AudioManager::eEventParameterName::ePlayer_PlayerActions), 0);
	weaponC->Reload();
	break;
case FOC::eInputEvent::eJump:
	if (!myIsUpgrading)
	{
		if (myWeaponComponent->GetLeftSlot().isEnabled)
		{
			myAnimPlayer->PlayAnimation("TwoJump");
		}
		else
		{
			myAnimPlayer->PlayAnimation("OneJump");
		}
	}
	audio->EditParameter(myGameObject->GetID(), audio->GetParameterName(FOC::AudioManager::eEventParameterName::ePlayer_PlayerCore), 3);
	audio->EditParameter(myGameObject->GetID(), audio->GetParameterName(FOC::AudioManager::eEventParameterName::ePlayer_PlayerCore), 0);
	moveC->Jump();
	break;
default:
	break;
}

Some more exaples of how we change the Event;

Here we look at how i manipulate the Event to play different audio for the weapon.
What the player do is Actions.
But i added "key" wich is a special case parameter SFX for the weapon trigger

Mail: kontakt@adamdahlin.se