c# 通过WinAPI播放PCM声音

下面就是一篇详细讲解“c# 通过WinAPI播放PCM声音”的完整攻略。

1. 背景知识

在开始讲解之前,我们需要了解一些背景知识:PCM(Pulse Code Modulation)脉冲编码调制,是一种数字音频编码方式,将模拟声音信号转换为数字信号,并以数字的形式进行存储和传输。在WinAPI中,我们可以使用waveOut API来播放PCM声音。

2. WaveOut API

我们可以使用waveOut API来播放PCM声音,该API提供了一些函数来完成播放操作,如以下几个函数:

  • waveOutOpen:打开设备,以便播放声音。
  • waveOutClose:关闭设备。
  • waveOutPrepareHeader:准备音频数据并将其添加到播放缓冲区。
  • waveOutUnprepareHeader:回收缓冲区。
  • waveOutWrite:将准备好的音频数据写入缓冲区并播放。

3. 实现步骤

下面是通过WinAPI播放PCM声音的实现步骤:

  1. 调用waveOutOpen函数打开设备,这个函数需要传入一个WAVEFORMATEX结构体参数,这个结构体包含了声音的格式信息,如每秒采样率、每个采样点占用的位数、每个采样点的声道数等。

  2. 准备音频数据并将其添加到播放缓冲区中,需要以下几个步骤:

a. 先使用GlobalAlloc函数在进程的全局堆中分配一块内存。

b. 填充WAVEHDR结构体,并将数据保存到这个结构体中。这里需要注意的是,每个缓冲区的大小要小于等于设备的缓冲区大小,不然会导致声音卡顿或者播放不了。

c. 调用waveOutPrepareHeader函数准备音频数据,并将其添加到播放缓冲区中。

  1. 调用waveOutWrite函数将缓冲区的音频数据写入设备,并开始播放声音。

  2. 播放完声音后,调用waveOutUnprepareHeader函数回收音频缓冲区和WAVEHDR结构体的内存。

  3. 最后,调用waveOutClose函数关闭设备,释放相应资源。

4. 示例说明

下面是两个示例来说明如何使用WinAPI播放PCM声音。

示例一

这个示例演示如何从一个PCM文件中读取音频数据并播放,具体的实现步骤如下:

  1. 打开PCM文件并读取其中的音频数据。

csharp
byte[] data = File.ReadAllBytes("test.pcm");

  1. 调用waveOutOpen函数打开设备,并填写WAVEFORMATEX结构体参数。

```csharp
WAVEFORMATEX format = new WAVEFORMATEX();
format.nSamplesPerSec = 44100;
format.wBitsPerSample = 16;
format.nChannels = 2;
format.wFormatTag = WAVE_FORMAT_PCM;
format.nBlockAlign = (short)(format.nChannels * format.wBitsPerSample / 8);
format.nAvgBytesPerSec = format.nSamplesPerSec * format.nBlockAlign;

IntPtr hWaveOut = IntPtr.Zero;
waveOutOpen(out hWaveOut, WAVE_MAPPER, format, IntPtr.Zero, 0, CALLBACK_NULL);
```

  1. 将音频数据准备好,并填充到WAVEHDR结构体中。

```csharp
IntPtr pData = Marshal.AllocHGlobal(data.Length);
Marshal.Copy(data, 0, pData, data.Length);

var header = new WAVEHDR();
header.dwBufferLength = data.Length;
header.lpData = pData;

waveOutPrepareHeader(hWaveOut, ref header, Marshal.SizeOf(header));
```

  1. 调用waveOutWrite函数将缓冲区的音频数据写入设备,开始播放声音。

csharp
waveOutWrite(hWaveOut, ref header, Marshal.SizeOf(header));

  1. 播放完声音后,释放相应资源。

csharp
waveOutReset(hWaveOut);
waveOutUnprepareHeader(hWaveOut, ref header, Marshal.SizeOf(header));
Marshal.FreeHGlobal(pData);
waveOutClose(hWaveOut);

示例二

这个示例演示如何使用录音设备将麦克风的声音录制下来,并使用WinAPI进行实时播放。具体的实现步骤如下:

  1. 打开录音设备,并填写WAVEFORMATEX结构体参数。

```csharp
WAVEFORMATEX format = new WAVEFORMATEX();
format.nSamplesPerSec = 44100;
format.wBitsPerSample = 16;
format.nChannels = 2;
format.wFormatTag = WAVE_FORMAT_PCM;
format.nBlockAlign = (short)(format.nChannels * format.wBitsPerSample / 8);
format.nAvgBytesPerSec = format.nSamplesPerSec * format.nBlockAlign;

IntPtr hWaveIn = IntPtr.Zero;
waveInOpen(out hWaveIn, WAVE_MAPPER, format, IntPtr.Zero, 0, CALLBACK_NULL);
```

  1. 准备音频数据并将其添加到录音缓冲区中。

```csharp
const int BUFFER_SIZE = 16384; // 缓冲区大小

byte[] data = new byte[BUFFER_SIZE];
GCHandle hData = GCHandle.Alloc(data, GCHandleType.Pinned);

var header = new WAVEHDR();
header.lpData = hData.AddrOfPinnedObject();
header.dwBufferLength = BUFFER_SIZE;
header.dwFlags = 0;

waveInPrepareHeader(hWaveIn, ref header, Marshal.SizeOf(header));
waveInAddBuffer(hWaveIn, ref header, Marshal.SizeOf(header));
```

  1. 开始录音。

csharp
waveInStart(hWaveIn);

  1. 调用waveOutOpen函数打开播放设备,并填写WAVEFORMATEX结构体参数。
    csharp
    IntPtr hWaveOut = IntPtr.Zero;
    waveOutOpen(out hWaveOut, WAVE_MAPPER, format, IntPtr.Zero, 0, CALLBACK_NULL);

  2. 在录音过程中,不断从录音缓冲区中获取音频数据,并将数据添加到播放缓冲区中。

```csharp
while (true)
{
uint flags = 0;

   waveInUnprepareHeader(hWaveIn, ref header, Marshal.SizeOf(header));
   waveInPrepareHeader(hWaveIn, ref header, Marshal.SizeOf(header));
   waveInAddBuffer(hWaveIn, ref header, Marshal.SizeOf(header));

   waveInGetPosition(hWaveIn, out uint position, Marshal.SizeOf<uint>());

   while ((flags & WHDR_DONE) == 0)
   {
       waveOutGetPosition(hWaveOut, out uint waveOutPosition, Marshal.SizeOf<uint>());

       if (waveOutPosition - position >= BUFFER_SIZE)
           continue;

       waveOutWrite(hWaveOut, ref header, Marshal.SizeOf(header));
       waveOutGetPosition(hWaveOut, out waveOutPosition, Marshal.SizeOf<uint>());

       waveInGetErrorText(waveInGetError(hWaveIn), sb, sb.Capacity);
       Console.WriteLine(sb.ToString().Trim());
       flags = header.dwFlags;
   }

}
```

  1. 在录音完成后,释放相应资源。

```csharp
waveInStop(hWaveIn);
waveInReset(hWaveIn);
waveInUnprepareHeader(hWaveIn, ref header, Marshal.SizeOf(header));

waveOutReset(hWaveOut);
waveOutUnprepareHeader(hWaveOut, ref header, Marshal.SizeOf(header));
waveOutClose(hWaveOut);

hData.Free();
```

以上就是使用WinAPI播放PCM声音的完整攻略,示例代码仅供参考。如果你需要播放其他格式的声音,比如MP3、WAV等,可以使用相应的解码库先将其解码成PCM数据再进行播放。

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:c# 通过WinAPI播放PCM声音 - Python技术站

(0)
上一篇 2023年5月17日
下一篇 2023年5月17日

相关文章

  • Python md5与sha1加密算法用法分析

    Python md5与sha1加密算法用法分析 简介 md5和sha1是两种常用的加密算法,可以用于加密密码、保护数据安全等等。Python中提供了标准库hashlib来支持这两种算法。该库支持多种加密算法,除了md5和sha1之外还包括sha256、sha512等算法。 md5加密算法 首先,我们需要将要加密的字符串转换为二进制格式。可以用encode()…

    云计算 2023年5月18日
    00
  • 基于docker和cri-dockerd部署k8sv1.26.3

      cri-dockerd是什么?   在 Kubernetes v1.24 及更早版本中,我们使用docker作为容器引擎在k8s上使用时,依赖一个dockershim的内置k8s组件;k8s v1.24发行版中将dockershim组件给移除了;取而代之的就是cri-dockerd(当然还有其它容器接口);简单讲CRI就是容器运行时接口(Containe…

    云计算 2023年4月17日
    00
  • 云计算的基本概念

    云计算的基本概念   ”云”这个词已经被说得烂到不能再烂了。云计算,云平台,云+端,云服务,云……但与很多行业里的朋友聊天发现,其实大家对云计算到底是怎么个玩意,并不是太了解。作者今天为大家梳理一下,各种各样的“云”,葫芦里都在卖什么药。   云是网络、互联网的一种比喻说法,计算可以理解为计算机,因此云计算的基本模型,就是远程计算服务:用户通过网络连接到计算…

    云计算 2023年4月10日
    00
  • 华为亮相KubeCon EU 2023 新云原生开源项目Kuasar推动“云上演进”

    摘要:协力同行、拥抱开源,解放数字生产力,为社会和行业带来更多价值。 在数字时代,如果说企业是一艘巨大的货船,那么云原生则为企业的每一个业务、每一个应用提供了标准化的集装箱,摆脱笨重的底层桎梏,打造新一代云操作系统,驾驶这轮“货船”航向数字化的未来世界。 4月18日—21日,一年一度的云原生开源领域顶级峰会KubeCon & CloudNativeC…

    云计算 2023年4月25日
    00
  • Serverless冷启动:如何让函数计算更快更强?

    摘要:借助Serverless计算,开发者仅需上传业务代码并进行简单的资源配置便可实现服务的快速构建部署,云服务商则按照函数服务调用量和实际资源使用收费,从而帮助用户实现业务的快速交付和低成本运行。 本文分享自华为云社区《Serverless冷启动:如何让函数计算更快更强?》,作者:DevAI 。 问题背景 Serverless计算也称服务器无感知计算或函数…

    云计算 2023年4月17日
    00
  • 调用无文档说明的 Web API过程描述

    调用无文档说明的 Web API 过程可以分为以下几个步骤: 1. 网络抓包获取 API 接口 首先需要在浏览器的开发者工具或者网络抓包工具上进行抓包。找到需要调用的 API 接口地址,并记录下来。 2. 请求方式与参数 请求方式一般为 GET 或 POST,需要根据具体情况进行选择。 在请求时,需要将请求需要的参数传递给 API 接口。通过分析 API 接…

    云计算 2023年5月17日
    00
  • python列表操作之extend和append的区别实例分析

    下面是“python列表操作之extend和append的区别实例分析”的详细攻略: 概述 在Python中,列表是一种非常常见的数据类型,它可以存储一系列的元素,并支持各种常见的操作。 其中,列表添加元素是常见的操作之一,而在列表中,有两种添加元素的方法,分别是extend()和append()。 两种方法都可以向列表中添加元素,但它们有不同的工作原理和适…

    云计算 2023年5月18日
    00
  • 云栖直播《云计算时代的企业容灾体系及能力建设精讲》(下)PPT资料整理

    云栖直播《云计算时代的企业容灾体系及能力建设精讲》(下)PPT资料整理 非常荣幸,今年三月份受阿里云MVP团队邀请,在云栖直播平台和钉钉平台上做了两期直播节目,与阿里云MVP以及全国各地ACE开发者一起分享了我对云计算时代的企业容灾体系及能力建设的一些认识。做完节目之后,许多同学给了我很多意见和建议,让我受益良多,非常感谢大家的支持和鼓励!针对大家比较集中的…

    云计算 2023年4月13日
    00
合作推广
合作推广
分享本页
返回顶部