Having finally got around to installing DirectX 9.0. I was impressed to find that it has several assemblies encapsulating .NET. Previous to the existence of .NET, I was loathe to touch those convoluted API's. The API's usually involved COM which sometimes requires a degree in Rocket Science or equivalent. Now perhaps its worth exploring.
This application takes advantage of DirectSound, one of the many media technologies packaged with DirectX 9. There are four steps to using the DirectSound technology in C#.
1. Create a sound device. (Here we just use a default constructor for the default device.)
2. Create a Buffer Description. The description contains information describing how the buffer will behave.
3. Use the device, description, and the name of the .WAV file to create a secondary buffer. All sound is played using this buffer object. You can also change properties of the secondary buffer (such as frequency) to alter the attributes of the sound
4. Play the sound. Playing the sound is accomplished through a method on the secondary buffer.
Figure 1 - Dot Net Piano Application
The table below lists the classes in the Microsoft.DirectX.DirectSound assembly:
DirectSound Class |
Description |
Device |
The DirectSound Device |
BufferDescription |
The description object for the buffer that sets some of the properties of the buffer |
SecondaryBuffer |
The buffer object where audio data is written to and played from. |
Table 1 - DirectSound classes used in this project
In this article we use DirectSound in conjunction with GDI+ (instead of DirectDraw) to create a virtual piano. The piano only contains a little more than one octave, but you can easily extend it given the extensible design shown below:
Figure 2 - Virtual Piano UML Design reverse engineered using WithClass 2000
To play the piano in the program, simply click the left mouse button on a virtual key on the keyboard. You can also adjust the volume and panning of the piano with the sliding toolbars.
This program uses DirectSound to take advantage of the fact that you can alter the frequency of the DirectSound buffer by changing the Frequency property. In this way, you can use the same wave file for each key and simply alter the buffer's frequency for the particular key. For this application, we chose the ding.wav file to produce the sound for the key, but the truth is, you could replace this file with any wave file of your choosing possessing a similar duration.
The first step is to put references in our project that allow us to use the DirectSound assemblies. You can either use the DirectX 9 Visual C# Project Wizard that is automatically installed with DirectX 9 or you can manually insert the assemblies into your project. You'll also need to declare the include references in your project as shown below:
using Microsoft.DirectX.DirectSound;
using Buffer = Microsoft.DirectX.DirectSound.Buffer;
The second step in playing sound is to create our Device object done in the form constructor shown in the code below. The Device object is constructed with no parameters to obtain the default device.
Listing 1 - Creating the DirectSound Device
try
{
//Initialize the DirectSound Device
applicationDevice = new Device();
// Set the priority of the device with the rest of the operating system
applicationDevice.SetCooperativeLevel(this, CooperativeLevel.Priority);
}
catch(SoundException)
{
// could not create sound device, exit
MessageBox.Show("Could not initialize DirectSound. Sample will exit.", "Exiting...", MessageBoxButtons.OK,
MessageBoxIcon.Error);
Close();
return;
}
In the piano program, the sound is played each time a virtual piano key is pressed. The code for the mouse being pressed is executed in the mousedown event handler shown below. This code determines the frequency of the Secondary Buffer by finding the key that was pressed and looking up the corresponding frequency for that piano key. (Each PianoKey object contains the frequency associated with it.). Once the frequency is determined we simply need to play the WAV file with the new frequency.
Listing 3 - Handling the Playing of the Piano Key when the Mouse is Pressed
private void PianoForm_MouseDown(object sender, System.Windows.Forms.MouseEventArgs e)
{
// Determine the key that was pressed from the mouse position and lookup the frequency
int freq = FindFrequency(new Point(e.X, e.Y), out CurrentKey);
// Redraw the current key
if (CurrentKey != null)
Invalidate(CurrentKey.Border);
// Play the Note for the pressed key
PlayNote(freq);
}
The PlayNote method creates the secondary sound buffer, assigns the frequency to the secondary sound buffer, and calls play on the buffer. We need to recreate the buffer each time so we are always altering the original reference data.
Listing 4 - Play the Note Corresponding to the Frequency
private void PlayNote(int freq)
{
if (null != applicationBuffer)
{
// First we need to 'recreate' the buffer
// so we have a starting point with fresh data
applicationBuffer.Dispose();
applicationBuffer = null;
// recreate a new buffer description as well
BufferDescription desc = new BufferDescription();
desc.ControlFrequency = true;
desc.ControlPan = true;
desc.ControlVolume = true;
try
{
applicationBuffer = new SecondaryBuffer(strFileName, desc, applicationDevice);
// Change the frequency here
applicationBuffer.Frequency = freq;
// No effects in this version
BufferPlayFlags PlayFlags = 0;
// Before we play, make sure we're using the correct settings of volume and pan
tbarPan_Scroll(tbarPan, null);
tbarVolume_Scroll(tbarVolume, null);
// Play the contents of the buffer
applicationBuffer.Play(0, PlayFlags);
}
catch
{
}
}
}
Conclusion
Producing sound just became a lot easier because its encapsulated nicely in .NET. This application could be easily expanded to turn the Virtual Piano into a virtual synthesizer because DirectSound gives you all sorts of effects that you can use to alter your wave file. We'll save this for the next composition using C# and .NET.
References
Microsoft DirectX on MSDN