How to customize audio using the Sound Provider component and scripting

Hello everyone ! In this post I will breakdown the Sound Provider component and show you how you can customize the audio experience in your game.

Prerequisites

  • Have a little coding experience. (Would be great if it is lua)
  • Have already gone through this page and followed its instructions.
  • Knowledge on how interactable component, Events, EventHelper, CustomEvents work for the complex section.

Comparison with Audio component

Add the Audio component to any object.
This is how the audio component looks with an audio added to it.
image

Currently only the starting of the audio can be customized with the trigger portion of the component.
Here the audio chosen is Casual Game Music 20. It plays on start, that is, the audio starts playing as soon as you load the space.

Starting scripting with the Sound Provider component

We can do the same thing as the above component using a simple script. For scripting purposes we will have to remove the Audio component and instead add the Sound Provider component under the node category of components. Its the same symbol as the Audio component but in yellow.

image image

Here’s how the sound provider component looks after adding an audio.
image

After this you will need to add an script. For this click on the arrow beside the (+component) button and choose add script.
Give your script an appropriate name.

Note : your script name can contain only letters and numbers, so no symbols or spaces

I will name my script AudioManager.
Then click on your file name beside the file field to open it up in your preferred Editor.
image

The Sound provider class

This is simply for some technical reference, you totally do not need to understand the following codeblock.

---@class YaSoundProviderComponent : AbstractHybridYaComponent
YaSoundProviderComponent = Class()
--- Return current clip's length.
---@return number
function YaSoundProviderComponent:GetClipLength() end
--- Set Track length.
---@param length number
function YaSoundProviderComponent:SetClipLength(length) end
--- Set the "volume"  as the sound volume,which ranges in [0-1f].
---@param volume number @Sound Volume.
function YaSoundProviderComponent:SetVolume(volume) end
--- Get the "volume" ,which ranges in [0-1f].
---@return number
function YaSoundProviderComponent:GetVolume() end
--- ToDo Api,which allow "Sound Provider" can play different sound clip.
---@param musicID string
function YaSoundProviderComponent:ChangeSong(musicID) end
--- Return "Sound Provider" whether is playing clip or not.
---@return boolean
function YaSoundProviderComponent:IsPlaying() end
---@return number
function YaSoundProviderComponent:GetSoundId() end
--- Make "Sound Provider" play from last track point.
function YaSoundProviderComponent:Play() end
--- Make "Sound Provider" play from head of the track.
function YaSoundProviderComponent:Replay() end
--- Make "Sound Provider" pause from playing.
function YaSoundProviderComponent:Pause() end
--- Set Sound 3D
---@param isThreeDimension boolean
function YaSoundProviderComponent:SetThreeDimensionalSound(isThreeDimension) end
--- Set Max Min Distance
---@param minValue number
---@param maxValue number
function YaSoundProviderComponent:SetMinMaxDistance(minValue,maxValue) end
--- Set Loop
---@param Looping boolean
function YaSoundProviderComponent:SetSoundLoop(Looping) end
--- Get Loop
---@return boolean
function YaSoundProviderComponent:GetSoundLoop() end

Scripting

We need to work with the above class. That is we will edit attributes of the component using functions defined by it. So we will need to have something to refer to it. So we will store the component attached to our object into a variable.

local audio = script:GetYaComponent("YaSoundProviderComponent");

Breakdown

  • We store the object in the variable audio.
  • We get the component from the object to which this script is attached to. Hence, script:GetYaComponent.
  • We take the required component by mentioning it within quotes. "YaSoundProviderComponent".

Playing Audio

Now we have to play the audio as it won’t be played automatically. For that we use the Play() method.

audio:Play();

This will play the audio as soon as the game starts because that is when the script is executed.

So, the total script looks like this right now :

local audio = script:GetYaComponent("YaSoundProviderComponent");

audio:Play();

Now if you hit play and enter the game you can hear your audio playing.

But the audio is 2D !

Yes it is 2D audio by default. This means that the audio is not spatial and you don’t hear it coming from a particular direction, instead it plays like a background audio.

Don’t worry it can be easily changed

For this we will need to use the SetThreeDImensionalSound() method.
Add the following line to your script befor audio:Play();.

audio:SetThreeDimensionalSound(true);

This will change the audio to spatial which you can confirm by going into the play mode. You can change it back to 2D by setting the above value of the function to false.

Its too LOUD !

We can change its volume using the SetVolume() method.
To change volume you will need to add the following line

audio:SetVolume(value);

where value is the volume amount. Replace it with the amount you want.
KEEP IN MIND : The value of volume here is different than in Audio component. In Audio component you choose a value from 0-100. Here you have to use a value between 0-1. That is if you want 70% volume then keep the value as 0.7.

It plays just once !

The audio stops after playing once. You might want it to repeat multiple times.

Do this to loop the audio

Add the following line before audio:Play();

audio:SetSoundLoop(true);

Time for some little Complex stuff

In this section we will get into changing properties of the audio based on various conditions and from a different script.

Changing audio upon entering a particular position

Simply changing the audio on a trigger. Instead we will use Interactable component.
Interactable component is the same as trigger but the differences are :

  • It triggers only for players and not for any object
  • Its shapes are cylinder(default), sphere, capsule. No box.

We will add a cylinder to our scene and turn of its mesh collider.


Do not add a trigger, its not needed.
Then add the Interactable component to it.
image
image
You can change the values here if you want to but its fine to keep them in default. The values for Radius and Height say 0 but they are actually 1. So, it perfectly aligns with the cylinder mesh and helps with visual reference.
No add a script to the object and name it AudioTrigger. We will trigger various events with this script.
Each subtopic in this complex section will assume this script is empty at the start of that topic.

Write the following in the script.

local intcmp = script:GetYaComponent("YaInteractableComponent");

local function ChangeAudio()
    CustomEvents:Emit("ChangeAudio");
end

EventHelper.AddListener(intcmp, "PlayerContactEvent", ChangeAudio);

Breakdown

  • intcmp is the variable for the interactable component.
  • ChangeAudio is a function that helps us changing the audio.
  • Last line is where we listen for events from intcmp, we listen for the event PlayerContactEvent which is emmited when the player enters the interactable component range(here a cylinder with height and radius of 1), then we call the function ChangeAudio if the event is heard.

Now the function ChangeAudio :

  • It will emit the event “ChangeAudio”. As it is a custom event, it does not need to refer to any object.
    Fun fact : this line can also be written as EventHelper.Emit(CustomEvents, "ChangeAudio");

Now how to handle this event and make changes in the AudioMangaer script

Before we edit the script, we need to make note of the audio we want to change to. Remember the name of the audio file as it appears in Yahaha along with the case.

We need to listen for the event from AudioTrigger script and when it is heard, we will change the audio using the ChangeSong() method.

Add the following block to your AudioManager script :

local function ChangeAudio()
    audio:Pause();
    audio:ChangeSong("Audio name");
    audio:Play();
end

CustomEvents:AddListener("ChangeAudio", ChangeAudio);

Replace “Audio name” with the name of the audio you took note of earlier. Make sure to type the name within quotes inside the parenthesis.

Breakdown

  • We pause the currently playing audio
  • Then change the audio
  • Then play the changed audio
  • Event listener calls the function when the it hears the “ChangeAudio” event.

You can probably notice now that the code is pretty self-explanatory.

Concept of how to make triggered functions

  • Create an interactable component.
  • Create a function to emit certain events for your purpose.
  • Call the function when the event PlayerContactEvent is emmited by the interactable component.
  • Listen for the Custom event you emmited in some other script.
  • Call the function that is supposed to do the stuff related to that event.

Play Audio on trigger

AudioTrigger Script :

local intcmp = script:GetYaComponent("YaInteractableComponent");

local function PlayAudio()
    CustomEvents:Emit("PlayAudio");
end

EventHelper.AddListener(intcmp, "PlayerContactEvent", PlayAudio);

AudioManager script :

local audio = script:GetYaComponent("YaSoundProviderComponent");

local function PlayAudio()
    audio:Play();
end

CustomEvents:AddListener("PlayAudio", PlayAudio);

I hope this is self-explanatory

Play audio for a set amount of time

Here we will have to work with a class called YaTime. It has no official documentation. Ekki found out how to make it work with the help of Kim.

---@class YaTimeComponent : AbstractSingletonYaComponent__YaTimeComponent__
YaTimeComponent = Class()
---@return number
function YaTimeComponent:CurrentTimeSinceStart() end
---@param seconds number
---@return TimeEventContainer
function YaTimeComponent:WaitFor(seconds) end

---@class TimeEventContainer : Object
TimeEventContainer = Class()
---@type fun()
TimeEventContainer.TimeEvent = value()

The concept for this would be we play the audio and then wait for a set amount of time before pausing the audio. This won’t require the interactable component scripting.

We need to start the AudioManager script as usual.

local audio = script:GetYaComponent("YaSoundProviderComponent");

audio:Play();

Now after we play we need to use the WaitFor method of YaTime class. As YaTime is not a component in a studio, we cannot get it using GetYaComponent. Instead we use YaTime, which is an accessor for the YaTime class.
The WaitFor function emits an even “TimeEvent” when the time of waiting is done. We listen for that and then call a function to pause the audio.
We can do the delay by writing YaTime:WaitFor(Time), where you replace ‘Time’ within parenthesis with the amount of time to wait in seconds.
Now to listen to this event we need to store it in a variable.
So add the following line in your code.

eventcontainer = YaTime:WaitFor(5);

Now we need to define the function for pausing the audio :

local function PauseAudio()
    audio:Pause();
end

We call this function when eventcontainer emits the TimeEvent event.
Note : this is not a Custom event, so we need to use EventHelper.
Add the following line to your script now

EventHelper.AddListener(eventcontainer, "TimeEvent", PauseAudio);

That’s it for now as I cannot think of any other situation. So, if you have ideas for any other situation do tell me and I will add it to this post.

Thank you for reading this <3

My Discord : ShambaC#3440

3 Likes

great tutorial! Really helps!

1 Like