Friday, May 8, 2009

Beat Box Application - Part 3: Playing Media Elements

In this part, I will talk about a tip I learned while working with media sources, and some insight I have gained when playing multiple media sources simultaneously. The source code for the complete project is on Codeplex. The complete source code for the beat box can be downloaded here:

http://drumpanel.codeplex.com/

Before this project, whenever I worked with MediaElements and media sources, I always loaded the sources dynamically from the ClientBin folder off of the server. This makes sense if the user is selecting from a number of sources or if the sources are large. However, in this application -- since the drum sound files are small and they are all likely to play at some time during the song -- I decided to load them as resources into the actual XAP file that is downloaded when the application is started. To do this you need to specify the file as a resource.



Then you need to load the source file into the MediaElement using the following code:



int i = 0;
string sSounds = "crash;hhclosed;hhopen;kick;snare;tom1;tom2;tom3";
foreach (string sKey in sSounds.Split(';'))
{
string sResource = string.Format(@"DrumPanel;component/sounds/{0}.mp3", sKey);
StreamResourceInfo oStream = Application.GetResourceStream(new Uri(sResource, System.UriKind.Relative));
Sounds.Add(i,oStream);

MediaElement oElement = new MediaElement();
Elements.Add(i, oElement);
LayoutRoot.Children.Add(oElement);
i++;
}


As for playing the sounds, the MediaElement is reading from a stream and the sound will start playing from the beginning. I have set AutoPlay = False so it will not play until I call the method Play. Also, I have learned through trial and error that in order to replay the sound, you first need to call the Stop method before you call the Play method again.

A drum machine plays a rhythm, which means on any beat, any one of the sounds can be played. Every sound has the same duration. If we were creating a piano synthesizer we would need to account for the duration of each note, like quarter notes and whole notes, but for drums this is not an issue.

Here is the code that plays all the sounds on each kit panel when the timer triggers a new beat. Each drum sound is stopped and then the active ones are played again on the next beat. The interesting thing about making music using software is you can do things you could never do in real life, like play all the drum sounds at once.


void Timer_Tick(object sender, EventArgs e)
{
PlayBeat();
}

public void PlayBeat()
{
MovingCursor++;
if (MovingCursor >= MaxCount)
MovingCursor = 0;
}
public void BackBeat()
{
MovingCursor--;
if (MovingCursor <= 0)
MovingCursor = MaxCount-1;
}

public int MovingCursor
{
get { return (int)GetValue(MovingCursorProperty); }
set { SetValue(MovingCursorProperty, value); }
}

public static readonly DependencyProperty MovingCursorProperty =
DependencyProperty.Register("MovingCursor", typeof(int), typeof(BeatPanel),
new PropertyMetadata(0,OnCursorChange));

protected static void OnCursorChange(DependencyObject obj, DependencyPropertyChangedEventArgs args)
{
BeatPanel oPanel = obj as BeatPanel;
oPanel.KitRest((int)args.OldValue);
oPanel.KitPlay((int)args.NewValue);
}



public void Play()
{
Background = new SolidColorBrush(Colors.Black);
for (int i = 0; i < 8; i++)
{
Ellipse oEllipse = Children[i] as Ellipse;
if (oEllipse.Opacity < .6)
continue;

StreamResourceInfo oInfo = Sounds[i];
MediaElement oElement = Elements[i];
oElement.SetSource(oInfo.Stream);
}
}

No comments: