Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / multimedia / DirectX

The ultimate Managed DirectSound 9 Tutorial. Part 1: a full introduction to Playback

4.72/5 (46 votes)
28 Sep 2005CPOL7 min read 1   7.2K  
How to playback sounds, apply effects and more with the Managed DirectSound 9.

Introduction

Managed DirectSound 9 is a part of Managed DirectX 9 library. In this tutorial I’m going to explain how to play sounds with Microsoft Managed DirectSound 9, how to apply effects, select the output audio device and a few more tricks. I hope you’ll discover that DirectSound is not as difficult as it seems.

Requirements

In order to develop and run applications using Managed DirectSound 9 (and all Managed DirectX 9), you have to install in your system the DirectX 9 standard redistributable package specifying in the command line the argument /InstallManagedDX (for example D:\Download\dxsetup /InstallManagedDX). You can also download from Microsoft’s website the package called the ‘DirectX 9.0c Redistributable for Software Developers - with updated DirectX for Managed Code and D3DX’, which includes and installs automatically the Managed DirectX libraries. Obviously, you have to add to your projects the references to Microsoft.DirectX and Microsoft.DirectX.DirectSound.

Here is an important note: in order to run the apps, the user is not forced to install the Managed DX9, but it is sufficient to copy in your apps’ root folder the referenced assemblies. To do this, set the LocalCopy property of the assemblies to true or copy them manually. I suggest the first way because if you create the Installer for the app with Visual Studio, it will detect the references and copy the assemblies to the installation destination. In the second way you have to remember to add them manually. To simplify your life, add these using clauses to your source files:

C#
using Microsoft.DirectX;
using Microsoft.DirectX.DirectSound;

First lines of code: play a Wave file

The first operation you have to do, is to create a DirectSound Device:

C#
private Device dSound;

After this, set it up:

C#
dSound = new Device();
dSound.SetCooperartiveLevel(handle, 
               CooperativeLevel.Priority);

Notes: handle is the handle to your main window (IntPtr), so write this.Handle. CooperativeLevel sets the hardware managing of the playback. There are three possibilities, but you should use Priority because it uses the hardware acceleration whenever possible. The Device constructor is overloaded: we’ll see other options later on.

The next step is to create a sound, using a Buffer, and its descriptor:

C#
private SecondaryBuffer sound;
private BufferDescription d = new BufferDescription();
// Set descriptor’s flags
d.ControlPan = true;
d.ControlVolume = true;
d.ControlFrequency = true;
d.ControlEffects = true;

The flags are:

  • ControlPan: allows you to control the balance (left/right) of the sound.
  • ControlVolume: allows you to control the volume of the sound.
  • ControlFrequency: allows you to control the sampling frequency of the sound (rarely used).
  • ControlEffects: allows you to apply effects to the sound.

There are a few other flags, but we are not interested in them.

C#
// Create the sound
sound = new SecondaryBuffer(filePath, d, dSound);

filePath is the full path of the .wav file, d is the descriptor created above, and dSound is the Device that will play the sound. After this, the sound is ready to be played. As a first parameter you can also specify System.IO.Stream, for example if you are receiving the sound from the Internet (in this case you must manage the difference between data consuming and receiving rates: think about your media player when it stops the playback to retrieve more data from the server).

The operations you can perform with a SecondaryBuffer are:

  • sound.Play(priority, flags): priority is the priority of the playback (0 is the default value, it means lowest priority), flags is a value of the enum BufferPlayFlags. The most important values are BufferPlayFlags.Default (stops when the sound finishes) and BufferPlayFlags.Looping (loops the sound).
  • sound.Stop(): pauses the sound. After this, if you call Play, the sound starts from the position where it was paused. To really stop the sound, you could use the following method.
  • sound.SetCurrentPosition(pos): sets the position in the sound at byte level. You can retrieve the total length (in bytes!) of the sound using the sound.Caps.BufferBytes property. If pos is zero, then that rewinds the sound.
  • sound.PlayPosition: returns the current playback position in the sound (in bytes!).
  • sound.Volume: the volume of the sound. Allowed values are from zero to -10000 decibel (logarithmic attenuation). 0 is the max volume, -10000 is the zero volume. At a value of -2500 or so, the sound is muted. It needs the flag sound.ControlVolume set to true.
  • sound.Pan: the balance of the sound. Values are from -10000 (all to left) to 10000 (all to right). Zero is the balanced value. Needs the flag sound.ControlPan set to true.
  • sound.Frequency: the sampling frequency of the sound (samples per second). Zero is the default value, but the allowed values are in the range 100 to 10000. This property is rarely used. Needs the flag sound.ControlFrequency set to true.
  • sound.Status.*: supplies information about the status of the sound. Playing (bool) indicates if the sound is currently playing, Looping (bool) indicates if the sound is set to loop.
  • sound.Format.*: more interesting info about the sound. Important members are AverageBytesPerSecond, BitsPerSample, Channels, SamplesPerSecond. They are self-explanatory, and thanks to them, we can compute the duration of the sound (that is not shown by any other property!). You can compute this in the following way:
    C#
    int totalSeconds = sound.Caps.BufferBytes / 
                       sound.Format.AverageBytesPerSecond;

Using speakers

Another amazing thing you can do, is to set the correct type of speakers attached to the output audio card. This option allows you to obtain better sound on each output system. The property you have to set is dSound.SpeakerConfig:

C#
// Create new Speakers
Speakers s = new Speakers();

// Set properties
s.Mono = false; // Sets as a mono speaker
s.Headphone = false; // Sets as headphones
s.Stereo = false; // Sets as generic stereo speakers
s.Quad = false; // Sets as quad system (two front, two rear)
s.FiveDotOne = false; // Sets as a 5.1 surround system
s.SevenDotOne = true; // Sets as a 7.1 surround system
s.Surround = false; // Sets as a generic surround system

dSound.SpeakerConfig = s;

Note: if you set the Speakers creating a new Speakers object every time, you can avoid resetting the old properties. So just replace the old Speakers object.

Choosing between sound cards

In many systems there are more than one sound card. DirectSound 9 allows you to choose the correct one, arranging a way to select the default one, or to choose the card that the user wants. To enumerate and show the user the installed cards, do as follows (supposing that the list of sound cards is put into a ComboBox, named cmbAudioCards):

C#
private DevicesCollection devList = new DevicesCollection();

cmbAudioCards.Items.Clear();
for(int i = 0; i < devList.Count; i++) {
   cmbAudioCards.Items.Add(devList[i].Description);
}

The first element is the default audio card that is always listed. The other items are the ‘physical’ audio cards. For example you can see:

  • Main Audio Driver
  • Audio SB Live! [400]
  • Bluetooth Audio

When the user selects a specific card, you can rebuild the Device as follows (in the ComboBox event handler):

C#
dSound = new Device(devList[cmbAudioCards.SelectedIndex].DriverGuid);
dSound.SetCooperativeLevel(this.Handle, CooperativeLevel.Priority);
// You have to recreate the sound (the descriptor d can be reused)
sound = new SecondaryBuffer(filePath, d, dSound);

DriverGuid is the Global Unique Identifier of the driver instance for the selected audio card. At the first start, or if the user doesn’t select a specific audio card, you can use the default constructor of Device, or select via-code the first element in devList, that is the default audio card.

Selecting different audio cards can effect the system performance: in the example above, the SoundBlaster Live! has in-hardware acceleration capabilities and supports upto 5.1 systems. Bluetooth audio is worst, and can cause a CPU load increment while applying effects or when managing 3D audio.

Applying effects to the audio playback

One of the most important features of DirectSound 9 is the possibility to apply real-time audio effects to the sounds in playback. You can also apply a virtually unlimited chain of effects (according to the system capabilities). All the effects are applied in-hardware whenever possible, otherwise emulated via-software (causing an important drop in the performance!). Remember to set the flag sound.ControlEffects to true!

In DirectSound 9 there are – needless to say :) – 9 effects: Chorus, Compressor, Distortion, Echo, Flanger, Gargle, Interactive3DLevel2Reverb, ParamEqualizer, WavesReverb. I want to say that there is not much documentation about these effects, especially because each of them has different settings. The first thing is to create an array of EffectDescription:

C#
EffectDescription[] fx = new EffectDescription[1];

In this example we use only one effect, but if you want, you can use more effects by creating an array long enough, and inserting one effect per element. Suppose you want to apply the Echo effect:

C#
fx[0].GuidEffectClass = DSoundHelper.StandardEchoGuid;
sound.SetEffects(fx);

Suppose now you want to apply the Parametric Equalizer effect:

C#
fx[0].GuidEffectClass = DSoundHelper.StandardParamEqGuid;
sound.SetEffects(fx);

Note: you can apply effects only when the sound is not played. As I reported above, each effect has different options. I will explain here how to set up the Parametric Equalizer effect:

C#
fx[0].GuidEffectClass = DSoundHelper.StandardParamEqGuid;
sound.SetEffects(fx);
ParamEqEffect eqEffect = (ParamEqEffect)sound.GetEffects(0);
EffectsParamEq eqParams = eqEffect.AllParameters;
// Specific properties!
eqParams.Bandwidth = 36; // Apply a gain on the highest frequency
eqParams.Gain = ParamEqEffect.GainMax;
eqEffect.AllParameters = eqParams;

Abstracting the procedure, you have to first create an array of effects with StandardEFFECTNAMEGuid, then set the parameters, using EFFECTNAMEEffect and EffectsEFFECTNAME objects, where EFFECTNAME is the name of the Effect. Always remember to perform the correct casts. To reset the effects on a sound, you have to overwrite it:

C#
sound = new SecondaryBuffer(filePath, d, dSound);

For further info about the effects, please go through the MSDN Library.

Conclusions

By now you should have developed a little familiarity with a DirectSound device, the Effects and other options. DirectSound fits to playbacks of short sounds, such as explosions, shots and so on. If you want to play a long Wave sound you should use the namespace Microsoft.DirectX.AudioVideoPlayback, that I will explain in future. In the next tutorial I will explain how to use 3D Sounds with DirectSound.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)