SoundPlayer.Stop不会停止播放声音 [英] SoundPlayer.Stop does not stop sound playback

查看:343
本文介绍了SoundPlayer.Stop不会停止播放声音的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我在C#编写起着小.wav文件的应用程序。它使用声音播放类的System.Media namepace来播放声音,使用调用线程的 SoundPlayer.PlaySync 方法以播放.wav文件。在一个类中,看起来像这样这一切都结束了:

I have an application written in C# which plays little .wav files. It uses the SoundPlayer class in the System.Media namepace to play the sounds, using a thread that calls the SoundPlayer.PlaySync method to play the .wav file. It's all wrapped up in a class that looks like 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() {
        do {
            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();
        }
    }
}

在我的程序调用 PlaySound 方法,并有完善的当前播放的停止方法被调用,但正在播放的声音并没有真正停止。我已经放在轨迹点在调用停止我之后添加了一行刚刚所以调用时我可以看到,当它返回,一边用耳机听。很明显,声音播放一路过关斩将到最后。

When my program calls the PlaySound method, and a sound is currently playing, the Stop method is called, but the sound that's playing doesn't actually stop. I've placed trace points on the call to Stop and a line I added after it just so I could see when the call was made and when it returned, while listening with headphones. It's obvious that the sound plays all the way through to the end.

我如何得到的声音停止播放可靠?

How do I get the sounds to stop playing reliably?

推荐答案

不幸的是,这是一个混乱的过程,但这个作品:

Unfortunately this is a messy process, but this works:

 class Program
    {
        static void Main(string[] args)
        {
            SoundPlayerEx player = new SoundPlayerEx(@"c:\temp\sorry_dave.wav");
            player.SoundFinished += player_SoundFinished;

            Console.WriteLine("Press any key to play the sound");
            Console.ReadKey(true);
            player.PlayAsync();

            Console.WriteLine("Press a key to stop the sound.");
            Console.ReadKey(true);
            player.Stop();

            Console.WriteLine("Press any key to 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();
                }
                finally
                {
                    OnSoundFinished();
                }

            }, _ct);            
        }

        public new void Stop()
        {
            if (_playingAsync)
                _tokenSource.Cancel();
            else
                _base.Stop();
        }

        protected virtual void OnSoundFinished()
        {
            Finished = true;
            _playingAsync = false;

            EventHandler handler = SoundFinished;

            if (handler != null)
                handler(this, EventArgs.Empty);
        }
    }

那么,为什么它的工作正常?其公知的问题。在声音播放是射后不理这块code,而如果你不取消它在同一个线程,你开始了它,它不会做任何事情。很多人抱怨和我敢肯定,你见过有极少数的解决方案在使用侧面生 wave_out 来电或移动到DirectX(或WPF ,使用的MediaPlayer 控制)。

So why doesn't it work "normally"? Its a well known problem. The SoundPlayer is a "fire and forget" piece of code, and if you don't cancel it on the same thread that you started it on, it will not do anything. A lot of people complain about it and as I'm sure you've seen there are very few solutions out side of using raw wave_out calls or moving to DirectX (or with WPF, using the MediaPlayer control).

SoundPlayerEx 类有让你知道当声音结束或取消播放您异步启动声音的几个属性。有没有必要建立一个新的线程上下工夫,使人们更方便使用。

This SoundPlayerEx class has a couple properties that let you know when the sound is finished or to cancel playing a sound that you started asynchronously. There is no need to create a new thread to work on, making it a lot easier to use.

随意在code扩展,这是一个快速和肮脏的解决您的问题。你需要这两个类是SoundInfo类和SoundPlayerEx类,上面code的其余部分是一个演示(替换用你自己的WAV文件)。

Feel free to expand on the code, it was a quick and dirty solution to your problem. The two classes you need are the SoundInfo class and the SoundPlayerEx class, the rest of the code above is a demo (replace the wav file with one of your own).

注意这不是一个通用的解决方案,因为它依赖于WINMM.DLL,所以这不会端口到单(不知道单声道有一个声音播放类与否)。此外,由于它是一个肮脏的解决方案,你会不会,如果你不使用 PlayAsync获得已完成事件或财产通话。

Note this is not a universal solution as it relies on the winmm.dll, so this will not port over to Mono (not sure if Mono has a SoundPlayer class or not). Also since its a dirty solution, you won't get the Finished event or property if you don't use the PlayAsync call.

这篇关于SoundPlayer.Stop不会停止播放声音的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆