i have application written in c# plays little .wav files. uses soundplayer
class in system.media namepace play sounds, using thread calls soundplayer.playsync
method play .wav file. it's wrapped in class looks this:
public class soundcontroller { private object soundlocker = new object(); protected thread soundthread { get; set; } protected string nextsound { get; set; } protected autoresetevent playasoundplag { get; set; } protected dictionary<string, soundplayer> sounds { get; set; } protected bool stopping { get; set; } public string soundplaying { get; private set; } public soundcontroller() { pendingcount = 0; playasoundflag = new autoresetevent( false ); sounds = new dictionary<string, soundplayer>(); soundlocker = new object(); stopping = false; soundthread = new thread( new threadstart( soundplayer ) ) { name = "soundthread", isbackground = true }; soundthread.start(); } private void soundplayer() { { playasoundflag.waitone(); bool soundwasplayed = false; while ( !stopping && nextsound != null ) { lock ( soundlocker ) { soundplaying = nextsound; nextsound = null; } sounds[ soundplaying ].playsync(); lock ( soundlocker ) { soundplaying = null; soundwasplayed = true; } } } while ( !stopping ); } public bool hassound( string key ) { return sounds.containskey( key ); } public void playalarmsound( string key, bool stopcurrentsound ) { if ( !sounds.containskey( key ) ) throw new argumentexception( "sound unknown", "key" ); lock ( soundlocker ) { nextsound = key; if ( soundplaying != null && stopcurrentsound ) sounds[ soundplaying ].stop(); playasoundflag.set(); } } }
when program calls playsound
method, , sound playing, stop
method called, sound that's playing doesn't stop. i've placed trace points on call stop
, line added after see when call made , when returned, while listening headphones. it's obvious sound plays way through end.
how sounds stop playing reliably?
unfortunately messy process, works:
class program { static void main(string[] args) { soundplayerex player = new soundplayerex(@"c:\temp\sorry_dave.wav"); player.soundfinished += player_soundfinished; console.writeline("press key play sound"); console.readkey(true); player.playasync(); console.writeline("press key stop sound."); console.readkey(true); player.stop(); console.writeline("press key continue"); } static void player_soundfinished(object sender, eventargs e) { console.writeline("the sound finished playing"); } } public static class soundinfo { [dllimport("winmm.dll")] private static extern uint mcisendstring( string command, stringbuilder returnvalue, int returnlength, intptr winhandle); public static int getsoundlength(string filename) { stringbuilder lengthbuf = new stringbuilder(32); mcisendstring(string.format("open \"{0}\" type waveaudio alias wave", filename), null, 0, intptr.zero); mcisendstring("status wave length", lengthbuf, lengthbuf.capacity, intptr.zero); mcisendstring("close wave", null, 0, intptr.zero); int length = 0; int.tryparse(lengthbuf.tostring(), out length); return length; } } public class soundplayerex : soundplayer { public bool finished { get; private set; } private task _playtask; private cancellationtokensource _tokensource = new cancellationtokensource(); private cancellationtoken _ct; private string _filename; private bool _playingasync = false; public event eventhandler soundfinished; public soundplayerex(string soundlocation) : base(soundlocation) { _filename = soundlocation; _ct = _tokensource.token; } public void playasync() { finished = false; _playingasync = true; task.run(() => { try { double lenms = soundinfo.getsoundlength(_filename); datetime stopat = datetime.now.addmilliseconds(lenms); this.play(); while (datetime.now < stopat) { _ct.throwifcancellationrequested(); //the delay helps reduce processor usage while "spinning" task.delay(10).wait(); } } catch (operationcanceledexception) { base.stop(); } { onsoundfinished(); } }, _ct); } public new void stop() { if (_playingasync) _tokensource.cancel(); else base.stop(); //to stop soundplayer wave file } protected virtual void onsoundfinished() { finished = true; _playingasync = false; eventhandler handler = soundfinished; if (handler != null) handler(this, eventargs.empty); } }
so why doesn't work "normally"? known problem. soundplayer
"fire , forget" piece of code, , if don't cancel on same thread started on, not anything. lot of people complain , i'm sure you've seen there few solutions out side of using raw wave_out
calls or moving directx (or wpf, using mediaplayer
control).
this soundplayerex
class has couple properties let know when sound finished or cancel playing sound started asynchronously. there no need create new thread work on, making lot easier use.
feel free expand on code, quick , dirty solution problem. 2 classes need soundinfo class , soundplayerex class, rest of code above demo (replace wav file 1 of own).
note not universal solution relies on winmm.dll, not port on mono (not sure if mono has soundplayer
class or not). since dirty solution, won't finished
event or property if don't use playasync
call.