65.9K
CodeProject 正在变化。 阅读更多。
Home

C# 中的底层音频播放器

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.87/5 (87投票s)

2002年12月12日

3分钟阅读

viewsIcon

981826

downloadIcon

16591

本文介绍了一个使用 C# 中的 waveout API 的示例应用程序。

Sample Image - cswavplay.gif

引言

.NET Framework 不包含任何处理声音的类,这已不是什么新闻。一些人通过将高级系统组件(如 Windows Media Player 或 DirectShow)封装到 .NET 友好型库中来规避此限制。但是,大多数这些库的设计方式使其无法用于即时操作音频样本,因为它们不让您访问实际的声音数据。

在开发处理此类低级问题的应用程序时,最常用的技术是 DirectSound 和 waveout API。本文介绍了一个示例应用程序,该应用程序通过 Interop 在 C# 中使用 waveout API 来循环播放 WAV 文件。

使用代码

示例应用程序中的大部分工作由两个类完成:WaveStreamWaveOutPlayer

WaveStream 类扩展 System.IO.Stream 并实现了从 WAV 文件读取音频样本所需的代码。构造函数读取文件头并提取所有相关信息,包括流的实际长度和音频样本的格式,这些信息通过 Format 属性公开。

需要注意的一点是,WaveStream 类中的搜索操作是相对于声音数据流进行的。这样您就不必关心标头长度或流末尾不属于音频数据块的那些额外字节。搜索到 0 将导致下一个读取操作从音频数据的开头开始。

WaveOutPlayer 类是发生最有趣事情的地方。为了简单起见,该类的接口已减少到严格的最小值。没有 Start、Stop 或 Pause 方法。创建 WaveOutPlayer 的实例将导致系统立即开始流式传输。

让我们看看创建 WaveOutPlayer 实例的代码。如您所见,构造函数接受五个参数

private void Play()
{
    Stop();
    if (m_AudioStream != null)
    {
        m_AudioStream.Position = 0;
        m_Player = new WaveLib.WaveOutPlayer(-1, m_Format, 16384, 3, 
            new WaveLib.BufferFillEventHandler(Filler));
    }
}

第一个参数是您要使用的波形设备的 ID。值 -1 表示默认的系统设备,但是如果您的系统有多个声卡,则可以传递从 0 到已安装声卡数量减一之间的任何数字来选择特定设备。

第二个参数是音频样本的格式。在此示例中,该格式直接从波形流中获取。

第三个和第四个参数是内部波形缓冲区的大小和要分配的缓冲区数量。您应该将这些设置为合理的值。较小的缓冲区将为您提供较小的延迟,但是如果您的计算机不够快,则音频可能会卡顿。

第五个也是最后一个参数是一个委托,该委托将定期调用,因为内部音频缓冲区播放完毕,以便您可以向它们提供新的声音数据。在示例应用程序中,我们只是从波形流中读取音频数据,如下所示

private void Filler(IntPtr data, int size)
{
    byte[] b = new byte[size];
    if (m_AudioStream != null)
    {
        int pos = 0;
        while (pos < size)
        {
            int toget = size - pos;
            int got = m_AudioStream.Read(b, pos, toget);
            if (got < toget)
                m_AudioStream.Position = 0; // loop if the file ends
            pos += got;
        }
    }
    else
    {
        for (int i = 0; i < b.Length; i++)
            b[i] = 0;
    }
    System.Runtime.InteropServices.Marshal.Copy(b, 0, data, size);
}

请注意,此委托可能会从 WaveOutPlayer 对象创建的任何内部线程调用,因此,如果您想调用您的 Windows 窗体对象,则应使用 Invoke 机制。

要停止播放,只需对播放器对象调用 Dispose,如下所示

private void Stop()
{
    if (m_Player != null)
    try
    {
        m_Player.Dispose();
    }
    finally
    {
        m_Player = null;
    }
}

结论

此示例演示了如何从 C# 使用 waveout API。与其它更高级别的库相比,这对于需要更多控制音频流的应用程序非常有用。典型的例子是那些即时生成或修改音频样本的应用程序,例如数字效果处理器。

此示例的修改版本(实现了对 DirectX 插件的支持)包含在 Adapt-X SDK 中,这是一个商业产品,可以在 www.chronotron.com 上找到。

历史

更新 03 年 8 月 28 日 -修复了与读取 WAV 文件头相关的错误。

© . All rights reserved.