In this tutorial we are going to look at playing audio with the PlayStation Suite SDK. This chapter is pretty simple over all, so it will be rather short. In the end you will have a UI driven application that can play sound effects and songs.
To get started, we are going to need a couple audio files, one or more songs for background music and one or more songs for sound effects. File formats are extremely limited in the SDK, all music files must be in MP3 format, while all sound effects must be in WAV format. If you need to convert your file, I recommend you check out audacity.
For my song files, I grabbed two freely available Mozart tracks from this site and saved them as Song1.mp3 and Song2.mp3. For sound effects I went to freesound.org ( free login required ) and downloaded this and this which I saved as Sound1.wav and Sound2.wav respectively. Of course, you can pick any mp3 or wav files you’d like, but these are the ones and the names I am going to use and the names I’ve chosen.
Now fire up UI Composer, if you haven’t already, you might want to read this tutorial, as I am not going to cover UI Composer in detail here. Create the following UI, I called mine AudioPlayer. Save the results and import into your project.
Let’s take a look at our GUI code in AudioPlayer.cs first, as it is the simplest.
using System; using System.Collections.Generic; using Sce.Pss.Core; using Sce.Pss.Core.Imaging; using Sce.Pss.Core.Environment; using Sce.Pss.HighLevel.UI; namespace Audio { public partial class AudioPlayer : Scene { public AudioPlayer() { InitializeWidget(); buttonNext.ButtonAction += delegate(object sender,TouchEventArgs e) { if(AppMain.currentSong ==0) AppMain.currentSong = 1; else AppMain.currentSong = 0; AppMain.songChanged = true; }; buttonPrev.ButtonAction += delegate(object sender,TouchEventArgs e) { if(AppMain.currentSong ==0) AppMain.currentSong = 1; else AppMain.currentSong = 0; AppMain.songChanged = true; }; buttonPlaySound1.ButtonAction += delegate(object sender, TouchEventArgs e) { AppMain.PlaySound("Sound1.wav"); LogText ("Playing Sound1.wav"); }; buttonPlaySound2.ButtonAction += delegate(object sender,TouchEventArgs e) { AppMain.PlaySound("Sound2.wav"); LogText ("Playing Sound2.wav"); }; } public void LogText(string outText) { this.labelOut.Text = outText; } } }
Everything here we have seen in prior tutorials. Basically we wire up our four buttons we created in UI Composer. nextButton and prevButton actually cheat a bit as the fact there are only two songs makes them identical in function. Obviously if you had more songs you would require more logic. Basically all either method do is update currentSong and songChanged values in AppMain. We will see these variables in more detail in a second. buttonPlaySound1 and buttonPlaySound2 simply call a PlaySound method in AppMain. Finally LogText simply exposes the labelOut field so the AppMain class can change it’s value.
Now lets take a look at AppMain.cs:
using System; using System.Collections.Generic; using Sce.Pss.Core; using Sce.Pss.Core.Environment; using Sce.Pss.Core.Graphics; using Sce.Pss.Core.Input; using Sce.Pss.Core.Audio; using Sce.Pss.HighLevel.GameEngine2D; using Sce.Pss.HighLevel.GameEngine2D.Base; using Sce.Pss.HighLevel.UI; namespace Audio { public class AppMain { private static BgmPlayer songPlayer; private static Bgm[] songs; private static SoundPlayer soundPlayer; public static bool songChanged { get; set; } public static int currentSong { get;set; } public static void Main (string[] args) { GraphicsContext graphics = new GraphicsContext(); UISystem.Initialize(graphics); AudioPlayer audioPlayer = new AudioPlayer(); UISystem.SetScene(audioPlayer); songs = new Bgm[2]; songs[0] = new Bgm("/Application/Song1.mp3"); songs[1] = new Bgm("/Application/Song2.mp3"); songPlayer = songs[0].CreatePlayer(); songPlayer.Play(); audioPlayer.LogText("Playing song1.mp3"); while(true) { SystemEvents.CheckEvents(); List<TouchData> touchData = Touch.GetData(0); UISystem.Update (touchData); graphics.SetViewport(0, 0, graphics.Screen.Width, graphics.Screen.Height); graphics.SetClearColor(new Vector4(0,0,0,1)); graphics.SetClearDepth(1.0f); graphics.Clear(); UISystem.Render (); graphics.SwapBuffers(); if(songPlayer.Status == BgmStatus.Stopped) { if(currentSong == 0) currentSong = 1; else currentSong = 0; audioPlayer.LogText ("Song ended, swapped to song:" + currentSong.ToString()); songPlayer.Dispose(); songPlayer = songs[currentSong].CreatePlayer(); songPlayer.Play (); } if(songChanged) { songPlayer.Dispose(); songPlayer = songs[currentSong].CreatePlayer(); songPlayer.Play (); songChanged = false; audioPlayer.LogText("Song changed due to user request."); } } } public static void PlaySound(string soundName) { //if(soundPlayer != null) // soundPlayer.Dispose(); soundPlayer = new Sound("/Application/" + soundName).CreatePlayer(); soundPlayer.Play(); } } }
The UI code is identical to that we covered in the earlier UI Composer tutorial, so we will ignore those bits. First we will declare our three audio related variables, a BgmPlayer, our Bgm songs array and our SoundPlayer. BGM stands for Background Music ( I assume ), and these two variables represent our songs and the player required to play them. Please note that both BgmPlayer and SoundPlayer have a dispose method and should be disposed of, something I do not do in this example ( given the primitive nature of the event loop ). In your code, be sure to dispose of them when you are done. The bool songChanged and currentSong are publically exposed so our UI has access to them, you will see them in action shortly.
In Main() we set up our graphics, initialize the UI system, create our UI and set it as active. We then declare our song array to contain two songs, both of which are loaded from file using the constructor. These (mp3 and wav) files need to be added to your project just like any other resource, right click your project-> Add Files… then right click the file, select Build Action and set it to content. The actual BgmPlayer is created by calling Bgm.CreatePlayer(), which is what we do next, then we tell the player to start playing the song.
Next we start our never ending event loop just like before. Unfortunately PS SDK has no callback facility to notify when a song has finished playing ( hopefully this is changed during beta! ) so we poll the songPlayer until the BgmStatus is Stopped. If it is stopped, we simply set currentSong to our other index into the songs array. As songPlayer has some unmanaged resources, we need to Dispose of it before creating a new player for the next song. Next we tell the newly created player to play the next song.
Next we check if the songChanged flag was changed ( from AudioPlayer on button handlers ), if a song change has occurred, we perform the exact same logic as if a song had ended. However instead of flipping to the next sound, the button press logic has already handled this step for us, so currentSong will already reference the next song to play.
Finally we expose a PlaySound method that was called earlier when we pressed a PlaySound1 or PlaySound2 button. You are limited to one Bgm at a time, but this is not the case with SoundPlayer objects. This code will allow multiple sound effects to play at once. Remove the commented section to limit playback to a single sound effect at a time. I am not sure what is proper form or if failing to dispose SoundPlayer before calling CreatePlayer again causes a leak, be wary!
And there you have a simple SoundPlayer application. Normally at this point I post an image of our application in action, but that obviously doesn’t work for a tutorial like this, so I captured instead this video:
You can download the full project here. This archive also includes all the songs and sounds used in this example.