DirectX:DirectSound:Tutorials:VBNET:DX9:Playing Sounds

From GPWiki
Jump to: navigation, search

DirectX Tutorial 1

Playing a Sound

Note: This project and all other projects that I’ll be doing assume you have the latest DirectX SDK installed; in this case the DirectX 9 Summer 2004 release. You can find the DirectX download here. You’ll also need a version of Visual Basic.NET or Visual Studio.NET. These tutorials use the VB.NET 2005 Express Beta release which you can download for free here (at the time of this writing). You might also want to check the Visual Studio.NET homepage here.

Get the code for this tutorial here. It's recommended that you type all the code yourself however.

Setting up Microsoft Visual Studio.NET

The first thing you need to do for any DirectX project is add a reference to the parts of DirectX that you’ll be using, in this case DirectSound. After creating a project, click on the Projects | Add Reference menu item. You’ll see the following:

Refdlg.JPG

Scroll down until you find the following:

Microsoft.DirectX
Microsoft.DirectX.DirectSound

Select both of them (hold down the Ctrl key) and click OK. If for some reason they don’t appear in the list, click on the Browse tab and find them in the <windows folder>\Microsoft.NET\Managed DirectX\<version> folder. The version should be 9.02.2904 if you’ve installed the Summer 2004 SDK.

Setting Up Microsoft Visual Studio 2010

  • Projects > Add Reference > Browse
  • Find Microsoft.DirectX.DirectSound.dll (most likely located in C:\WINDOWS\Microsoft.NET\DirectX for Managed Code\1.0.XXXX.0 where XXXX is a version of DirectX. You might want to search this folder to find the file. Latest DirectX version where the file could be found: 1.0.2902.0).
  • Clock on the Ok button to close the Add Reference dialog.

Imports Microsoft.DirectX.DirectSound now works without a problem.

Using the Imports Statement

Every time you use something from DirectX (say a sound buffer), you need to fully qualify the name with the namespace in which it’s contained (if you’re not sure what a namespace is, check the VS.NET Help). For a DirectSound buffer this would be Microsoft.DirectX.DirectSound.SecondaryBuffer. Typing all this would get old quickly. Fortunately, Visual Studio has a way to shorten this - the Imports statement. Add the following the top of the code page in the form that was created when you created the project:

Imports Microsoft.DirectX.DirectSound

Now when you want to reference a buffer you just type the object name directly - SecondaryBuffer.

Declaring & Initializing the Objects

We’ll need two objects to play a sound - a Device object (which communicates with your sound hardware) and a SecondaryBuffer object (basically a class that uses a chunk of memory in which a sound file is stored). Add the following to the Declaration section of the form’s code:

Private _dev As Device
Private _buffer As SecondaryBuffer

Although we’ve declared them, we can’t use them until we actually create them. For the Device object that means using the New statement and setting it’s cooperative level. The cooperative level defines how well your application plays with other applications that want to use the sound card. Add the following to the form’s Load handler:

_dev = New Device
_dev.SetCooperativeLevel(Me.Handle, CooperativeLevel.Priority)

Passing the form’s handle to the SetCooperativeLevel function allows DirectSound to keep track of which application is using the device. The second parameter is the cooperative level, which can be one of the following:

Member

Value

Description

Normal

1

Sets the normal level. This level has the smoothest multitasking and resource-sharing behavior, but because it does not allow the primary buffer format to change, output is restricted to the default 8-bit format.

Priority

2

Sets the priority level. Applications with this cooperative level can have buffers that use the Format property and devices that call the Compact method.

WritePrimary

4

Sets the write-primary level. The application has write access to the primary buffer. No secondary buffers can be played. This level cannot be set if the DirectSound driver is being emulated for the device; that is, if the Device.Caps property returns the EmulateDriver flag in the Caps structure.

Using Priority is recommended.

Loading a Sound File

Our buffer is now ready to be filled with a sound file and played. We need a way to select a file. The easiest way to do this is with the OpenFileDialog object. Drop one onto your form along with a textbox and button (you can also add the label as I’ve done in the sample). Name the OpenFileDialog “ofd”, the textbox “txtSelectedFile” and the button “btnSelFile”. Set the OpenFileDialog’s Filter property to “Sound Files (*.wav)|*.wav”. Add the following code to the button’s Click handler:

If ofd.ShowDialog Then
    _buffer = New SecondaryBuffer(ofd.FileName, _dev)
    txtSelectedFile.Text = ofd.FileName
End If

Clicking the button will show the Open dialog, allowing you to select a wave (.wav) file. If you select one, the buffer is created using the filename and the device we’ve created and the name of the file is displayed in the textbox.

Playing and Stopping the Sound

OK, so we’ve loaded a sound in the buffer. Now we need a way to play and stop it. Drop two buttons on the form, name them “btnPlay” and “btnStop” and add the following:

Private Sub btnPlay_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnPlay.Click
   If Not _buffer Is Nothing Then _buffer.Play(0, BufferPlayFlags.Default)
End Sub
Private Sub btnStop_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnStop.Click
    If Not _buffer Is Nothing Then _buffer.Stop()
End Sub

Fairly straightforward, huh? We check to see if the buffer has been loaded with a sound by seeing verifying that it’s not Nothing, then play or stop it as needed. The parameters for the Play function are as follows:

priority - An integer that represents the Priority for the sound, used by the voice manager when assigning hardware mixing resources. The lowest priority is 0, and the highest priority is 0xFFFFFFFF. If the buffer was created with the BufferCaps.LocationDefer property set to false, this value must be 0.

flags - One or more of the following BufferPlayFlags enumerated values:

Member

Value

Description

TerminateByPriority

32

If the hardware has no available voices, a currently playing buffer will be stopped to make room for the new buffer. The buffer prematurely terminated will be the one with the lowest priority as set by the priority parameter Play method for the buffer.

TerminateByDistance

16

If the hardware has no available voices, a currently playing buffer will be stopped to make room for the new buffer. The buffer prematurely terminated will be selected from buffers that have the buffer's BufferDescription.Mute3DAtMaximumDistance property set to true and are beyond their maximum distance. If there are no such buffers, the method fails.

TerminateByTime

8

If the hardware has no available voices, a currently playing nonlooping buffer will be stopped to make room for the new buffer. The buffer prematurely terminated is the one with the least time left to play.

LocateInSoftware

4

Play this voice in a software buffer only. This flag cannot be combined with LocateInHardware or any voice management flag.

LocateInHardware

2

Play this voice in a hardware buffer only. If the hardware has no available voices and no voice management flags are set, the Play method fails. This flag cannot be combined with LocateInSoftware.

Looping

1

After the end of the audio buffer is reached, play restarts at the beginning of the buffer. Play continues until explicitly stopped. This flag must be set when playing a primary buffer.

Default

0

Play this voice with the default settings.

Note that the flags can have multiple values set to it. One of the things you might want to do is have the sound loop, playing over and over until it’s stopped. Let’s give our app this capability. Drop a checkbox on the form called chkLoop. Add the following code to it’s CheckChanged handler:

_loop = chkLoop.Checked

Add the _loop variable to the Declarations section:

Private _loop As Boolean = False

Initially the sound will not played looped. Now we need to tell DirectSound to loop the sound if we’ve checked the checkbox. Change the Play button’s code as follows:

If Not _buffer Is Nothing Then _buffer.Play(0, IIf(_loop, BufferPlayFlags.Looping, BufferPlayFlags.Default))

If we’ve checked the CheckBox, we’ll loop the sound, otherwise we’ll play it just once using the default settings.

We should be ready to try out our app. Click the Start button Start.JPG or use the Debug | Start menu. Click the button to select a file, then use the Play and Stop buttons a couple times. If you stop the sound in the middle of playing then play it again, you’ll notice that it picks up where it left off. DirectSound keeps track of the position in memory where you stopped it. So this really isn’t a Stop button, it’s more of a Pause button. Let’s make it a Stop button by resetting the position. Change the code in the Stop button’s Click handler to:

If Not _buffer Is Nothing Then
   _buffer.Stop()
   _buffer.SetCurrentPosition(0)
End If

Add another button to act as our Pause button. Add the call to the Stop method in its Click handler.

Setting Buffer Flags

In order to use SecondaryBuffer capabilities such as pan change, volume change, Etc., some flags must be set on the buffer. As described above, you can instantiate a SecondaryBuffer by passing the name of the file to be loaded, and the DirectSound.Device object. There are several overloaded constructors, however, one of which we will focus on here. This constructor takes three parameters instead of two. These are as follows:

  1. String: The path to a sound file to load.
  2. DirectSound.BufferDescription: A BufferDescription object which enables pan, Etc.
  3. DirectSound.Device: An instantiated device object which is used to create the buffer.


You cannot use frequency change, Etc. without explicitly telling DirectSound that this buffer can have frequency changed on it. To do this, Use the BufferDescription object:

 Imports Microsoft.DirectX
 dim BufferDesc as DirectSound.BufferDescription=new DirectSound.BufferDescription()

You can now set appropriate properties of BufferDesc to true. For instance, if we wanted to instantiate a SecondaryBuffer whose volume can be set, we would set ControlVolume to true:

 BufferDesc.ControlVolume=true

Once we're done setting properties to true as needed, we pass the BufferDesc object as the second parameter to a SecondaryBuffer:

 ' has volume change capabilities.
 dim TheSound as DirectSound.SecondaryBuffer=new DirectSound.SecondaryBuffer("C:\sound.wav",BufferDesc,DSDev)

Once we do this, we can safely change the volume of the buffer:

 TheSound.Volume=-5000

If the .ControlVolume property had not been set, the above line would have thrown an exception.

IMPORTANT! .Control3D cannot be set along with .ControlPan.

End Comments

DirectX makes it almost a no-brainer to play sounds in an application, especially using VB.NET and the Managed portion of the SDK. There’s a lot of other things you can do to a sound when you play it back - echo it, distort it, change it’s gain, etc. Check out the Effects… structures in the SDK documentation. If you have a surround sound system, you can also do 3D sound by using a Buffer3D object. It’s a very powerful section of the DirectX SDK that will add immeasurable value to your games.

See Also