<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Audio on DAD 424 Sound Design for Games</title><link>https://dsu-digital-sound-design.github.io/f-25-dad-424-sound-design-for-games/tags/audio/</link><description>Recent content in Audio on DAD 424 Sound Design for Games</description><generator>Hugo -- gohugo.io</generator><language>en</language><lastBuildDate>Wed, 12 Jun 2024 00:00:00 +0000</lastBuildDate><atom:link href="https://dsu-digital-sound-design.github.io/f-25-dad-424-sound-design-for-games/tags/audio/index.xml" rel="self" type="application/rss+xml"/><item><title>Controlling FMOD Busses with Unity Sliders (Part 2)</title><link>https://dsu-digital-sound-design.github.io/f-25-dad-424-sound-design-for-games/lectures/week-13/controlling-fmod-busses-2/</link><pubDate>Wed, 12 Jun 2024 00:00:00 +0000</pubDate><guid>https://dsu-digital-sound-design.github.io/f-25-dad-424-sound-design-for-games/lectures/week-13/controlling-fmod-busses-2/</guid><description>In this tutorial, we will cover how to control the volume of your FMOD buses (like Music and SFX) using Unity sliders, and more importantly, how to make sure those sliders remember their positions when your player changes scenes.
Part 1: Setting the Volume The Code In your Bus Control script, create a new public function. Let’s call it VolumeLevel. This function needs to accept a float parameter, which will represent the data coming from the slider.</description><content>&lt;p>In this tutorial, we will cover how to control the volume of your FMOD buses (like Music and SFX) using Unity sliders, and more importantly, how to make sure those sliders remember their positions when your player changes scenes.&lt;/p>
&lt;h2 id="part-1-setting-the-volume">Part 1: Setting the Volume&lt;/h2>
&lt;h3 id="the-code">The Code&lt;/h3>
&lt;p>In your Bus Control script, create a new public function. Let’s call it &lt;code>VolumeLevel&lt;/code>. This function needs to accept a &lt;code>float&lt;/code> parameter, which will represent the data coming from the slider.&lt;/p>
&lt;p>Inside the function, we use the FMOD API command &lt;code>.setVolume()&lt;/code>.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-csharp" data-lang="csharp">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#66d9ef">public&lt;/span> &lt;span style="color:#66d9ef">void&lt;/span> VolumeLevel(&lt;span style="color:#66d9ef">float&lt;/span> newSliderValue)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>{
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#75715e">// &amp;#39;Bus&amp;#39; is the variable where you stored your FMOD Bus path earlier&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> Bus.setVolume(newSliderValue);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="understanding-fmod-volume-floats">Understanding FMOD Volume Floats&lt;/h3>
&lt;p>It is important to understand how FMOD interprets these numbers. FMOD faders use a logarithmic scale (decibels), but the API uses a linear float scale between &lt;strong>0&lt;/strong> and &lt;strong>1&lt;/strong>.&lt;/p>
&lt;ul>
&lt;li>&lt;strong>1.0f:&lt;/strong> This equals the volume the fader was set to when you built your banks in FMOD. (If your fader was at 0dB, 1.0 = 0dB. If it was at -10dB, 1.0 = -10dB).&lt;/li>
&lt;li>&lt;strong>0.0f:&lt;/strong> This equals -infinity dB (silence).&lt;/li>
&lt;li>&lt;strong>0.5f:&lt;/strong> This is roughly -28dB (depending on the curve), effectively half the &amp;ldquo;fader distance&amp;rdquo; between the start point and silence.&lt;/li>
&lt;/ul>
&lt;h2 id="part-2-connecting-the-ui-slider">Part 2: Connecting the UI Slider&lt;/h2>
&lt;p>Now that the logic exists, we need to hook it up to the Unity UI.&lt;/p>
&lt;ol>
&lt;li>Select your &lt;strong>Slider&lt;/strong> object in the Unity Hierarchy.&lt;/li>
&lt;li>Find the &lt;strong>Slider&lt;/strong> component in the Inspector.&lt;/li>
&lt;li>Look for the &lt;strong>&amp;ldquo;On Value Changed (Single)&amp;rdquo;&lt;/strong> event box at the bottom.&lt;/li>
&lt;li>Click the &lt;strong>(+)&lt;/strong> icon to add a listener.&lt;/li>
&lt;li>Drag the GameObject containing your Bus Control script into the &amp;ldquo;Object&amp;rdquo; slot.&lt;/li>
&lt;li>In the function dropdown, look for your script name and select &lt;code>VolumeLevel&lt;/code>.&lt;/li>
&lt;/ol>
&lt;p>&lt;strong>Crucial Step:&lt;/strong> When selecting the function, you will see it listed twice: once under &amp;ldquo;Dynamic float&amp;rdquo; and once under &amp;ldquo;Static Parameters.&amp;rdquo;&lt;/p>
&lt;ul>
&lt;li>&lt;strong>Select the Dynamic Float version.&lt;/strong> This ensures the function receives the &lt;em>actual&lt;/em> current value of the slider as the player moves it.&lt;/li>
&lt;/ul>
&lt;h2 id="part-3-the-scene-reset-problem">Part 3: The &amp;ldquo;Scene Reset&amp;rdquo; Problem&lt;/h2>
&lt;p>If you play your game now, the sliders will work. However, you will notice a bug if you switch scenes.&lt;/p>
&lt;p>Let&amp;rsquo;s say the player lowers the volume to 50%. When they walk through a portal to Level 2, the scene reloads. FMOD is smart—it remembers the volume is at 50%. However, the &lt;strong>UI Slider&lt;/strong> is just a dumb object; it reloads from the prefab and resets visually to its default position (usually 100% or 0.9).&lt;/p>
&lt;p>Now you have a slider showing 100% volume, but the audio is playing at 50%. We need to sync them up when the scene starts.&lt;/p>
&lt;h2 id="part-4-syncing-sliders-with-getvolume">Part 4: syncing Sliders with &lt;code>.getVolume()&lt;/code>&lt;/h2>
&lt;p>To fix this, we need to tell the slider where the bus volume is actually located the moment the script starts. We do this using the &lt;code>.getVolume()&lt;/code> command.&lt;/p>
&lt;h3 id="the-challenge-with-getvolume">The Challenge with &lt;code>.getVolume()&lt;/code>&lt;/h3>
&lt;p>This command is slightly unique because it outputs &lt;strong>two&lt;/strong> distinct values. It requires two &lt;code>out&lt;/code> variables:&lt;/p>
&lt;ol>
&lt;li>&lt;strong>Volume:&lt;/strong> The value set by the &lt;code>setVolume&lt;/code> command (User input).&lt;/li>
&lt;li>&lt;strong>Final Volume:&lt;/strong> The value calculated after combining user input with FMOD modulators or automation.&lt;/li>
&lt;/ol>
&lt;p>We only care about the first one (User input).&lt;/p>
&lt;h3 id="the-solution">The Solution&lt;/h3>
&lt;p>First, update your script to include the UI namespace, or Unity won&amp;rsquo;t recognize what a &amp;ldquo;Slider&amp;rdquo; is.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-csharp" data-lang="csharp">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#66d9ef">using&lt;/span> UnityEngine;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#66d9ef">using&lt;/span> UnityEngine.UI; &lt;span style="color:#75715e">// Don&amp;#39;t forget this!&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Next, inside your class, create a reference to the Slider and variables to hold the volume data.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-csharp" data-lang="csharp">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#66d9ef">private&lt;/span> Slider slider;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#66d9ef">private&lt;/span> &lt;span style="color:#66d9ef">float&lt;/span> busVolume;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#66d9ef">private&lt;/span> &lt;span style="color:#66d9ef">float&lt;/span> finalBusVolume;
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Finally, in your &lt;code>Start()&lt;/code> function, grab the slider component and sync it to the FMOD bus:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-csharp" data-lang="csharp">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#66d9ef">void&lt;/span> Start()
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>{
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#75715e">// 1. Get the slider component attached to this object&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> slider = GetComponent&amp;lt;Slider&amp;gt;();
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#75715e">// 2. Get the current volume from the FMOD Bus&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#75715e">// We pass in our two float variables to catch the data&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> Bus.getVolume(&lt;span style="color:#66d9ef">out&lt;/span> busVolume, &lt;span style="color:#66d9ef">out&lt;/span> finalBusVolume);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#75715e">// 3. Update the slider visual to match the FMOD bus volume&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> slider.&lt;span style="color:#66d9ef">value&lt;/span> = busVolume;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="summary">Summary&lt;/h2>
&lt;p>By using &lt;code>setVolume&lt;/code> dynamically and &lt;code>getVolume&lt;/code> on initialization, you create a seamless experience for the player.&lt;/p>
&lt;ol>
&lt;li>&lt;strong>On Start:&lt;/strong> The script checks FMOD to see what the volume &lt;em>should&lt;/em> be and updates the slider visual.&lt;/li>
&lt;li>&lt;strong>During Gameplay:&lt;/strong> The slider updates the FMOD bus using the Dynamic Float event.&lt;/li>
&lt;li>&lt;strong>On Scene Change:&lt;/strong> Even though the UI destroys and reloads, the &lt;code>Start&lt;/code> function runs again, grabs the persistent FMOD volume, and snaps the slider back to the correct position.&lt;/li>
&lt;/ol>
&lt;p>Now your audio settings menu is robust and persistent across your entire game!&lt;/p></content></item><item><title>Creating Audio Zones and Dynamic Mixing with Snapshots</title><link>https://dsu-digital-sound-design.github.io/f-25-dad-424-sound-design-for-games/lectures/week-14/snapshots/</link><pubDate>Wed, 12 Jun 2024 00:00:00 +0000</pubDate><guid>https://dsu-digital-sound-design.github.io/f-25-dad-424-sound-design-for-games/lectures/week-14/snapshots/</guid><description>In our previous tutorial, we mastered Playback States to control when sounds play. Now, we’re going to control how they sound based on the game environment.
Have you ever noticed how audio changes when a character walks into a cave (reverb kicks in) or when they are low on health (the sound gets muffled and bass-heavy)? In FMOD, these dynamic mixing changes are handled by Snapshots.
In this guide, we will cover:</description><content>&lt;p>In our previous tutorial, we mastered &lt;strong>Playback States&lt;/strong> to control &lt;em>when&lt;/em> sounds play. Now, we’re going to control &lt;em>how&lt;/em> they sound based on the game environment.&lt;/p>
&lt;p>Have you ever noticed how audio changes when a character walks into a cave (reverb kicks in) or when they are low on health (the sound gets muffled and bass-heavy)? In FMOD, these dynamic mixing changes are handled by &lt;strong>Snapshots&lt;/strong>.&lt;/p>
&lt;p>In this guide, we will cover:&lt;/p>
&lt;ol>
&lt;li>What Snapshots are and how to create them.&lt;/li>
&lt;li>Creating a &lt;strong>Reverb Zone&lt;/strong> using Triggers.&lt;/li>
&lt;li>Handling signal routing (so your UI clicks don&amp;rsquo;t echo!).&lt;/li>
&lt;li>Creating a &lt;strong>&amp;ldquo;Low Health&amp;rdquo; filter&lt;/strong> using Parameters.&lt;/li>
&lt;/ol>
&lt;hr>
&lt;h2 id="what-is-a-snapshot">What is a Snapshot?&lt;/h2>
&lt;p>Think of a Snapshot as a &amp;ldquo;preset&amp;rdquo; for your FMOD Mixer. When active, a Snapshot can change the volume of buses, toggle effects on and off, or change effect parameters.&lt;/p>
&lt;p>There are two main types:&lt;/p>
&lt;ul>
&lt;li>&lt;strong>Overriding:&lt;/strong> Forces values to a specific state (good for absolute changes).&lt;/li>
&lt;li>&lt;strong>Blending:&lt;/strong> Adds changes on top of existing values (good for layering).&lt;/li>
&lt;/ul>
&lt;p>For this tutorial, we will focus on &lt;strong>Overriding&lt;/strong> snapshots to create distinct audio zones.&lt;/p>
&lt;hr>
&lt;h2 id="part-1-the-reverb-zone-trigger-based">Part 1: The Reverb Zone (Trigger-Based)&lt;/h2>
&lt;p>Let’s create a system where entering a tunnel applies reverb to our sound effects.&lt;/p>
&lt;h3 id="step-1-fmod-setup">Step 1: FMOD Setup&lt;/h3>
&lt;ol>
&lt;li>Go to the &lt;strong>Mixer&lt;/strong> window in FMOD.&lt;/li>
&lt;li>Create a Return Bus for Reverb if you haven&amp;rsquo;t already, and add a Reverb effect to it.&lt;/li>
&lt;li>Go to the &lt;strong>Snapshots&lt;/strong> tab and create a new &lt;strong>Overriding Snapshot&lt;/strong> named &amp;ldquo;Reverb Zone 1&amp;rdquo;.&lt;/li>
&lt;li>Deselect your snapshot for the time being.&lt;/li>
&lt;li>Select the SFX Bus and add a &lt;strong>Send&lt;/strong> to the Reverb Return Bus. Set the send level to around -6dB (adjust to taste).&lt;/li>
&lt;li>&lt;strong>Scoping:&lt;/strong> When you select the snapshot, your mixer faders will turn into yellow outlines. This means they are &amp;ldquo;scoped out&amp;rdquo; (unaffected).
&lt;ol>
&lt;li>Select the snapshot again. Mixer controls will appear with yellow outlines, meaning they are currently scoped out.&lt;/li>
&lt;li>Click the reverb send level on the SFX Bus so it becomes fully yellow, indicating it is scoped in and will be driven by the snapshot.&lt;/li>
&lt;/ol>
&lt;/li>
&lt;li>When you select the snapshot, the reverb send will be set to the value defined in the snapshot.&lt;/li>
&lt;li>&lt;strong>Modulation:&lt;/strong> To make the reverb fade in smoothly, right-click the Snapshot&amp;rsquo;s &lt;strong>Intensity&lt;/strong> dial (in the deck area) and add an &lt;strong>AHDSR Modulator&lt;/strong>. This ensures the reverb doesn&amp;rsquo;t snap on/off instantly.&lt;/li>
&lt;/ol>
&lt;h3 id="step-2-the-unity-script">Step 2: The Unity Script&lt;/h3>
&lt;p>Open Level 2 in Unity. This scene has more tunnels that we can use for testing reverb.&lt;/p>
&lt;p>In Unity, Snapshots are treated almost exactly like Events. We create an instance, play it, and stop it.&lt;/p>
&lt;p>Create a cube object with Create &amp;gt; 3D Object &amp;gt; Cube. Scale it to form a tunnel entrance and set its position so the player can walk through it. Make sure to set the cube&amp;rsquo;s collider to &lt;strong>Is Trigger&lt;/strong>. Call it &lt;code>Reverb Zone&lt;/code>. Also make sure the layer is set to &lt;code>Environment&lt;/code>.&lt;/p>
&lt;p>We&amp;rsquo;ll leave the &lt;code>Mesh Renderer&lt;/code> on so you can see the zone in the scene, but in a real game, you would likely turn this off.&lt;/p>
&lt;p>Create a script called &lt;code>F_ReverbZone&lt;/code> and attach it to the &lt;code>Reverb Zone&lt;/code> object.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-csharp" data-lang="csharp">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#66d9ef">using&lt;/span> UnityEngine;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#66d9ef">using&lt;/span> FMODUnity;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#66d9ef">using&lt;/span> FMOD.Studio;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#66d9ef">public&lt;/span> &lt;span style="color:#66d9ef">class&lt;/span> &lt;span style="color:#a6e22e">ReverbZone&lt;/span> : MonoBehaviour
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>{
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">private&lt;/span> EventInstance reverbSnapshot;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">void&lt;/span> Start()
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#75715e">// Create the instance just like a normal event&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#75715e">// Note: The path starts with &amp;#34;snapshot:/&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> reverbSnapshot = RuntimeManager.CreateInstance(&lt;span style="color:#e6db74">&amp;#34;snapshot:/Reverb Zone&amp;#34;&lt;/span>);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">void&lt;/span> OnTriggerEnter(Collider other)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">if&lt;/span> (other.CompareTag(&lt;span style="color:#e6db74">&amp;#34;Player&amp;#34;&lt;/span>))
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> reverbSnapshot.start();
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">void&lt;/span> OnTriggerExit(Collider other)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">if&lt;/span> (other.CompareTag(&lt;span style="color:#e6db74">&amp;#34;Player&amp;#34;&lt;/span>))
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#75715e">// Allow the AHDSR to fade it out naturally&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> reverbSnapshot.stop(STOP_MODE.ALLOWFADEOUT);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">void&lt;/span> OnDestroy()
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> reverbSnapshot.release();
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="step-3-fixing-the-signal-path-the-ui-echo-problem">Step 3: Fixing the Signal Path (The UI Echo Problem)&lt;/h3>
&lt;p>If you test the game now, entering the zone will reverb &lt;em>everything&lt;/em> sent to the SFX bus—including your UI menu clicks! That’s not what we want. We need to split the signal.&lt;/p>
&lt;p>&lt;strong>In the FMOD Mixer:&lt;/strong>&lt;/p>
&lt;ol>
&lt;li>Delete the send that goes from the &lt;strong>SFX&lt;/strong> bus to the &lt;strong>Reverb&lt;/strong> bus.&lt;/li>
&lt;li>Create two new Group Buses under your main &lt;strong>SFX&lt;/strong> bus: call them &lt;strong>In-Game&lt;/strong> and &lt;strong>UI&lt;/strong>.&lt;/li>
&lt;li>Route your footsteps, attacks, and environmental sounds into &lt;strong>In-Game&lt;/strong>.&lt;/li>
&lt;li>Route your menu sounds into &lt;strong>UI&lt;/strong>.&lt;/li>
&lt;li>Place the &lt;strong>Reverb Send&lt;/strong> on the &lt;strong>In-Game&lt;/strong> bus only.&lt;/li>
&lt;/ol>
&lt;p>Now, your UI remains crisp and dry, while the game world echoes around you.&lt;/p>
&lt;hr>
&lt;h2 id="part-2-the-distance-based-zone-automation">Part 2: The Distance-Based Zone (Automation)&lt;/h2>
&lt;p>Sometimes you don&amp;rsquo;t want a hard trigger; you want the effect to get stronger as you get closer to a specific object.&lt;/p>
&lt;h3 id="fmod-setup">FMOD Setup&lt;/h3>
&lt;ol>
&lt;li>Create a new snapshot (e.g., &amp;ldquo;Reverb Zone 2&amp;rdquo;).&lt;/li>
&lt;li>Instead of an AHDSR on the &lt;strong>Intensity&lt;/strong> dial, right-click and &lt;strong>Add Automation&lt;/strong>.&lt;/li>
&lt;li>Select the built-in &lt;strong>Distance&lt;/strong> parameter.&lt;/li>
&lt;li>Draw a curve:
&lt;ul>
&lt;li>At 5 meters, Intensity = 100%&lt;/li>
&lt;li>At 10 meters, Intensity = 0%&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ol>
&lt;h3 id="unity-script">Unity Script&lt;/h3>
&lt;p>Since this relies on distance, we must tell FMOD where the snapshot is located in 3D space.&lt;/p>
&lt;p>Create an Empty GameObject called &lt;code>Reverb Zone 2&lt;/code> and position at (-1, -3, 28.5). Create a new script and attach to the gameObject:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-csharp" data-lang="csharp">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#66d9ef">void&lt;/span> Start()
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>{
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> reverbSnapshot = RuntimeManager.CreateInstance(&lt;span style="color:#e6db74">&amp;#34;snapshot:/Reverb Zone 2&amp;#34;&lt;/span>);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> reverbSnapshot.start(); &lt;span style="color:#75715e">// Start immediately&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#75715e">// Attach the instance to this object so FMOD knows its location&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> RuntimeManager.AttachInstanceToGameObject(reverbSnapshot, transform, GetComponent&amp;lt;Rigidbody&amp;gt;());
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#66d9ef">void&lt;/span> OnDestroy()
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>{
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> reverbSnapshot.stop(STOP_MODE.IMMEDIATE);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> reverbSnapshot.release();
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;hr>
&lt;h2 id="part-3-the-low-health-filter-parameter-control">Part 3: The &amp;ldquo;Low Health&amp;rdquo; Filter (Parameter Control)&lt;/h2>
&lt;p>A common game mechanic is muffling audio when the player is dying. We can achieve this by controlling a Snapshot via a script parameter.&lt;/p>
&lt;h3 id="step-1-fmod-setup-1">Step 1: FMOD Setup&lt;/h3>
&lt;ol>
&lt;li>Add a &lt;strong>3-EQ Effect&lt;/strong> to your &lt;strong>In-Game&lt;/strong> bus.&lt;/li>
&lt;li>Create a Snapshot called &amp;ldquo;Low Health&amp;rdquo;.&lt;/li>
&lt;li>Scope in the &lt;strong>High&lt;/strong> and &lt;strong>Mid&lt;/strong> bands of the EQ and turn them all the way down (leaving only low frequencies).&lt;/li>
&lt;li>Right-click the Snapshot&amp;rsquo;s &lt;strong>Intensity&lt;/strong> dial and select &lt;strong>Expose as Parameter&lt;/strong>.&lt;/li>
&lt;li>Set the range of the parameter from 1 to 5.&lt;/li>
&lt;li>&lt;strong>Invert the curve:&lt;/strong>
&lt;ul>
&lt;li>When Parameter = 5 (Full Health), Intensity = 0% (No filter).&lt;/li>
&lt;li>When Parameter = 1 (Low Health), Intensity = 100% (Fully muffled).&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ol>
&lt;h3 id="step-2-linking-health-to-audio">Step 2: Linking Health to Audio&lt;/h3>
&lt;p>In your Player Controller script, you need to update the snapshot whenever health changes.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-csharp" data-lang="csharp">&lt;span style="display:flex;">&lt;span>FMOD.Studio.EventInstance healthFilter;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#66d9ef">void&lt;/span> Start()
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>{
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> healthFilter = RuntimeManager.CreateInstance(&lt;span style="color:#e6db74">&amp;#34;snapshot:/Low Health&amp;#34;&lt;/span>);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> healthFilter.start();
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> healthFilter.release(); &lt;span style="color:#75715e">// Release immediately so it cleans up when stopped&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">// Call this function whenever the player takes damage or heals&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#66d9ef">public&lt;/span> &lt;span style="color:#66d9ef">void&lt;/span> SetHealthSnapshot() {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> healthFilter.setParameterByName(&lt;span style="color:#e6db74">&amp;#34;Intensity&amp;#34;&lt;/span>, m_Damageable.currentHitPoints); &lt;span style="color:#75715e">// currentHealth should be between 1 and 5&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#66d9ef">void&lt;/span> OnDestroy()
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>{
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> healthFilter.stop(STOP_MODE.IMMEDIATE);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>By hooking the &lt;code>UpdateAudioHealth&lt;/code> function into your existing damage logic (using Unity Events or direct method calls), your audio will dynamically react to the player&amp;rsquo;s survival status.&lt;/p>
&lt;hr>
&lt;h2 id="summary">Summary&lt;/h2>
&lt;p>Snapshots are powerful tools that move beyond simple &amp;ldquo;Play/Stop&amp;rdquo; commands. By treating them as Event Instances in code, you can:&lt;/p>
&lt;ul>
&lt;li>Trigger reverb zones using &lt;strong>Colliders&lt;/strong>.&lt;/li>
&lt;li>Automate audio changes based on &lt;strong>3D Distance&lt;/strong>.&lt;/li>
&lt;li>Drive mix changes using gameplay variables like &lt;strong>Player Health&lt;/strong>.&lt;/li>
&lt;/ul>
&lt;p>With these tools, you&amp;rsquo;ve now covered the core pillars of FMOD implementation: Events, Parameters, Playback States, and Snapshots.&lt;/p></content></item><item><title>How to Use Playback States to Control Audio Flow</title><link>https://dsu-digital-sound-design.github.io/f-25-dad-424-sound-design-for-games/lectures/week-14/playback-states/</link><pubDate>Wed, 12 Jun 2024 00:00:00 +0000</pubDate><guid>https://dsu-digital-sound-design.github.io/f-25-dad-424-sound-design-for-games/lectures/week-14/playback-states/</guid><description>When implementing dynamic audio in Unity using FMOD, you often need to know exactly what an audio event is doing at any given moment. Is it playing? Is it stopping? Has it already stopped?
This is where Playback States come in.
In this tutorial, we will explore what FMOD Playback States are, how to access them in your C# scripts, and how to use them to fix a common audio bug: the &amp;ldquo;stuttering&amp;rdquo; sound effect on UI sliders.</description><content>&lt;p>When implementing dynamic audio in Unity using FMOD, you often need to know exactly what an audio event is doing at any given moment. Is it playing? Is it stopping? Has it already stopped?&lt;/p>
&lt;p>This is where &lt;strong>Playback States&lt;/strong> come in.&lt;/p>
&lt;p>In this tutorial, we will explore what FMOD Playback States are, how to access them in your C# scripts, and how to use them to fix a common audio bug: the &amp;ldquo;stuttering&amp;rdquo; sound effect on UI sliders.&lt;/p>
&lt;h2 id="what-is-a-playback-state">What is a Playback State?&lt;/h2>
&lt;p>A Playback State is a tool from FMOD’s API that allows us to check the status of an Event Instance. It returns the current status of the audio as an &lt;strong>Enumerator (Enum)&lt;/strong>.&lt;/p>
&lt;p>The five main states returned by the API are:&lt;/p>
&lt;ol>
&lt;li>&lt;strong>PLAYING&lt;/strong>&lt;/li>
&lt;li>&lt;strong>STARTING&lt;/strong>&lt;/li>
&lt;li>&lt;strong>STOPPING&lt;/strong>&lt;/li>
&lt;li>&lt;strong>STOPPED&lt;/strong>&lt;/li>
&lt;li>&lt;strong>SUSTAINING&lt;/strong>&lt;/li>
&lt;/ol>
&lt;p>By accessing these states, we can write logic that prevents sounds from overlapping or triggering incorrectly.&lt;/p>
&lt;h2 id="part-1-setting-up-the-variable">Part 1: Setting Up the Variable&lt;/h2>
&lt;p>To use Playback States, you first need to define a variable to store the state information. Open the &lt;code>MusicPlayer&lt;/code> script.&lt;/p>
&lt;p>Inside your class, declare a variable of type &lt;code>FMOD.Studio.PLAYBACK_STATE&lt;/code>:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-csharp" data-lang="csharp">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#66d9ef">private&lt;/span> FMOD.Studio.PLAYBACK_STATE musicPlayState;
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;em>Note: You can omit &lt;code>FMOD.Studio&lt;/code> if you are already using that namespace at the top of your script.&lt;/em>&lt;/p>
&lt;h2 id="part-2-retrieving-the-state">Part 2: Retrieving the State&lt;/h2>
&lt;p>To see what your audio is doing, you need to use the &lt;code>.getPlaybackState()&lt;/code> command. This command queries the event instance and outputs the result into the variable we just created.&lt;/p>
&lt;p>If you want to track the state in real-time, place this logic in your &lt;code>Update()&lt;/code> function:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-csharp" data-lang="csharp">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#66d9ef">void&lt;/span> Update()
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>{
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#75715e">// Assuming &amp;#39;_musicInst&amp;#39; is your FMOD Event Instance&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> _musicInst.getPlaybackState(&lt;span style="color:#66d9ef">out&lt;/span> musicPlayState);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#75715e">// Debug to the console to see the state change&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> Debug.Log(musicPlayState);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;strong>How it works:&lt;/strong>&lt;/p>
&lt;ul>
&lt;li>&lt;strong>While Playing:&lt;/strong> The console will log &lt;code>PLAYING&lt;/code>.&lt;/li>
&lt;li>&lt;strong>Entering a Portal/Stopping:&lt;/strong> If you trigger a stop command, it switches to &lt;code>STOPPING&lt;/code>.&lt;/li>
&lt;li>&lt;strong>Finished:&lt;/strong> Once the audio fades out completely, it switches to &lt;code>STOPPED&lt;/code>.&lt;/li>
&lt;/ul>
&lt;p>&lt;em>Important:&lt;/em> &lt;code>getPlaybackState&lt;/code> only checks the state on the specific frame it is called. If you only put this in &lt;code>Start()&lt;/code>, it will likely only ever say &lt;code>STARTING&lt;/code> because it never updates again.&lt;/p>
&lt;h2 id="part-3-a-practical-example-the-ui-slider-fix">Part 3: A Practical Example (The UI Slider Fix)&lt;/h2>
&lt;p>One of the most useful applications for Playback States is preventing audio spam.&lt;/p>
&lt;p>Imagine you have a volume slider in your Settings menu. You want a sound effect to play when the user moves the slider so they can hear the volume level. However, if you simply tell the sound to &lt;code>Start()&lt;/code> every time the slider value changes, the sound will restart every single frame the mouse moves. This creates a messy &amp;ldquo;machine gun&amp;rdquo; stuttering effect.&lt;/p>
&lt;p>We can use Playback States to ensure the sound only starts if it isn&amp;rsquo;t &lt;em>already&lt;/em> playing.&lt;/p>
&lt;h3 id="step-1-the-setup">Step 1: The Setup&lt;/h3>
&lt;ol>
&lt;li>&lt;strong>In FMOD:&lt;/strong> Create a 2D Event (e.g., &lt;code>LevelTest&lt;/code>) in a &amp;ldquo;UI&amp;rdquo; folder.
&lt;ol>
&lt;li>Add some test sounds into a Multi Instrument.&lt;/li>
&lt;li>Ensure the event is sent to the SFX bus.&lt;/li>
&lt;/ol>
&lt;/li>
&lt;li>&lt;strong>In Unity:&lt;/strong> Open your &lt;code>F_BusControl&lt;/code> script.&lt;/li>
&lt;/ol>
&lt;h3 id="step-2-modify-the-script">Step 2: Modify the Script&lt;/h3>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-csharp" data-lang="csharp">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">// ... Your existing using statements ...&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#66d9ef">public&lt;/span> &lt;span style="color:#66d9ef">class&lt;/span> &lt;span style="color:#a6e22e">F_BusControl_class&lt;/span> : MonoBehaviour
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>{
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#75715e">// .. . Your existing variables ...&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> EventInstance levelTestEvent;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#75715e">// Start is called once before the first execution of Update after the MonoBehaviour is created&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">void&lt;/span> Start()
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#75715e">// ... Your existing bus setup code ...&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#75715e">// only assign the event if its actually an SFX&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">if&lt;/span>(busPath == &lt;span style="color:#e6db74">&amp;#34;SFX&amp;#34;&lt;/span>)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> levelTestEvent = RuntimeManager.CreateInstance(&lt;span style="color:#e6db74">&amp;#34;event:/SFX/UI/LevelTest&amp;#34;&lt;/span>);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> } &lt;span style="color:#66d9ef">else&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> levelTestEvent.release();
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">public&lt;/span> &lt;span style="color:#66d9ef">void&lt;/span> VolumeLevel(&lt;span style="color:#66d9ef">float&lt;/span> newSliderValue)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> bus.setVolume(newSliderValue);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> levelTestEvent.start();
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>You can hear that the sound stutters when you drag the slider quickly. We need to fix it by telling it to only start if th sound is not already playing.&lt;/p>
&lt;p>We can use playback states to fix this issue:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-csharp" data-lang="csharp">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">// ... Your existing using statements ...&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#66d9ef">public&lt;/span> &lt;span style="color:#66d9ef">class&lt;/span> &lt;span style="color:#a6e22e">F_BusControl_class&lt;/span> : MonoBehaviour
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>{
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#75715e">// .. . Your existing variables ...&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> EventInstance levelTestEvent;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> PLAYBACK_STATE pbState;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#75715e">// Start is called once before the first execution of Update after the MonoBehaviour is created&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">void&lt;/span> Start()
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#75715e">// ... Your existing bus setup code ...&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#75715e">// only assign the event if its actually an SFX&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">if&lt;/span>(busPath == &lt;span style="color:#e6db74">&amp;#34;SFX&amp;#34;&lt;/span>)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> levelTestEvent = RuntimeManager.CreateInstance(&lt;span style="color:#e6db74">&amp;#34;event:/SFX/UI/LevelTest&amp;#34;&lt;/span>);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> } &lt;span style="color:#66d9ef">else&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> levelTestEvent.release();
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">public&lt;/span> &lt;span style="color:#66d9ef">void&lt;/span> VolumeLevel(&lt;span style="color:#66d9ef">float&lt;/span> newSliderValue)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> bus.setVolume(newSliderValue);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">if&lt;/span>(busPath == &lt;span style="color:#e6db74">&amp;#34;SFX&amp;#34;&lt;/span>)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#75715e">// Check the playback state before starting the event&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> levelTestEvent.getPlaybackState(&lt;span style="color:#66d9ef">out&lt;/span> pbState);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">if&lt;/span> (pbState != PLAYBACK_STATE.PLAYING)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> levelTestEvent.start();
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Now, when you drag the slider, the sound will play once. It will only play again after the previous instance has finished, resulting in a smooth, non-stuttering audio experience.&lt;/p>
&lt;h2 id="summary">Summary&lt;/h2>
&lt;p>By using &lt;strong>Playback States&lt;/strong>, you gain precise control over when your audio triggers. You are no longer just telling FMOD to &amp;ldquo;Play&amp;rdquo;; you are asking FMOD &amp;ldquo;Are you busy?&amp;rdquo; and acting accordingly.&lt;/p>
&lt;p>This simple check is essential for creating polished UI sounds, managing dynamic music transitions, and ensuring your game&amp;rsquo;s audio sounds professional and clean.&lt;/p></content></item></channel></rss>