C#3.0怎么搭配Windows原生的组件播放ogg音频文件?(wav也是可以的,但我想尽量使用ogg)

2026-08-07T06:53:43.2533333+00:00

这是dll,制作的有点拉跨...


using System;
using System.Media;
using System.IO;

namespace AudioTool
{
    public class WavHelper
    {
        public static byte[] ReadWavToMemory(string filePath)
        {
            return File.ReadAllBytes(filePath);
        }
        public static TimeSpan? GetWavTimeFromBuffer(byte[] buffer)
        {
            if (buffer == null || buffer.Length < 44)
            {
                return null;
            }
            using (MemoryStream stream = new MemoryStream(buffer))
            using (BinaryReader br = new BinaryReader(stream))
            {
                stream.Seek(24, SeekOrigin.Begin);
                int sampleRate = br.ReadInt32();

                stream.Seek(28, SeekOrigin.Begin);
                int bytesPerSecond = br.ReadInt32();

                stream.Seek(40, SeekOrigin.Begin);
                uint dataSize = br.ReadUInt32();

                double seconds = (double)dataSize / bytesPerSecond;
                return TimeSpan.FromSeconds(seconds);
            }
        }
        public static SoundPlayer CreateInMemoryPlayer(byte[] wavBytes)
        {
            MemoryStream ms = new MemoryStream(wavBytes);
            SoundPlayer player = new SoundPlayer(ms);
            for (int i = 0; i < 10; i++)
            {
                Console.WriteLine(string.Format("正在第 {0} 次播放", i + 1));
                player.PlaySync();
            }
            player.Load();
            return player;
        }
    }
}

这是我最后调用dll的程序:

using System;
using System.Media;
using System.Reflection;
using System.Threading;

namespace AudioTest
{
    public class Program
    {
        public static void Main()
        {
            Assembly asm = Assembly.LoadFrom(@"G:\视觉小说游戏制作挑战\4.锐核奇点 编辑器制作\一个程序播放两个音频\DLL.dll");
            Type helperType = asm.GetType("AudioTool.WavHelper");

            MethodInfo readMethod = helperType.GetMethod("ReadWavToMemory");
            MethodInfo getTimeMethod = helperType.GetMethod("GetWavTimeFromBuffer");
            MethodInfo createPlayer = helperType.GetMethod("CreateInMemoryPlayer");

            string audioPath = @"G:\视觉小说游戏制作挑战\4.锐核奇点 编辑器制作\一个程序播放两个音频\真厉害好棒啊你.wav";

            // 第一次执行播放
            Console.WriteLine("第1次播放");
            byte[] buffer1 = (byte[])readMethod.Invoke(null, new object[] { audioPath });
            TimeSpan? dur1 = (TimeSpan?)getTimeMethod.Invoke(null, new object[] { buffer1 });
            if (dur1.HasValue)
            {
                Console.WriteLine(string.Format("音频毫秒时长:{0}", (long)dur1.Value.TotalMilliseconds));
            }
            SoundPlayer pl1 = (SoundPlayer)createPlayer.Invoke(null, new object[] { buffer1 });
            pl1.PlaySync();
            pl1.Dispose();

            // 停顿 1000 毫秒(1秒)
            Console.WriteLine("等待1秒");
            Thread.Sleep(1000);

            // 第二次执行播放
            Console.WriteLine("第2次播放");
            byte[] buffer2 = (byte[])readMethod.Invoke(null, new object[] { audioPath });
            TimeSpan? dur2 = (TimeSpan?)getTimeMethod.Invoke(null, new object[] { buffer2 });
            if (dur2.HasValue)
            {
                Console.WriteLine(string.Format("音频毫秒时长:{0}", (long)dur2.Value.TotalMilliseconds));
            }
            SoundPlayer pl2 = (SoundPlayer)createPlayer.Invoke(null, new object[] { buffer2 });
            pl2.PlaySync();
            pl2.Dispose();
        }
    }
}

Windows 开发 | Windows API - Win32
0 个注释 无注释

问题作者接受的答案
Taki Ly (WICLOUD CORPORATION) 5,055 信誉分 Microsoft 外部员工 审查方
2026-08-07T08:52:23.96+00:00

您好 @电脑先生工作室 ,

感谢您的提问,也感谢您提供了完整的代码示例。

先说明一下大致情况:Windows 系统本身并不自带 Ogg/Vorbis 解码器,因此仅依靠系统原生组件来播放 .ogg 并不是平台开箱即用支持的做法。根据您后续打算如何分发这个项目,可以考虑以下几个方向。

为什么目前的做法只能停留在 .wav

System.Media.SoundPlayer 的文档说明它用于控制来自 .wav 文件的声音播放,并且只处理未压缩的 PCM。如果传入的流不是 PCM .wavPlay() 会引发 InvalidOperationException。这大概可以解释为什么 Ogg 数据无法传给它,压缩格式的 WAV(例如 ADPCM 或 GSM 6.10)也是同样的情况。可参阅 SoundPlayer 类SoundPlayer.Play 方法

Media Foundation 作为当前的原生媒体框架,其原生支持的格式列表中同样没有列出 Ogg 容器和 Vorbis。可参阅 Media Foundation 中支持的媒体格式支持的编解码器

1. 在机器上添加编解码器

Microsoft 在 Microsoft Store 上提供了 Web 媒体扩展 扩展包,它会为 Windows 增加 OGG 容器解析器以及 Vorbis/Theora 解码器。安装之后,Ogg 内容即可通过原生管线播放。需要权衡的是,这会成为依赖具体机器的前提条件,如果您打算分发这款游戏,这种方式可能不太实用。如果您走这条路并使用 MCI,请留意 MCI 目前已属于旧版 API,新代码建议改用 MediaPlayer。可参阅 MCI 命令字符串和消息

2. 在应用程序内部解码为 PCM

托管的 Vorbis 解码器可以在内存中把 Ogg 流转换为 PCM,随后再把这个 PCM 流交给 SoundPlayer。这样所有依赖都包含在程序内部,不受最终用户系统配置的影响;对于需要对外分发的项目而言,通常是更可控的选择。这个社区讨论描述的正是这种做法,其中包括如何把解码器包装成 SoundPlayer 可以直接接受的流。

3. 使用专门的音频库

对于视觉小说引擎,您很可能还需要多音轨叠加播放、循环播放和音量控制,而这些 SoundPlayer 都不提供。NAudio 或许值得评估一下。

4. 直接使用 PCM WAV 分发

如果这些素材只是较短的语音或提示音,在构建阶段完成转换可以让代码保持简单,主要代价就是文件体积。

关于代码示例的一点观察

与格式问题无关,CreateInMemoryPlayer 先调用了十次 PlaySync(),然后才调用 Load() 并返回 player,而调用方又执行了一次 PlaySync(),因此每次调用实际会播放十一次。把"创建"与"播放"分开,并把 Load() 放在播放之前,应该更接近您想要的行为。另外 MemoryStream 没有被释放,由于 SoundPlayer 会持有该流的引用,它需要在 player 的整个生命周期内保持有效,并与 player 一同释放。

请告诉我哪个方向更符合您的项目情况,特别是这款游戏是否会分发到其他机器上,我很乐意进一步展开说明。

如果我的回复对您有帮助或有参考价值,恳请您按照此指南进行确认。

非常感谢。

此答案是否有帮助?

2 个人认为此答案很有帮助。

0 个其他答案

排序依据: 最早

你的答案

提问者可以将答案标记为“已接受”,审查方可以将答案标记为“已推荐”,这有助于用户了解答案是否解决了提问者的问题。