【问题标题】:How to use System.Media.SoundPlayer to asynchronously play a sound file?如何使用 System.Media.SoundPlayer 异步播放声音文件?
【发布时间】:2010-11-12 18:31:16
【问题描述】:

这是一个看似简单的问题:

在 Windows 窗体中异步播放嵌入式 .wav 资源文件的正确方法是什么?

尝试 #1:

var player = new SoundPlayer();
player.Stream = Resources.ResourceManager.GetStream("mySound");
player.Play(); // Note that Play is asynchronous
  • 好:不会阻塞 UI 线程
  • 错误:SoundPlayer 和嵌入式 资源流不会立即 弃置。

尝试 #2:

using (var audioMemory = Resources.ResourceManager.GetStream("mySound"))
{
    using (var player = new SoundPlayer(audioMemory))
    {
        player.Play();
    }
}
  • 很好:UI 线程没有被阻塞,SoundPlayer 和音频内存流立即被释放。
  • 错误:比赛条件! Play() 是异步的,如果音频内存在 Play 完成之前被释放......繁荣!抛出运行时异常。

尝试 #3:

using (var audioMemory = Resources.ResourceManager.GetStream("mySound"))
{
    using (var player = new SoundPlayer(audioMemory))
    {
        player.PlaySync();
    }
}
  • 很好:立即释放播放器和音频流。
  • 错误:PlaySync 阻塞了 UI 线程

尝试 #4:

ThreadPool.QueueUserWorkItem(ignoredState =>
  {
    using (var audioMemory = Resources.ResourceManager.GetStream("mySound"))
    {
        using (var player = new SoundPlayer(audioMemory))
        {
            player.PlaySync();
        }
    }
  });
  • 很好:UI 没有冻结,播放器和内存流立即被释放。
  • 不好:因为这经常触发,我们可能会用完线程池线程!请参阅 Larry Osterman 的 what's wrong with this code part 26

SoundPlayer 似乎应该有一个 PlayAsyncCompleted 事件。不幸的是,不存在这样的事件。我错过了什么吗?在 Windows 窗体中异步播放 .wav 嵌入资源的正确方法是什么?

【问题讨论】:

    标签: c# winforms audio


    【解决方案1】:

    我没有足够的声誉来发表评论,所以我只会回答。

    如果您对播放声音的要求“看似简单”(您只想在单个 winform 用户执行某项操作时偶尔播放声音),那么我将使用上面的尝试 #4。

    Larry Osterman 的“此代码第 26 部分有什么问题”让他的“系统”在每次击键时都分离出一个新的线程池线程(以播放声音)。他指出,在大约 15 秒的输入时间内,默认的 500 个线程池大小已经饱和,但这也是使用异步 RPC 的客户端/服务器应用程序也使用线程池的情况。真的不是一个“看似简单”的应用程序。

    如果您尝试每秒(或更快)将声音字节排队 10 秒或 100 秒,那么它真的不是一个“简单的应用程序”,排队的线程/优先级子系统可能是有序的。

    【讨论】:

    • 感谢您的反馈。我的应用程序是具有异步 RPC 的客户端/服务器应用程序,当远程用户发送一些消息时会播放声音。不幸的是,这正是奥斯特曼所描述的那种问题。我的应用程序并不简单,但是,我确实认为“从嵌入式资源异步播放声音文件”是一个简单的场景。
    • 我同意“从嵌入式资源异步播放声音文件”是一个简单的场景,但您需要在远程用户发送消息时播放声音(假设您可以获得大量消息)不是...所以如果声音已经在播放并且有新消息到达,您是否要(a)忽略该消息,因为声音已经在播放?如果是这样,请不要使用线程池,只需使用命名线程,如果线程已经在运行,则不要执行任何操作。如果线程没有运行,请启动它。
    • (b) 让“SAME”声音再次播放?如果是这样,每次收到消息时,增加一个共享变量(或者,如果为零,则启动线程)。在线程池线程中,每次播放消息时,都会减少循环中的变量。 (c) 排队不同的声音?您必须建立一个队列来“保留”要播放的声音。收到消息时将声音放在队列中,播放时将其剥离
    • 感谢您的建议。我已将您的答案标记为答案。
    【解决方案2】:

    我仍然使用来自 win32 API 的优秀的 waveOut____ 函数。这是一个很好的代码示例:

    http://www.codeproject.com/KB/audio-video/cswavplay.aspx

    编辑:解决您的问题的一个更简单的方法是提取嵌入的资源,将其作为真实文件保存在某处,然后使用 SoundPlayer 播放该文件。有点笨重,但很简单,不会有资源处理问题。

    【讨论】:

    • 使用waveOut,你可以异步播放声音并在完成后处理音频流吗?
    • 当然。链接中的代码正是这样做的(除非它没有——我可能给了你错误的链接)。 waveOut____ 是旧时的 API,你可以用它做任何你喜欢的事情。相对过于复杂,但功能强大。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2013-03-12
    • 2014-05-09
    • 2010-09-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多