NAudio WaveOut.PlaybackState属性未更新

时间:2018-01-02 14:28:49

标签: c# wpf multithreading naudio background-foreground

使用C#中的WPF框架编写应用程序(我真的很陌生,不要太苛刻我的编码风格哈哈)。当我退出时,我希望播放一些“再见”音频(使用NAudio 1.8.4从MPQ文件中读取流),即使主窗口消失也不要中断;因此我在名为PlayerThread的类中使用前景线程。

我使用PlayerThread构造函数播放声音

 foregroundChannel = new PlayerThread(soundStream, null, null);

但是我以后没有跟踪它,我只是依靠PlayerThread在游戏后“自然地”结束。

这是PlayerThread构造函数:

public PlayerThread(MpqFileStream stream, Action<PlayerThread> over, Dispatcher callerDispatcher)
    {
        noCallback = false;
        dispatcher = callerDispatcher;
        fadeOut = false;
        soundStream = stream;
        ThreadStart threadStart = RunThread;
        if (callerDispatcher != null)
        {
            callback = over;
            threadStart += () =>
            {
                dispatcher.Invoke(() =>
                {
                    Cleanup();
                });
            };
        }
        this.m_playerThread = new Thread(RunThread)
        {
            Name = "Audio Player Thread",
            IsBackground = false
        };
        this.m_playerThread.SetApartmentState(ApartmentState.STA);
        this.m_playerThread.Start();
    }

这是它的工作:

private void RunThread()
    {
        internalPlayer = new WaveOut();
        Mp3FileReader mp3FileReader = new Mp3FileReader(soundStream);
        inputStream = new WaveChannel32(mp3FileReader);
        internalPlayer.Init(inputStream);
        internalPlayer.Play();
        while (internalPlayer.PlaybackState == PlaybackState.Playing && !fadeOut)
        {
            System.Threading.Thread.Sleep(100);
        }
        if (fadeOut)
        {
            while (((float)0F).CompareTo(inputStream.Volume) < 0)
            {
                System.Threading.Thread.Sleep(150);
                inputStream.Volume -= 0.1F;
            }
        }
        internalPlayer.Dispose();
        inputStream.Dispose();
        mp3FileReader.Dispose();
        soundStream.Dispose(); // Yeah I'm so desperate for a solution I'm just Disposing everything even though I don't think it helps
    }

单击退出时,将执行以下行:

soundEngine.FadeOutBackground();
soundEngine.PlayVoice(SoundNamesConstants.BYE);
soundEngine.WaveGoodbye();
Close();
soundEngine.WaitForTheCrewBeforeWeighingAnchor();

最后,这是Cleanup()中的代码:

if (!noCallback)
            callback.Invoke(this);

这里的回调非常无关紧要,我用它来切换音乐与淡出效果。另外,在这种情况下不会发生,因为我只是将noCallback设置为false。

如果有用,这是资源使用情况图表。红线是我按退出的时候。

resource usage chart

更新 根据@sacha的建议添加Thread.Join()同步:

public void WaitForTheCrewBeforeWeighingAnchor() // yes, every word matters
    {
        backgroundChannel.Join();

        foreach (PlayerThread pt in foregroundChannels)
            pt.Join();
    }

没有问题加入backgroundChannel线程但从未加入foregroundChannels中唯一的线程。使用第5-6行反转第3行具有相同的结果,系统地未能pt.Join();。此PlayerThread是单击退出时创建的。

更新2 这不是线程问题。这只是发生,因为并不总是满足条件internalPlayer.PlaybackState == PlaybackState.Playing(使用断点检查),所以我们只是保持while循环。目前正在寻找更好的方法来检查比赛结束。

2 个答案:

答案 0 :(得分:1)

WaveChannel32构造函数创建一个零填充缓冲区,这对于您进入混音器的场景非常有用。在这种情况下,它只是导致我的流到&#34;从来没有&#34;端。

添加:

inputStream.PadWithZeroes = false;

完全解决了我的问题。在这种情况下,并不需要加入。

了解更多信息:

  

永不结束的流问题

     

IWavePlayer的每个实现者确定它的方式   应该在Read方法上自动停止播放   source IWaveProvider返回0(实际上Read应该总是返回   count参数,除非已到达流的末尾。)

     

但是,NAudio中有一些WaveStreams / IWaveProviders   从不停止返回音频。这不是一个错误 - 这是很正常的   某些场景的行为。也许BufferedWaveProvider是最好的   例如 - 如果没有足够的话,它将返回一个零填充的缓冲区   排队的数据用于播放。这对于从中流式传输非常有用   网络中数据可能无法快速到达但您却没有   希望播放停止。类似地,WaveChannel32有一个名为的属性   PadWithZeroes允许您打开或关闭此行为,这可以   适用于您进入调音台的场景。

     

来自:http://mark-dot-net.blogspot.be/2011/05/naudio-and-playbackstopped-problem.html

答案 1 :(得分:0)

听起来你需要同步你的线程,主线程应该在退出之前等待其他线程完成。请查看Thread.Joinhttps://msdn.microsoft.com/en-us/library/95hbf2ta(v=vs.110).aspx