I finally had an opportunity to update my Silverlight drumpad program to be a little more what I had in mind when I started.

There are now 6 different drum kits you can choose from on the fly. This was done by adding the sound bites for the drums to the project as a resource, and assigning the path of the sound file to a string when a particular kit is selected from the list box. This is done through a case statement, a portion of which is shown here:

case “Yamaha RX-21″:
bassDrumSound = “sounds/YamahaRX21/Bassdrum.wma”;
snareDrumSound = “sounds/YamahaRX21/Snaredrum.wma”;
closedHatDrumSound = “sounds/YamahaRX21/ClosedHat.wma”;
openHatDrumSound = “sounds/YamahaRX21/OpenHat.wma”;
tomHighDrumSound = “sounds/YamahaRX21/TomH.wma”;
tomMedDrumSound = “sounds/YamahaRX21/TomM.wma”;
tomLowDrumSound = “sounds/YamahaRX21/TomL.wma”;
clapDrumSound = “sounds/YamahaRX21/Clap.wma”;
clapPad.msgDrumType.Text = “Clap”;
crashDrumSound = “sounds/YamahaRX21/Crash.wma”;
break;

As described in my previous drumpad post, due to problems with repeating audio clips, I elected to insert a media element each time a key is pressed. Because of this, it was easy to utilize the string variables defined above to make a quick change to the source property of the media element before inserting it into the application:

_mediaElement.Source = new Uri(snareDrumSound, UriKind.Relative);

In the case of the “80’s Drum Kit”, there is a cowbell sound instead of the clapping sound attached to the drumpad triggered with the “D” key. When this kit is selected, the label on the appropriate pad is also updated. You can see the text property being changed in the statement shown above, and when the 80’s kit is selected, the label is updated right after the new sound file is assigned. Here’s what that looks like:

clapDrumSound = “sounds/80s/Cowbell.wma”;
clapPad.msgDrumType.Text = “Cowbell”;

Another big change is the addition of backing tracks to which you can play along. For these, I initially figured I’d use a loop-based program (Sony ACID Studio 7.0) to create some tracks, but given time constraints, I instead elected to use the software to render out MIDI files I found online. The upside is that it’s a big time saver, and it’s very easy to pull out the drum tracks before rendering the file to a .wma for use in the app. The downside is, they don’t always sound so great.

The drum sound effects are all fairly compact - ranging from 15-40K a piece. There’s a couple that are bigger, but the majority are small. The backing tracks are fairly heavily compressed, but the songs still average around 1MB a piece. This made the app a little bigger than I had hoped, but I like having the sounds all there “on demand”.

The other change that happens under the covers is the cleaning mechanism. As I described previously, the sounds are added to a special container canvas on the fly as keys are pressed. I set a cleaning threshold, then run a method to remove the old media elements from the container when it reaches a certain size. This worked, but it would cause some hesitation in the sounds from time to time, because it would clean out the entire containing canvas, meaning the sound that was playing, too. I changed this so that the threshold is much lower (8 nodes), but the cleaner only pulls out the bottom 4, so it will typically not touch the sound that is currently playing. This turns out to be much smoother and cleaner, as well as doing its job a little more quickly.

The final app can be seen here.

As I’ve been migrating towards using C# with Silverlight 2, the best way I’ve found to learn it (aside from the tutorials/labs out there), is to actually write applications. Today, I wrote one that emulates an electronic drum kit.

The idea was to create a set of drum “pads”, associate each pad with a key on the keyboard, and then play an associated MediaElement sound when the key is pressed. The end result looks like this:

You can check out the live version of it here.

The application was created by using a single “drum pad” that has a storyboard on it that will flash the pad red when a key is pressed. The pad was instantiated 9 times and positioned as shown in the screenshot.

Interestingly enough, the most challenging part of creating the app was getting the sound to play. The “press a key, play the sound” idea sounds really simple, and works great.

Once.

After that, the sound changes volume randomly, and does not play consistently. Searching the Silverlight.net forums turned up a history on this issue back as far as June of 2007. Unfortunately, this problem was in Silverlight 1, and it’s still present in Silverlight 2.

I developed a work-around that is probably not ideal, but it does the job.

What I discovered is that each time a MediaElement is triggered, it will play fine one time. So my answer was to dynamically create a new MediaElement each time a key is pressed. The problem with this approach is cleanup. Once 125 or so are added, the application crashes.

Attaching a MediaEnded event to remove the MediaElement when the audio is done playing results in choppy or truncated audio. Instead, I elected to place the dynamically created MediaElements into a specific container on the main canvas, and use MediaEnded to check the number of children in that container.

When a preset threshold has been reached (the default is 25), I run a quick loop that deletes all the children on that canvas, effectively clearing out my cache of “used” MediaElements. This allows my sounds to play each time a key is pressed, and cleans itself up.

Codewise, my MediaElement is defined globally in the Page class as follows:
MediaElement _mediaElement;

When a key is pressed, I play the storyboard for the appropriate instance of the drum pad, instantiate the MediaElement that will hold the sound for that keypress, assign the sound to it, then call a function called “do_MediaElement” that handles tasks common to all key presses.

These steps look like this:

case Key.J:
crashPad.padHit.Begin();
_mediaElement = new MediaElement();
_mediaElement.Source = new Uri(crashDrumSound, UriKind.Relative);
do_MediaElement();
break;

The do_MediaElement function attaches a MediaEnded event to the newly created MediaElement, then adds it to the media element container. It then plays the sound.

void do_MediaElement()
{
_mediaElement.MediaEnded += new RoutedEventHandler(_mediaElement_MediaEnded);
mediaElementContainer.Children.Add(_mediaElement);
_mediaElement.Play();
}

Earlier I mentioned that I didn’t have luck using a MediaEnded event to remove the MediaElement when the sound finishes playing. Instead, it’s used to do a quick check on the number of children in the container I’m using. If the number of children is greater than or equal to the defined threshold, then it does a quick removal of the children on that canvas.

Note that the line that removes the children removes them from the bottom up - always removing the child element at index 0. This is because you can’t just directly count through the children as they are being removed without running into problems - the first time through there are 25 children, then 24, then 23, and so on. If I attempted to remove the node at position 25, it would fail since node 25 doesn’t exist after any node is removed. The loop has to compensate for the fact that it is causing the number of children to reduce each time through.

void _mediaElement_MediaEnded(object sender, RoutedEventArgs e)
{
int count = mediaElementContainer.Children.Count;

if (count >= cleaningThreshold)
{
for (int i = 0; i < cleaningThreshold; i++)
{
mediaElementContainer.Children.RemoveAt(0);
}
}
}

I have some ideas for expanding upon the app and may come back to it at some point in the future. For the time being though, it does what I set out to do, and my son seems to enjoy it quite a bit.