Posts Tagged ‘WinForms’

How do I – Play the TNG Red Alert (or any audio file for that matter) in my WinForms C# app

Written by Cornelius J. van Dyk on . Posted in Blog, How Do I...

Out of the box, C# and .NET supports the playing of any .WAV file.  The code is pretty straight forward.  We simply need to reference the system media class to create a SoundPlayer{} object thus:

System.Media.SoundPlayer p = new System.Media.SoundPlayer(@"C:\Media\MySoundEffect.wav");
Once we have the object, we simply invoke the Play() method thus:
p.Play();

So in two simple lines of code, we are able to play any .wav file from our application.  There are some more embedded problems that may not immediately be obvious.  Allow me to explain.

Suppose I’m trying to add the Star Trek TNG Red Alert sound to my app for an emergency notification.  I begin by locating the sound in question.  A quick web search takes me to the http://www.trekcore.com/audio/ site where the Red Alert section has several samples to choose from.  Once I locate the sound sample that I wish to use, I click the link and download the file.

image

PROBLEM #1:  The file is in the .mp3 audio format.  Attempting to use it in the instantiation of the SoundPlayer() method fails with the message that only .wav files are supported.

RESOLUTION We could use a custom media player class, or we could just let the internet do some work for us.  A quick search pointed me to the http://media.io/ online audio conversion web site.  The simple interface couldn’t be cleaner or more straight forward:

image

Simply take the .mp3 file we previously downloaded and upload and convert it on this site.  Now we finally have a .wav file that we can use in our project.  Now we would like the alert sound to be played 3 times and a popup window present the error notification.  That’s easy, right?  We simply use a for() loop that looks like this:

for (int i = 0; i <= 2; i++)
{
    System.Media.SoundPlayer p = new System.Media.SoundPlayer(@"TNG-Red-Alert.wav");
    p.Play();
}
MessageBox.Show("There is an error!", "ERROR", MessageBoxButtons.OK, MessageBoxIcon.Error);

We compile and run the app and expect the alert sound to play 3 times… only… it doesn’t.  So why doesn’t it play our audio?  It seems the problem is related to the fact that the asynchronous .Play() method, being spun from the current executing thread, does not have enough time to execute before the modal .Show() method is executed.  As you know, the MessageBox.Show() method will block the existing thread from executing any further until user action is taken on the window.  As such, the Play() method is queued really quickly given present computer CPU speeds, and once the.Show() method is invoked, it aborts the asynchronous threads to the .Play() method.  To solve the problem we have to allow the .Play() method some time to begin execution.  The easiest way to do that is to simply .Sleep() the current thread.  By injecting this line of code:

System.Threading.Thread.Sleep(1500);

right after the .Play() call, the currently executing thread will pause for 1.5 seconds.  Depending on what audio you’re playing, you can tweak this value (in milliseconds) to match accordingly.

At this point, the alert message and audio work correctly… but the OCD in me still isn’t happy with the effect we get.  When the error condition is generated, the audio alert is played three times, per our code, and then the error alert is popped up.  The problem I have with that is that the audio warning gets my attention, but then I have to wait for it to finish playing three times before the error message with useful information pops up.  That should be easy to fix, right?  Why not just simply move the .Show() method call to before the for() loop containing the .Play() call?

Unfortunately, this doesn’t work as you’d expect.  Because of the fact that the .Show() call actually freezes the current thread, the message box is displayed and then awaits user interaction before it allows the audio code to execute.  What we need is for the .Show() code to be spun off in another thread so as to allow the audio alert to play while the message is displayed.  To do this, we create a new method thus:

public void ShowAlert()
{
    System.Threading.Thread t = new System.Threading.Thread(() =>
    {
        MessageBox.Show("There is an error!", "ERROR", MessageBoxButtons.OK, MessageBoxIcon.Error);
    });
    t.Start();
}

Once we have the message box contained in a separate thread, we simply make that call prior to playing our audio thus:

ShowAlert();
for (int i = 0; i <= 2; i++)
{
    System.Media.SoundPlayer p = new System.Media.SoundPlayer(@"TNG-Red-Alert.wav");
    p.Play();
    System.Threading.Thread.Sleep(1500);
}

Once the user interacts with the message box, the thread (t) will terminate gracefully.  In the mean time our main app is free to continue execution of our loop to play the alert sound.



Cheers
C




image