2

所以我有一个参数化的线程启动,它初始化了一些 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(); 但我不能,因为它是跨线程的并且它崩溃了。我该怎么做?

4

2 回答 2

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();
    }
于 2013-02-28T03:30:36.313 回答
0

如果您希望在后台线程上填充音频缓冲区,我建议使用 WaveOutEvent 类。这将创建自己的后台线程,用于通过管道提取音频。然后,您可以从主线程调用 Play 和 Stop。在这种情况下,您需要注意的主要线程问题是,如果您需要在正在播放的文件中实现重新定位,因为重新定位将发生在 GUI 线程上,而读取可能在另一个线程上进行。

对于 WinForms/WPF 应用程序,最可靠的机制是使用 WaveOut 的 windows 消息回调(如果您在 GUI 线程上创建 WaveOut,则默认情况下会得到这个)。这样就无需担心要重新定位的线程。此外,通过对所有内容使用 GUI 线程,它可以避免潜在的 MTA 与 STA 线程问题,如果您使用基于 COM 的 API,例如 Windows Media API、MediaFoundation 或 DirectX 媒体对象重采样器,就会遇到这些问题。

于 2013-02-28T11:59:22.723 回答