Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to use System.Media.SoundPlayer to asynchronously play a sound file?

Tags:

c#

winforms

audio

Here's a deceptively simple question:

What is the proper way to asynchronously play an embedded .wav resource file in Windows Forms?

Attempt #1:

var player = new SoundPlayer();
player.Stream = Resources.ResourceManager.GetStream("mySound");
player.Play(); // Note that Play is asynchronous
  • Good: doesn't block the UI thread
  • Bad: SoundPlayer and the embedded resource stream are not immediately disposed.

Attempt #2:

using (var audioMemory = Resources.ResourceManager.GetStream("mySound"))
{
    using (var player = new SoundPlayer(audioMemory))
    {
        player.Play();
    }
}
  • Good: UI thread is not blocked, SoundPlayer and audio memory stream are immediately disposed.
  • Bad: Race condition! Play() is async, and if audio memory gets disposed before Play is done...boom! Runtime exception is thrown.

Attempt #3:

using (var audioMemory = Resources.ResourceManager.GetStream("mySound"))
{
    using (var player = new SoundPlayer(audioMemory))
    {
        player.PlaySync();
    }
}
  • Good: Player and audio stream are immediately disposed.
  • Bad: PlaySync blocks the UI thread

Attempt #4:

ThreadPool.QueueUserWorkItem(ignoredState =>
  {
    using (var audioMemory = Resources.ResourceManager.GetStream("mySound"))
    {
        using (var player = new SoundPlayer(audioMemory))
        {
            player.PlaySync();
        }
    }
  });
  • Good: UI doesn't freeze, player and memory stream are immediately disposed.
  • Bad: Because this fires often, we may run out of thread pool threads! See Larry Osterman's what's wrong with this code part 26.

It seems like SoundPlayer should have a PlayAsyncCompleted event. Unfortunately, no such event exists. Am I missing something? What's the proper way to asynchronously play a .wav embedded resource in Windows Forms?

like image 696
Judah Gabriel Himango Avatar asked Jul 21 '09 19:07

Judah Gabriel Himango


2 Answers

I don't have enough reputation to comment so I'll just answer.

If your requirements to play sound are "deceptively simple" (you just want to play the occasional sound when a single winform user does something) then I would use Attempt #4 above.

Larry Osterman's "what's wrong with this code part 26" has his "system" spin off a new threadpool thread (to play sound) with each keystroke. He indicates than hammering away on it saturated the default 500 thread pool size in about 15 seconds of typing but this was also with a client/server app using async RPC that were also using the threadpool. Really not a "deceptively simple" application.

If you are trying to queue sound bytes every second (or faster) for 10s or 100s of seconds at a time then its really not a "simple application" and a queued threading/priority subsystem would probably be in order.

like image 195
BlueShepherd Avatar answered Oct 13 '22 06:10

BlueShepherd


I still use the good ol' waveOut____ functions from the win32 API. Here's a good code sample:

http://www.codeproject.com/KB/audio-video/cswavplay.aspx

Edit: a much simpler solution to your problem is to extract the embedded resource, save it as a real file somewhere, and then use SoundPlayer to play the file. A little clunky, but simple and you won't have the resource disposal problem.

like image 36
MusiGenesis Avatar answered Oct 13 '22 05:10

MusiGenesis