C# 通过winmm枚举音频设备

文章目录

  • 前言
  • 一、如何实现?
    • 1、添加依赖
    • (1)、nuget安装winmm的封装库
    • (2)、补充接口
    • 2、定义实体
    • 3、实现枚举
  • 二、完整代码
  • 三、使用示例
  • 总结


前言

使用C#做音频录制时需要获取音频设备信息,比如使用ffmpeg进行录制需要先获取音频设备名称,再Windows上获取音频设备的方法有不少,其中比较简单的就是使用winmm,这是一套比较旧的api但是使用方法简单,当然有个缺陷就是音频名称不能超过32个字符,超过会被截断,当然如果作为winmm的采集或播放配套使用则不会有问题。


一、如何实现?

需要先导入winmm的api,以及定义存放音频设备信息的实体,最后通过调用api实现枚举设备功能。

1、添加依赖

由于编写dllimport比较麻烦,而且nuget已经有封装好的库,所以没必要重复造轮子,直接nuget安装然后补充一些必要的接口即可。

(1)、nuget安装winmm的封装库

在这里插入图片描述

(2)、补充接口

由于Vanara.PInvoke.Multimedia对waveInGetDevCaps和waveOutGetDevCaps导入的有点问题,所以需要自己dllimport。还需要添加一些相关的枚举用于获取声音格式。

[DllImport("winmm.dll")]
public static extern MMRESULT waveInGetDevCapsW(uint uDeviceID, out WAVEINCAPS pwic, uint cbwic);
[DllImport("winmm.dll")]
public static extern MMRESULT waveOutGetDevCapsW(uint uDeviceID, out WAVEOUTCAPS pwoc, uint cbwoc);
public enum DWFormats
{WAVE_INVALIDFORMAT = 0x00000000,     /* invalid format */WAVE_FORMAT_1M08 = 0x00000001,    /* 11.025 kHz, Mono,   8-bit  */WAVE_FORMAT_1S08 = 0x00000002,    /* 11.025 kHz, Stereo, 8-bit  */WAVE_FORMAT_1M16 = 0x00000004,    /* 11.025 kHz, Mono,   16-bit */WAVE_FORMAT_1S16 = 0x00000008,    /* 11.025 kHz, Stereo, 16-bit */WAVE_FORMAT_2M08 = 0x00000010,    /* 22.05  kHz, Mono,   8-bit  */WAVE_FORMAT_2S08 = 0x00000020,    /* 22.05  kHz, Stereo, 8-bit  */WAVE_FORMAT_2M16 = 0x00000040,    /* 22.05  kHz, Mono,   16-bit */WAVE_FORMAT_2S16 = 0x00000080,    /* 22.05  kHz, Stereo, 16-bit */WAVE_FORMAT_4M08 = 0x00000100,    /* 44.1   kHz, Mono,   8-bit  */WAVE_FORMAT_4S08 = 0x00000200,    /* 44.1   kHz, Stereo, 8-bit  */WAVE_FORMAT_4M16 = 0x00000400,    /* 44.1   kHz, Mono,   16-bit */WAVE_FORMAT_4S16 = 0x00000800,    /* 44.1   kHz, Stereo, 16-bit */WAVE_FORMAT_44M08 = 0x00000100,     /* 44.1   kHz, Mono,   8-bit  */WAVE_FORMAT_44S08 = 0x00000200,     /* 44.1   kHz, Stereo, 8-bit  */WAVE_FORMAT_44M16 = 0x00000400,     /* 44.1   kHz, Mono,   16-bit */WAVE_FORMAT_44S16 = 0x00000800,     /* 44.1   kHz, Stereo, 16-bit */WAVE_FORMAT_48M08 = 0x00001000,     /* 48     kHz, Mono,   8-bit  */WAVE_FORMAT_48S08 = 0x00002000,     /* 48     kHz, Stereo, 8-bit  */WAVE_FORMAT_48M16 = 0x00004000,     /* 48     kHz, Mono,   16-bit */WAVE_FORMAT_48S16 = 0x00008000,     /* 48     kHz, Stereo, 16-bit */WAVE_FORMAT_96M08 = 0x00010000,     /* 96     kHz, Mono,   8-bit  */WAVE_FORMAT_96S08 = 0x00020000,     /* 96     kHz, Stereo, 8-bit  */WAVE_FORMAT_96M16 = 0x00040000,     /* 96     kHz, Mono,   16-bit */WAVE_FORMAT_96S16 = 0x00080000,     /* 96     kHz, Stereo, 16-bit */
}

2、定义实体

需要定义声音格式

class SampleFormat
{/// <summary>/// 声道数/// </summary>public ushort Channels { set; get; }/// <summary>/// 采样率/// </summary>public uint SampleRate { set; get; }/// <summary>/// 位深/// </summary>public ushort BitsPerSample { set; get; }
};

以及声音设备实体

class AudioDevice
{/// <summary>/// 设备Id/// </summary>public uint Id { set; get; }/// <summary>/// 设备名称/// </summary>public string Name { set; get; } = "";/// <summary>/// 声道数/// </summary>public int Channels { set; get; }/// <summary>/// 支持的格式/// </summary>public IEnumerable<SampleFormat>? SupportedFormats { set; get; }
};

3、实现枚举

音频采集设备

/// <summary>
/// 枚举声音采集设备
/// </summary>
public static IEnumerable<AudioDevice> WaveInDevices
{get{var deviceCount = waveInGetNumDevs();for (uint i = 0; i < deviceCount; i++){var sd = GetWaveInDeviceById(i);if (sd != null) yield return sd;}}
}
/// <summary>
/// 通过id获取声音采集设备信息
/// </summary>
/// <param name="id">设备id</param>
/// <returns>设备对象</returns>
public static AudioDevice? GetWaveInDeviceById(uint id)
{WAVEINCAPS pwic;//声音设备功能信息var result = waveInGetDevCapsW(id, out pwic, (uint)Marshal.SizeOf<WAVEINCAPS>());if (result != MMRESULT.MMSYSERR_NOERROR) return null;AudioDevice sd = new AudioDevice();sd.Id = id;sd.Channels = pwic.wChannels;sd.Name = pwic.szPname;sd.SupportedFormats = _GetSurportFormats(pwic.dwFormats);return sd;
}

音频播放设备

 /// <summary>/// 枚举声音播放设备/// </summary>public static IEnumerable<AudioDevice> WaveOutDevices{get{var deviceCount = waveOutGetNumDevs();for (uint i = 0; i < deviceCount; i++){var sd = GetWaveOutDeviceById(i);if (sd != null) yield return sd;}}}
/// <summary>
/// 通过id获取声音播放设备信息
/// </summary>
/// <param name="id">设备id</param>
/// <returns>设备对象</returns>
public static AudioDevice? GetWaveOutDeviceById(uint id)
{WAVEOUTCAPS pwic;//声音设备功能信息var result = waveOutGetDevCapsW(id, out pwic, (uint)Marshal.SizeOf<WAVEOUTCAPS>());if (result != MMRESULT.MMSYSERR_NOERROR) return null;AudioDevice sd = new AudioDevice();sd.Id = id;sd.Channels = pwic.wChannels;sd.Name = pwic.szPname;sd.SupportedFormats = _GetSurportFormats(pwic.dwFormats);return sd;
}

二、完整代码

.net 7.0

using System.Runtime.InteropServices;
using static Vanara.PInvoke.WinMm;
/************************************************************************
* @Project:  	AC::Winmm
* @Decription:  音频设备枚举
* @Verision:  	v1.0.0.0
* @Author:  	Xin Nie
* @Create:  	2023/10/8 09:27:00
* @LastUpdate:  2023/10/8 15:04:00
************************************************************************
* Copyright @ 2025. All rights reserved.
************************************************************************/
namespace AC
{class Winmm{/// <summary>/// 枚举声音采集设备/// </summary>public static IEnumerable<AudioDevice> WaveInDevices{get{var deviceCount = waveInGetNumDevs();for (uint i = 0; i < deviceCount; i++){var sd = GetWaveInDeviceById(i);if (sd != null) yield return sd;}}}/// <summary>/// 枚举声音播放设备/// </summary>public static IEnumerable<AudioDevice> WaveOutDevices{get{var deviceCount = waveOutGetNumDevs();for (uint i = 0; i < deviceCount; i++){var sd = GetWaveOutDeviceById(i);if (sd != null) yield return sd;}}}/// <summary>/// 通过id获取声音采集设备信息/// </summary>/// <param name="id">设备id</param>/// <returns>设备对象</returns>public static AudioDevice? GetWaveInDeviceById(uint id){WAVEINCAPS pwic;//声音设备功能信息var result = waveInGetDevCapsW(id, out pwic, (uint)Marshal.SizeOf<WAVEINCAPS>());if (result != MMRESULT.MMSYSERR_NOERROR) return null;AudioDevice sd = new AudioDevice();sd.Id = id;sd.Channels = pwic.wChannels;sd.Name = pwic.szPname;sd.SupportedFormats = _GetSurportFormats(pwic.dwFormats);return sd;}/// <summary>/// 通过id获取声音播放设备信息/// </summary>/// <param name="id">设备id</param>/// <returns>设备对象</returns>public static AudioDevice? GetWaveOutDeviceById(uint id){WAVEOUTCAPS pwic;//声音设备功能信息var result = waveOutGetDevCapsW(id, out pwic, (uint)Marshal.SizeOf<WAVEOUTCAPS>());if (result != MMRESULT.MMSYSERR_NOERROR) return null;AudioDevice sd = new AudioDevice();sd.Id = id;sd.Channels = pwic.wChannels;sd.Name = pwic.szPname;sd.SupportedFormats = _GetSurportFormats(pwic.dwFormats);return sd;}static List<SampleFormat> _GetSurportFormats(uint foramts){var sfs = new List<SampleFormat>();foreach (var j in Enum.GetValues<DWFormats>()){if ((foramts & (uint)j) != 0){var name = Enum.GetName(j)!.Split("_").Last();var sp = name.Substring(0, name.Length - 3);var ch = name.Substring(name.Length - 3, 1);var bp = name.Substring(name.Length - 2, 2);uint isp = 0;switch (sp){case "1": isp = 11025; break;case "2": isp = 22050; break;case "4": isp = 44100; break;case "44": isp = 44100; break;case "48": isp = 48000; break;case "96": isp = 96000; break;}sfs.Add(new SampleFormat() { Channels = (ushort)(ch == "S" ? 1 : 2), SampleRate = isp, BitsPerSample = ushort.Parse(bp) });}}return sfs;}public enum DWFormats{WAVE_INVALIDFORMAT = 0x00000000,     /* invalid format */WAVE_FORMAT_1M08 = 0x00000001,    /* 11.025 kHz, Mono,   8-bit  */WAVE_FORMAT_1S08 = 0x00000002,    /* 11.025 kHz, Stereo, 8-bit  */WAVE_FORMAT_1M16 = 0x00000004,    /* 11.025 kHz, Mono,   16-bit */WAVE_FORMAT_1S16 = 0x00000008,    /* 11.025 kHz, Stereo, 16-bit */WAVE_FORMAT_2M08 = 0x00000010,    /* 22.05  kHz, Mono,   8-bit  */WAVE_FORMAT_2S08 = 0x00000020,    /* 22.05  kHz, Stereo, 8-bit  */WAVE_FORMAT_2M16 = 0x00000040,    /* 22.05  kHz, Mono,   16-bit */WAVE_FORMAT_2S16 = 0x00000080,    /* 22.05  kHz, Stereo, 16-bit */WAVE_FORMAT_4M08 = 0x00000100,    /* 44.1   kHz, Mono,   8-bit  */WAVE_FORMAT_4S08 = 0x00000200,    /* 44.1   kHz, Stereo, 8-bit  */WAVE_FORMAT_4M16 = 0x00000400,    /* 44.1   kHz, Mono,   16-bit */WAVE_FORMAT_4S16 = 0x00000800,    /* 44.1   kHz, Stereo, 16-bit */WAVE_FORMAT_44M08 = 0x00000100,     /* 44.1   kHz, Mono,   8-bit  */WAVE_FORMAT_44S08 = 0x00000200,     /* 44.1   kHz, Stereo, 8-bit  */WAVE_FORMAT_44M16 = 0x00000400,     /* 44.1   kHz, Mono,   16-bit */WAVE_FORMAT_44S16 = 0x00000800,     /* 44.1   kHz, Stereo, 16-bit */WAVE_FORMAT_48M08 = 0x00001000,     /* 48     kHz, Mono,   8-bit  */WAVE_FORMAT_48S08 = 0x00002000,     /* 48     kHz, Stereo, 8-bit  */WAVE_FORMAT_48M16 = 0x00004000,     /* 48     kHz, Mono,   16-bit */WAVE_FORMAT_48S16 = 0x00008000,     /* 48     kHz, Stereo, 16-bit */WAVE_FORMAT_96M08 = 0x00010000,     /* 96     kHz, Mono,   8-bit  */WAVE_FORMAT_96S08 = 0x00020000,     /* 96     kHz, Stereo, 8-bit  */WAVE_FORMAT_96M16 = 0x00040000,     /* 96     kHz, Mono,   16-bit */WAVE_FORMAT_96S16 = 0x00080000,     /* 96     kHz, Stereo, 16-bit */}[DllImport("winmm.dll")]public static extern MMRESULT waveInGetDevCapsW(uint uDeviceID, out WAVEINCAPS pwic, uint cbwic);[DllImport("winmm.dll")]public static extern MMRESULT waveOutGetDevCapsW(uint uDeviceID, out WAVEOUTCAPS pwoc, uint cbwoc);}
}

三、使用示例

foreach (var i in Winmm.WaveInDevices)
{Console.WriteLine("音频采集设备" + i.Id + ":" + i.Name);
}
foreach (var i in Winmm.WaveOutDevices)
{    Console.WriteLine("音频播放设备"+i.Id+":" +i.Name);
}

在这里插入图片描述


总结

以上就是今天要讲的内容,使用winnm枚举设备还是比较简单的,唯一麻烦一点的地方就是支持格式的获取,但是通过C#使用字符串处理,实现也变得很简单。总的来说,这算是一种获取音频设备信息的方法,能够满足一些使用场景的需求。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/132663.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

[LitCTF 2023]导弹迷踪

这道题相较于其他的分数类型的js题有一点不一样&#xff0c;他不是像常规的有用bp多次抓包修改最后得分来获取flag的。 本题将flag藏到了他的前端文件中本身没有任何难度&#xff0c;只是为了记录一种新的做法 按照我们平常做js的思路就是先随便玩一下然后bp抓包看得分或者抓包…

解决github加载过慢问题

github打不开怎么办&#xff1f;看到这篇文章&#xff0c;一切都稳了&#xff01; DNS被污染&#xff0c;一句话&#xff0c;修改系统hosts文件&#xff01; 1.hosts文件在哪&#xff1f;C:\Windows\System32\drivers\etc 2.用记事本打开hosts&#xff0c;在最后加入以下两行…

【斗罗2】天梦哥抓捕冰帝,霍雨浩与她完美融合,喜提五挂

Hello,小伙伴们&#xff0c;我是小郑继续为大家深度解析斗罗大陆2绝世唐门。 斗罗大陆动画第二部绝世唐门第19集预告终于更新了&#xff0c;看到公布时间&#xff0c;大半夜才发布&#xff0c;着实不过瘾。不过剧情相当炸裂&#xff0c;天梦哥与冰帝对峙&#xff0c;造神计划正…

skywalking动态配置[集成nacos/apollo/consul]

说明:以下配置仅关于的阈值规则的动态配置,其他参数也可以进行配置。 1,skywalking动态配置集成nacos 编辑application.yml nacos配置参数如下: nacos:# Nacos Server HostserverAddr: 10.10.5.145# Nacos Server Portport: 8848# Nacos Configuration Groupgroup: skywal…

electron 升级 v22 遇到问题

Electron 漏洞 https://mp.weixin.qq.com/s/5LpSJb_5uV8EIDOl3fz9Tw 由于 23以上不在支持win 7 8 8.1 所以我选择安装 v22.3.24 electron 22.3.24 node-sass 6.0.1 sass-loader 10.4.1 对应的版本 npm i node-sass6.0.1 --sass_binary_sitehttps://npm.taobao.org/mirrors…

Springboot使用sqlcipher4加密sqlite数据库

在有些业务场景&#xff0c;需要使用sqlite数据库&#xff0c;但sqlite数据库生的db文件&#xff0c;是明文的&#xff0c;该文件被别人拿到&#xff0c;就可以看到里面的所有数据&#xff0c;非常不安全&#xff0c;市面上有很多对sqlite数据库文件加密的方式&#xff0c;但都…

布隆过滤器原理浅析

前言 熟悉Redis缓存应用的伙伴们都知道&#xff0c;Redis缓存存在缓存击穿、雪崩和穿透的问题&#xff0c;通常在解决缓存穿透问题时&#xff0c;除了缓存异常请求外&#xff0c;还有一个叫做布隆过滤器的方案。下面&#xff0c;我们认识下布隆过滤器。 结构&原理 布隆过…

适合自学的网络安全基础技能“蓝宝书”:《CTF那些事儿》

CTF比赛是快速提升网络安全实战技能的重要途径&#xff0c;已成为各个行业选拔网络安全人才的通用方法。但是&#xff0c;本书作者在从事CTF培训的过程中&#xff0c;发现存在几个突出的问题&#xff1a; 1&#xff09;线下CTF比赛培训中存在严重的 “最后一公里”问题 &#…

2023年电工(初级)证考试题库及电工(初级)试题解析

题库来源&#xff1a;安全生产模拟考试一点通公众号小程序 2023年电工&#xff08;初级&#xff09;证考试题库及电工&#xff08;初级&#xff09;试题解析是安全生产模拟考试一点通结合&#xff08;安监局&#xff09;特种作业人员操作证考试大纲和&#xff08;质检局&#…

2023年【危险化学品生产单位安全生产管理人员】及危险化学品生产单位安全生产管理人员模拟考试题

题库来源&#xff1a;安全生产模拟考试一点通公众号小程序 危险化学品生产单位安全生产管理人员考前必练&#xff01;安全生产模拟考试一点通每个月更新危险化学品生产单位安全生产管理人员模拟考试题题目及答案&#xff01;多做几遍&#xff0c;其实通过危险化学品生产单位安…

微信小程序报错request:fail -2:net::ERR_FAILED(生成中间证书)

微信小程序报错request:fail -2:net::ERR_FAILED-生成中间证书 前言一、检查网站ssl证书二、生成证书方法1.获取中间证书手动合并1.进入网站&#xff1a;[https://www.myssl.cn/tools/downloadchain.html](https://www.myssl.cn/tools/downloadchain.html)2.点击下一步3.手动合…

(十四)VBA常用基础知识:当前excel文件所有sheet循环,获取sheet名,获取最大行数最大列数

获取当前excel的所有sheet以及sheet名 Sub test()Dim ws As WorksheetFor Each ws In Worksheetsws.ActivateDebug.Print ws.NameNext ws End Sub 输出结果&#xff1a; Sheet1 Sheet2 Sheet3 Sheet4 Sheet52.获取最大行数 首先要明白取最大行的方式&#xff0c;为什么这样取…