所以我有一个参数化的线程启动,启动一些NAudio的东西...但我需要一个单独的线程告诉NAudio线程启动。当我尝试它时,它会崩溃。如何实现安全的跨线程通信?
这是线程(此类成为主线程中的音频对象):
class Audio
{
public IWavePlayer OutDevice;
public WaveStream OutStream;
public Thread thread;
public Audio(string file)
{
this.thread = new Thread(new ParameterizedThreadStart(InitAudio)); thread.Start(file);
}
private void InitAudio(object data) {
this.OutDevice = new WaveOut();
this.OutStream = new WaveChannel32(new Mp3FileReader(data.ToString()));
this.OutDevice.Init(OutStream);
}
}
我需要能够让我的主线程调用audio.OutDevice.Play();但我不能因为它是跨线程而且崩溃了。我该怎么做?
答案 0 :(得分:0)
您的音频播放使用的是硬件资源,而且通常情况下,必须在创建它们的同一个线程上访问这些资源。在您的代码中,您没有留下继续访问该线程的空间。我很惊讶你有任何代码的声音,因为你的拥有线程将很快超出范围。这只是因为你有那些与班级相关的变量发生了什么。我个人认为在这种情况下你不需要一个单独的线程,因为我认为回放是或可以是非阻塞的。尽管如此,你可以按照你的计划(伪代码):
public bool ShouldPlay;
private void InitAudio(object data) {
var OutDevice = new WaveOut();
var OutStream = new WaveChannel32(new Mp3FileReader(data.ToString()));
var OutDevice.Init(OutStream);
while(!_exitEvent.WaitOne(50ms)){
if(ShouldPlay && !OutDevice.IsPlaying)
OutDevice.Play();
else if(!ShouldPlay && OutDevice.IsPlaying)
OutDevice.Stop();
}
OutDevice.Stop();
OutDevice.Dispose();
OutStream.Dispose();
}
答案 1 :(得分:0)
如果要在后台线程上填充音频缓冲区,我建议使用WaveOutEvent类。这将创建自己的后台线程,用于通过管道拉动音频。然后,您可以从主线程调用Play和Stop。在这种情况下需要注意的主要线程问题是,如果需要在正在播放的文件中实现重新定位,因为重新定位将在GUI线程上发生,而Read可能正在另一个上进行。
对于WinForms / WPF应用程序,最可靠的机制是使用WaveOut的Windows消息回调(如果在GUI线程上创建WaveOut,默认情况下会得到这个)。这样可以避免担心要重新定位的线程。此外,通过使用GUI线程实现一切,它可以避免潜在的MTA与STA线程问题,如果您使用基于COM的API(如Windows Media API,MediaFoundation或DirectX Media Object重采样器),您将获得这些问题。