C# 通过winmm枚举音频设备

news/2024/10/22 9:34:31/

文章目录

  • 前言
  • 一、如何实现?
    • 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.ppmy.cn/news/1145393.html

相关文章

win10通过导入注册表快速添加小鹤双拼

环境:win10 win10通过导入注册表快速添加小鹤双拼 fly.reg win10导入附件中的注册表,系统将会自动添加小鹤双拼方案,并将小鹤双拼方案设置为默认。 将下面的代码复制并保存到reg文件中 Windows Registry Editor Version 5.00[HKEY_CURRENT_USER\Software\Microsoft\InputMe…

大数据中的一些词汇解释

OLTP&#xff08;online Transaction Prrocessing&#xff09; OLTP是一个处理面向事务的数据的软件系统。术语“在线交易”是指实时完成活动&#xff0c;而不是批处理。此数据是结构化数据的常见来源&#xff0c;可作为许多分析过程的输入。OLTP交易讲究实时性&#xff0c;就…

Jenkins 构建时动态获取参数

文章目录 问题简介Groovy 脚本配置进阶 问题 在做jenkins项目时&#xff0c;有些参数不是固定写死的&#xff0c;而是动态变化的&#xff0c;这时我们可以用 Active Choices 插件来远程调用参数 问题解决方案&#xff1a;执行构建前使用Groovy Scrip调用本地脚本&#xff0c;…

Java8实战-总结40

Java8实战-总结40 用Optional取代null如何为缺失的值建模采用防御式检查减少 NullPointerExceptionnull 带来的种种问题其他语言中 null 的替代品 用Optional取代null 如何为缺失的值建模 假设需要处理下面这样的嵌套对象&#xff0c;这是一个拥有汽车及汽车保险的客户。 pub…

归纳所猜半结论推出完整结论:CF1592F1

https://www.luogu.com.cn/problem/CF1592F1 场上猜了个结论&#xff0c;感觉只会操作1。然后被样例1hack了。然后就猜如果 ( n , m ) (n,m) (n,m) 为1则翻转4操作&#xff0c;被#14hack了。然后就猜4操作只会进行一次&#xff0c;然后就不知道怎么做下去了。 上面猜的结论都…

vue js 实现页面在浏览器全屏切换

需求&#xff1a; 在浏览器中点击按钮实现页面的全屏与非全屏的切换。 如图&#xff1a; 全屏前&#xff1a; 全屏后&#xff1a; 具体实现代码如下&#xff1a; html&#xff1a; <template><div class"development-history" id"echarts-wrap&quo…

【数据结构】算法效率的度量方法

&#x1f984;个人主页:修修修也 &#x1f38f;所属专栏:数据结构 ⚙️操作环境:Visual Studio 2022 目录 &#x1f38f;事后统计方法 &#x1f38f;事前分析估算方法 &#x1f38f;函数的渐进式增长 结语 在上篇文章中我们提到了算法的设计要求中我们要尽量满足时间效率高…

Springcloud笔记(2)-Eureka服务注册

Eureka服务注册 服务注册&#xff0c;发现。 在Spring Cloud框架中&#xff0c;Eureka的核心作用是服务的注册和发现&#xff0c;并实现服务治理。 Eureka包含两个组件&#xff1a;Eureka Server和Eureka Client。 Eureka Server提供服务注册服务&#xff0c;各个节点启动后…