Audio
Go to the
guide project to see the source code for a fully working example of this guide.Can you imagine playing Mario Brothers games without the jump sound effect? How about playing Metroid with no music and no weapon sound effects? Audio is a great way to add some life to your game. Whether it is background music or sound effects, Velaptor has you covered.
Check out some of this audio!! They might bring back memories. 😉
Mario Jump | Metroid Ice Beam | Metroid Brinstar Music |
Step 1: Project setup​
Create a basic Velaptor application.
For more info, refer to the Project Setup guide.
You only need the OnLoad(), OnUnload(), and OnUpdate() methods.
Step 2: Create class fields​
Create the required class fields that will be used to get the keyboard input, and hold the previous keyboard state for each loop iteration, load the music audio content.
public class Game : Window
{
private ILoader<IAudio> audioLoader;
private IAppInput<KeyboardState> keyboard;
private IAudio? music;
private KeyboardState prevKeyState;
...
}
Step 3: Setup window​
Let's set up our window to not be so large, create an object to detect keyboard input, and create the audio loader to load audio content.
Create a Game() constructor and add the following code:
public Game()
{
Title = "Sounds";
Width = 1210;
Height = 600;
this.keyboard = HardwareFactory.GetKeyboard();
this.audioLoader = ContentLoaderFactory.CreateAudioLoader();
}
Step 4: Load and unload the content​
Now, let's proceed with loading the audio content for playback. Before we delve into the code for loading the
audio content, it's crucial to add the audio file to the project as content. This step ensures it can be loaded.
Once that's finished, we can add code to the OnLoad() method. This code will enable
us to load the audio content and create a keyboard input object. By utilizing the keyboard, we'll be able to easily
control the audio playback.
Currently, the supported audio formats are .mp3 and .ogg.
4.1: Get audio content​
You can provide whatever audio file you want, but if you don't have one, you can download one of these from the audio guide project, or from the mini audio players on this page.
4.2: Setup file as content​
We need to add the audio file to the project into a particular directory and then set the file to be copied to the build output. This will be the default location where Velaptor will be looking for audio content.
For more info, refer to the Adding Content guide.
4.3: Load the content​
Now that we have the audio file added to the project, we can write some code to load the audio content.
Add the following code to the OnLoad() method to load the sound. All that is required is the name of the
audio content and the type of audio buffer to use.
protected override void OnLoad()
{
this.music = this.audioLoader.Load("brinstar-music", AudioBuffer.Stream);
base.OnLoad();
}
Remember, the name of the audio file without its extension will be the name of the content.
Example: 'brinstar-music.ogg' will become 'brinstar-music'.
What is an audio buffer? An audio buffer is just a container that holds the audio data. When you hear "audio buffer", think of "audio data". There are two types of audio buffers, each with its use cases.
The two types of audio buffers are:
AudioBuffer.Full- This buffer type means that ALL of the audio data will be loaded into memory.AudioBuffer.Stream- This buffer type means that ONLY a small portion of the audio data will be loaded into memory at a time.
Using AudioBuffer.Full is better for short audio clips such as sound effects, while AudioBuffer.Stream is better for longer audio clips such as music.
You can use either buffer type you want for short and large audio content, but the buffer type you choose will affect your game's memory usage and performance.
If you want to save memory, use AudioBuffer.Stream. This will directly affect the performance of loading content. If you want better performance when it comes
to quick response time when changing audio playback such as pausing, playing, rewinding, and fast forwarding, use AudioBuffer.Full.
4.4: Unload the content​
We also need to unload the audio content when the window is closed. Add the following code to the OnUnload() method.
1. Add a using statement at the top of the file to get access to some content-related extension methods. Though these are not required,
they make unloading content a bit easier.
...
using Velaptor.ExtensionMethods; // Add this line here
...
public class Game : Window
{
...
}
2. Create the OnUnload() method and add the following code to unload the audio content.
protected override void OnUnload()
{
this.audioLoader.Unload(this.music);
base.OnUnload();
}
Step 5: Displaying sound info​
So we have an audio file to play, but we need to know some information like the current duration of the audio, if it's playing or paused, and some basic instructions for the user on how to control the audio playback. Since we are going to be using the keyboard to control the sound, it makes sense to tell the user which keyboard keys are used to stop, play, and perform other sound-related operations.
In the spirit of keeping things simple, we'll display the information in the title bar of the window. You could display the information in the window itself by rendering text.
5.1: Displaying audo state​
We'll create a method that will be used to display the information we want in the title bar. Create the following UpdateTitle()
method and add some code to get a string representing the current state of the audio file.
private void UpdateTitle()
{
string state;
if (this.music.IsStopped)
{
state = "Stopped";
}
else
{
state = this.music.IsPlaying ? "Playing" : "Paused";
}
}
5.2: Display time and instructions​
Now, add the code below the code you previously added. This code will get the minutes and seconds of the audio playback in the correct 2-digit format as well as the instructions for the user on how to play, pause, fast forward, and rewind the audio.
private void UpdateTitle()
{
...
var minutes = this.music.Position.Minutes;
var seconds = this.music.Position.Seconds;
var minStr = minutes <= 9 ? $"0{minutes}" : minutes.ToString();
var secStr = seconds <= 9 ? $"0{seconds}" : seconds.ToString();
var volume = $"Volume: {this.music.Volume}";
const string instructions = "Instructions: Space(play/pause) Esc(restart) Left(-5s) - Right(+5s) - Up(+vol) - Down(-vol)";
Title = $"{state} - {playTime} - {volume} | {instructions}";
}
Great! Now we have a method to update useful information about the audio as well as instructions for the user!!
Step 6: Controlling the audio​
For more info, refer to the Keyboard Input guide.
We can put this all together so we can control the audio playback but first, we need to inspect the state of the keyboard to see which key is being pressed.
6.1: Keyboard state & key pressed​
To get the current state of the keyboard and to check if any key has been fully pressed, add the code below to the
OnUpdate() method.
protected override void OnUpdate(FrameTime frameTime)
{
var currKeyState = this.keyboard.GetState();
bool IsPressed(KeyCode key)
{
return this.prevKeyState.IsKeyDown(key) && currKeyState.IsKeyUp(key);
}
}
This code snippet uses a local function to check whether or not a key is pressed, which is a new feature in C# 7.0. If you are not familiar with local functions, you can read more about them
.// This is a local function
bool IsPressed(KeyCode key)
{
return this.prevKeyState.IsKeyDown(key) && currKeyState.IsKeyUp(key);
}
6.2: Audio control with keyboard​
This tutorial would be pretty useless if we didn't provide control over the audio for demo and learning purposes, right?😉
You can make that happen by adding the following code after the local function in the OnUpdate() method.
protected override void OnUpdate(FrameTime frameTime)
{
...
if (IsPressed(KeyCode.Space))
{
if (this.music.IsPaused or this.music.IsStopped)
{
this.music.Play();
}
else
{
this.music.Pause();
}
}
else if (IsPressed(KeyCode.Escape))
{
this.music.Stop();
}
else if (IsPressed(KeyCode.Left))
{
this.music.Rewind(5);
}
else if (IsPressed(KeyCode.Right))
{
this.music.FastForward(5);
}
else if (IsPressed(KeyCode.Up))
{
this.music.Volume += 10;
}
else if (IsPressed(KeyCode.Down))
{
this.music.Volume -= 10;
}
}
6.3: Finishing up​
Now, to finish things up, we can finalize our code by implementing keyboard state tracking for the previous frame, alongside updating the window title. The significance of the previous keyboard state is important. Without it, we cannot accurately determine if a key has been pressed.
To accomplish this, we must diligently maintain a record of the keyboard's state in the previous frame and subsequently compare it with the current state. This approach enables us to discern whether a key has moved from a pressed down position to a released up position.
To implement this, simply insert the following code snippet after the OnUpdate() method.
protected override void OnUpdate(FrameTime frameTime)
{
...
this.prevKeyState = currKeyState;
UpdateTitle();
base.OnUpdate(frameTime);
}
Step 7: Run it​
Run the game and let's play some music! You should see the following window:
As the instructions in the title bar state, do the following to test out your game:
- Press the space bar to play and pause the audio.
- Press the escape key to stop or restart the audio.
- Press the left arrow key to rewind 5 seconds.
- Press the right arrow key to fast forward 5 seconds.
- Press the up arrow key to increase the volume.
- Press the down arrow key to decrease the volume.
Not bad right?! Now you can add sound effects and/or music to your games!!