NAudio WaveOut.PlaybackState 属性未更新

Posted

技术标签:

【中文标题】NAudio WaveOut.PlaybackState 属性未更新【英文标题】:NAudio WaveOut.PlaybackState property is not updated 【发布时间】:2018-01-02 14:28:48 【问题描述】:

使用 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。

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


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

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

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

加入backgroundChannel线程没有问题,但从来没有加入foregroundChannels中唯一的线程。将第 3 行与第 5-6 行反转具有相同的结果,系统性地失败为pt.Join();。这个 PlayerThread 是点击退出时创建的。


UPDATE 2 这不是线程问题。这只是因为条件internalPlayer.PlaybackState == PlaybackState.Playing 并不总是满足(使用断点检查),所以我们只停留在while 循环中。目前正在寻找更好的方法来检查播放已经结束。

【问题讨论】:

您是否尝试在RunThread 中设置断点以查看卡住的位置? 是的,背景音乐线程执行到最后。 “再见”的声音没有。不知道为什么我一直在修改值和断点,但没有任何改变。 : 程序员被 Dispatcher.Invoke() 所吸引,就像飞蛾扑火一样。永远,永远支持 BeginInvoke()。调用可能会导致死锁,只需要 UI 线程停止调度或被其他东西占用。两者都发生在这里,当主窗口关闭时它停止调度。并且在调用 Thread.Join() 时被其他东西占用。 nocallback = false; 并没有实际实现,无论如何都应该是 = true 。使用 Debug > Windows > Threads 来诊断死锁。 @HansPassant 感谢您的宝贵建议,我将其更改为 BeginInvoke 并有适当的代表。至于nocallback = false;,它是通过soundEngine.WaveGoodbye(); 实现的,它将nocallback 设置为false。这很有用,因为回调实际上检查队列中是否有另一个背景音乐(使用 backgroundChannel 时),而我们在退出时不希望这样。唉,问题仍然存在,因为所有这些都不能解决退出的问题,因为当声音停止播放时 WaveOut 的 PlaybackState 属性并不总是改变(我使用断点检查了这个)...... 【参考方案1】:

WaveChannel32 构造函数创建一个零填充缓冲区,这对于您向混音器馈送的场景很有用。在这种情况下,它只是导致我的流“永不”结束。

添加:

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

【讨论】:

【参考方案2】:

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

【讨论】:

试图这样做,但和以前一样,“foregroundChannel”PlayerThread 永远不会结束,因此永远不会加入调用者线程(更新了我的原始帖子)。

以上是关于NAudio WaveOut.PlaybackState 属性未更新的主要内容,如果未能解决你的问题,请参考以下文章

NAudio 的问题

NAudio 演示不再工作

NAudio:正确使用 MixingSampleProvider 和 VolumeSampleProvider

NAudio 和 Midi 文件读取

来自流的NAudio?播放音频

NAudio,Windows服务,多线程