Xblog.org

>> Tutorials >> MDX


Locations of visitors to this page

Background Music

Right so we've covered Sound-FX in the Last Tutorial is Time we're going to cover the area so often missed in Game tutorials, The Background music. This might not sound like a big deal but if you like me keep the background music on, it can add to the suspense of a situation like in DOOM3 or AvP, or it can make the adrenalin pump that little bit faster as you try to out run the cops like in the Need for Speed games. In short Music is every bit as important as the individual Sound-FX for setting your scene.

To start off open your project from the last tutorial and add another Class Library but this time call it "MusicFX". then add the same using lines that appear in fig 1.0.

fig 1.0
1:   namespace xblog {
2:       using System;
3:       using System.IO;
4:       using System.Windows.Forms;
5:       using DSound Microsoft.DirectX.DirectSound;
6:       using Microsoft.DirectX.AudioVideoPlayback;

Straight off you should notice that we are using an extra library called "Microsoft.DirectX.AudioVideoPlayback" and it's this that makes it all work. Once again I've given DirectSound a short hand name of "Dsound". We're also using "System.IO" so that we can fetch the list of music files ourselves, I'll explain this more later when we need it.

Next we need our global data storage, so add the code from fig 2.0 to the class.

fig 2.0
8:       /// <summary>
9:       /// DX-Music Wrapper for playing MP3s in background.
10:       /// </summary>
11:       public class MusicFX : object {
12:           private Audio songObject;
13:  
14:           private string[] songList;
15:           private int currentSong 0;
16:           private int totalSongs 0;
17:  
18:           private int musicVolume 0;

The Audio object on line 12 is the one that makes all the noise.
The songlist string array on line 14 is going to store the file & path for the songs we find.
currentSong, totalSongs, & musicVolume are self-explanatory.

fig 3.0
20:           /// <summary>
21:           /// Default constructor - initializes nothing, there is nothing to do here.
22:           /// </summary>
23:           public MusicFX() {
24:           }

The default constructor(fig 3.0) is empty because, well there really is nothing to do here. The initSongList on the other hand is where we get everything ready in fig 4.0.

fig 4.0
26:           /// <summary>
27:           /// Set up the song list from the path that is passed.
28:           /// </summary>
29:           /// <param name="dir">path to scan</param>
30:           public void initSongList(string dir) {
31:               songList = (string[])Directory.GetFilesdir"*.mp3");
32:               currentSong 0;
33:               totalSongs songList.Length;
34:               
35:               songObject new Audio(songList[currentSong]);
36:               songObject.Ending += new System.EventHandlerthis.SongEnding );
37:               
38:               // Get the init volume
39:               songObject.Volume -= 600;
40:               musicVolume songObject.Volume;
41:           }

First up is the one and only use for "System.IO" this is just an option on how to get the songs in to the Class if you prefer the method we used in the Sound-FX tutorial then just change "String Dir" to "string[] Dir" and set songList directly that way you control the indexes for each song.

CurrentSong defaults to 0 as we're using a Zero based array. totalSongs is set to mark the top end of the songList array

Now, the Audio Object is set to the songObject variable using the first song in the array as the default song to play. if the songList array is NULL at this point things don't go well, that's not to say that if the songList is just empty things go wrong only if it's a NULL object.

Also we need to add an event handler to the songObject for when the current song is ending or we'll never know when the current song has ended so there will be a sudden lack of music, no crash, no fire and brimstone, just NO MUSIC!!!!!!

when the Audio object is first created it defaults to maximum volume quite why I subtract 600 I can't remember but we should lower the volume of the background music so we can hear the other sounds. we also need to track the current volume for when we change it so store it in musicVolume for later.

fig 5.0
43:           /// <summary>
44:           /// Return the currently playing song filename
45:           /// </summary>
46:           public string songName() {
47:               return (string)"(" currentSong.ToString() + "/" 
                     totalSongs.ToString() + ") " songList[currentSong];
48:           }

Fig 5.0 was just added so I could get the name of the current song and it's place in the song list.

fig 6.0
50:           public void OpenSong() {
51:               if(songList != null)
52:               {
53:                   songObject.Open(songList[currentSong]);
54:               else {
55:                   MessageBox.Show("songlist is NULL");
56:               }
57:           }

Fig 6.0 and Fig 6.1 are overloads to do the same thing, Fig 6.0 simply checks that songList is not NULL, if everything is ok it opens the file that is in the currently selected index of songList. you still need to make a call to the PlaySong method that I'll go through in a second. no joke.

fig 6.1
59:           public void OpenSong(string Song) {
60:               if(File.Exists(Song))
61:               {
62:                   // check if the song is in the array
63:                   int Array.IndexOf(songListSong);
64:                   if (!= -1) {
65:                       // Song Found In Array.
66:                       currentSong i;
67:                       songObject.Open(songList[currentSong]);
68:                   else {
69:                       // Song NOT Found In Array.
70:                       songList[++totalSongs] = Song// Add new song to array.
71:                       songObject.Open(songList[currentSong]);// Open the Song.
72:                   }
73:               }
74:           }

Fig 6.1 is a little more complex you supply the fully qualified file name for the file and if its already in the array, its entry is used, if not the new file path is added at the end of the array and loaded anyway. Again you still need to call PlaySong method which leads me nicely into fig 7.0

fig 7.0
76:           public void PlaySong() {
77:               songObject.Play();
78:               songObject.Volume musicVolume;
79:           }

Now you may want me to go into more detail but I think you can see that by calling the PLAY method on the songObject it plays the song, we also reset the volume just for the hell of it.

fig 8.0
81:           public void StopSong() {
82:               songObject.Stop();
83:           }

Are you seeing a pattern forming here by calling the songObject's STOP method the song magically stops playing.

fig 9.0
85:           public void nextSong() {
86:               if(songList != null)
87:               {
88:                   StopSong();
89:                   currentSong++;
90:                   if(currentSong totalSongs 1currentSong 0;
91:                   OpenSong();
92:                   PlaySong();
93:               else {
94:                   MessageBox.Show("songlist is NULL");
95:               }
96:           }

Well the nextSong & prevSong methods are a little more complex than the last two, but not by much.

Check the songList, stop the current song(fig 8.0) increment the song index and check it's not too big, open the next song(fig 6.0), play it(fig 7.0) done

fig 9.1
98:           public void prevSong() {
99:               if(songList != null)
100:               {
101:                   StopSong();
102:                   currentSong--;
103:                   if(currentSong 0currentSong totalSongs 1;
104:                   OpenSong();
105:                   PlaySong();
106:               else {
107:                   MessageBox.Show("songlist is NULL");
108:               }
109:           }

prevSong(fig 9.1) method is exactly the same just decrement the index and check it's not less than 0.

fig 10.0
110:           public void pauseSong() {
111:               songObject.Pause();
112:           }

PauseSong(fig 10.0) is as sickeningly straight forward as the Play and Stop methods.

fig 11.0
114:           /// <summary>
115:           /// Event occurs when a song has reached it's end.
116:           /// </summary>
117:           public void SongEnding(System.Object senderSystem.EventArgs e)
118:           {
119:               currentSong++;
120:               if(currentSong totalSongs 1currentSong 0;
121:               this.OpenSong();
122:               this.PlaySong();
123:           }

Remember that SongEnding event we set up way back in fig 4.0, well here it is in fig 11.0 in this class I went for the old play the next song in the list job, simple increment the index check it's not too big Open the new song play the new song, job done.

fig 12.0
125:           public void Dispose()
126:           {
127:               ifsongObject != null songObject.Dispose();
128:           }
129:       } // Closing for the Class.
130:   } // Closing for the namespace.

With that all done I chucked in the dispose method just to be tidy.

Using this class is as straight forward as you'd think, create the instance of the class, init the song list, then play.

MORT.


Disclaimer

This document and all files and code provided with it are provided "as is" and without any warranty at all, not even the implied warranty of fitness of use for any particular purpose. I am not responsible for any harm that might come from use or misuse of either this document or any of the files available for download from this site. Neither is there any guarantee that the information in this document is correct.