In the simplest case, what do you want to do for a sound? You want to have some type of audio data, and you want to output this audio data to all of the speakers attached to the system. Sounds easy enough, but how do you do that in a DirectX application? For the simple case just described, it is exceedingly simple. Much like Direct3D has a Device class that controls the actual graphics hardware, DirectSound has a Device class that controls the actual sound hardware. Since both of these classes share the same name (in different namespaces), it's important to remember that if you are referencing both Direct3D and DirectSound in the same code file, you may need to fully qualify any references to a "Device" variable. DirectSound also has buffers that are used to hold the audio data it needs to play the sounds, much like Direct3D used buffers to hold geometry or pixel data. There are only two types of buffers in DirectSound, either the stock "Buffer" object, or the more robust "SecondaryBuffer" object (which also derives from Buffer). So since the simple case is so simple, you can just write it now, and take a look at it. First, create a new project and get the DirectSound references and namespaces in, and then add the following variable declarations: private Device device = null; private SecondaryBuffer sound = null; You will have one device that is used to talk to the sound hardware, and you will have one buffer you can use to play the actual audio data that will be loaded. Now, you can write the "InitializeSound" method, which will load and play a sound. Since this method will reference an audio file, you will need to provide one. The file used in this method was copied from the DirectX SDK. You can copy this file from the included source on the CD. Add the following method: public void InitializeSound() { device = new Device(); device.SetCooperativeLevel(this, CooperativeLevel.Normal); sound = new SecondaryBuffer(@"..\..\drumpad-crash.wav", device); sound.Play(0, BufferPlayFlags.Default); } As you can see, the code is quite simple. You create a new device (by using the parameter-less constructor; the other overloads for this object will be discussed later in this chapter), and then set the cooperative level on the device. I know what you're thinking: What exactly is a cooperative level? The sound hardware in your machine is shared among many different applications. Windows may beep at you when an error has occurred. Messenger may beep when a friend has logged in, and all of these things can happen while you're listening to your favorite songs. The cooperative level is used to determine how your application behaves with others, hence its name. The choice selected here is the default option. It has the best multitasking and resource sharing behavior. You may look in the DirectX SDK documentation for details on the other priority levels; for the needs of this application, you can stick with normal. After you have set the cooperative level, and are prepared to play nice with the rest of the system, you can create the buffer. You will load the buffer from a file (the code on the included CD uses a simple sound that ships with the DirectX SDK), and finally just play it. The first parameter of the play call is the priority, which is only valid if you've created your buffer deferred (which you haven't). You play the buffer using the default flags, and you're off. Now, you just need to actually call this method from somewhere, so how about replacing the main code with this: static void Main() { using (Form1 frm = new Form1()) { frm.InitializeSound(); Application.Run(frm); } } The simple case really is that simple. Just a few lines of code and the sound plays when the application starts up; it doesn't get much easier than that. If you look at the constructor for the SecondaryBuffer though, you'll notice that it takes quite a few different sets of parameters. You should look at those now. The following two overloads cover the basic variations of the constructors: public SecondaryBuffer ( System.String fileName , Microsoft.DirectX.DirectSound.BufferDescription desc , Microsoft.DirectX.DirectSound.Device parent ) public SecondaryBuffer ( System.IO.Stream source , System.Int32 length , Microsoft.DirectX.DirectSound.BufferDescription desc , Microsoft.DirectX.DirectSound.Device parent ) All of the constructors for the secondary buffer take in the device that will be used to play the actual audio data. As you can see, the constructors can take either a filename for the audio data you wish to load, or a stream that contains the audio data. There is only one constructor available that does not take in a BufferDescription object as well (which happens to be the one you used). A default BufferDescription is used in that case, because all buffers need to have a description when they are created. So what does the BufferDescription do? As the name implies, it describes the various options of the buffer you are creating. The properties of this object are found in Table 14.1.
As you can see, there are quite a few options for a buffer. This next example will use a few of them to show you what they do. It will be based off the simple one you've already done; however, this one will rotate the sound back and forth between left and right by controlling the pan of the sound. In order to do this, you'll need to use a buffer description object like you've just described. Add the following code after your call to SetCooperativeLevel: BufferDescription desc = new BufferDescription(); desc.ControlPan = true; desc.GlobalFocus = true; You are essentially telling DirectSound that you will be creating a buffer and will want to allow the pan to be controlled. Also, you want the buffer to be global across the entire system. You will also need to modify the creation of the buffer to use the constructor that takes the buffer description you just created. To improve the effect, you will have the buffer loop forever while it is being played. Modify the code as follows: sound = new SecondaryBuffer(@"..\..\drumpad-crash.wav",desc, device); sound.Play(0, BufferPlayFlags.Looping); Running the application now will cause the sound to play, and loop continuously, but the pan value of the buffer is never actually updated anywhere. For a nifty effect, why don't you update it every 50 milliseconds or so. In the design view for your form, add a timer control, and set the interval to 50. Add the following code for the timer event (double-clicking the timer in design view will bring up the event handler method): private void timer1_Tick(object sender, System.EventArgs e) { // Adjust the pan sound.Pan *= -1; } The pan value is an integer value that specifies the stereo position of the sound; the farther negative the value, the more the sound comes out of the left speaker. The more the value is positive, the more it comes out of the right speaker. A value of zero (the default) says to come out of both speakers equally. Since the default is zero, the code above will not actually do anything (since 0 * -1 is still 0). Now set the initial pan value to the left speaker and start the timer. Add this code directly after the play call in InitializeSound: sound.Pan = -5000; timer1.Enabled = true; Run the example now. You should now hear the sound file being played back and forth across your two speakers for an interesting effect. It's really quite simple to use sounds for your application. |