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 属性未更新的主要内容,如果未能解决你的问题,请参考以下文章