Virtual Piano in C#

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.

DotNetPiano.jpg

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:

DotNetPianoUML.jpg

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


Similar Articles