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.rhkb.cn/news/152366.html

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

相关文章

R | R包默认安装路径的查看及修改

R | R包默认安装路径的查看及修改 一、R包安装位置查看二、已安装R包查询三、R包安装位置修改四、R包安装位置永久修改 在【R: R package安装的几种方式】【R: R版本更新及R包迁移&#xff08;详细步骤&#xff09;】两篇文章中介绍过R包的常见安装方式&#xff0c;以及在不同R…

STM32实战项目——WIFI远程开关灯

前言 其实WIFI开关灯在几个月前就想做了&#xff0c;但是对于没有云平台调试经验的我&#xff0c;一开始有些摸不着头脑&#xff0c;所以就搁置了。十一假期与老同学聊天时了解到他也在做一个远程开关灯的小项目&#xff0c;所以就重新开始了WIFI远程开关灯的小项目。 本文使用…

学习Consul中踩过的坑

一、杀不死的consul 通过mac的homebrew安装了consul以后&#xff0c;手动启动consul报8300端口已被占用&#xff0c;通过lsof -i:8300和lsof -i:8500查看端口占用情况&#xff0c;发现consul已经启动了。然后手动kill -9对应的进程id&#xff0c;再启动consul&#xff0c;还是…

ChatGPT私有数据结合有什么效果?它难吗?

ChatGPT的出现可谓是惊艳了全世界&#xff0c;ChatGPT的问答能力通过了图灵测试&#xff0c;使其回答问题的方式与人类几乎无法区分。大家不甘于只在官方的对话页面问答&#xff0c;想利用 GPT 模型的自然语言能力结合私有数据开拓更多的应用场景。 | ChatGPT私有数据结合特点 …

[Java] 服务端消息推送汇总

前言&#xff1a;当构建实时消息推送功能时&#xff0c;选择适合的方案对于开发高效的实时应用至关重要。消息的推送无非就推、拉两种数据模型。本文将介绍四种常见的消息实时推送方案&#xff1a;短轮询&#xff08;拉&#xff09;、长轮训&#xff08;拉&#xff09;、SSE&am…

c++视觉处理---高斯滤波

高斯滤波处理 高斯滤波是一种常用的平滑滤波方法&#xff0c;它使用高斯函数的权重来平滑图像。高斯滤波通常用于去除噪声并保留图像中的细节。在OpenCV中&#xff0c;可以使用cv::GaussianBlur()函数来应用高斯滤波。 以下是cv::GaussianBlur()函数的基本用法&#xff1a; …

vue实现echarts中 9种 折线图图例

let datas [{ DivideScore: 7, UserScore: 7.2, Name: 目标制定 },{ DivideScore: 7, UserScore: 7, Name: 具体性 },{ DivideScore: 7, UserScore: 7.5, Name: 可衡量性 },{ DivideScore: 7, UserScore: 7, Name: 可实现性 },{ DivideScore: 7, UserScore: 7, Name: 时间限定…

简单强大的时序图绘制工具

今天分享一个简单强大的时序图绘制工具——WaveDrom。 WaveDrom Digital Timing Diagram everywhere WaveDrom draws your Timing Diagram or Waveform from simple textual description. It comes with description language, rendering engine and the editor. WaveDrom edi…

基于Springboot实现房屋租赁租房平台系统项目【项目源码+论文说明】

基于Springboot实现房屋租赁租房平台系统演示 摘要 在网络高速发展的时代&#xff0c;众多的软件被开发出来&#xff0c;给用户带来了很大的选择余地&#xff0c;而且人们越来越追求更个性的需求。在这种时代背景下&#xff0c;房东只能以用户为导向&#xff0c;所以开发租房网…

MongoDB-介绍与安装部署

介绍与安装部署 1.MongoDB简介a) 体系结构b) 数据模型c) MongoDB的特点c.1) 高性能c.2) 高性可用性c.3) 高拓展性c.4) 丰富的查询支持 2.单机部署a) Windows系统中的安装启动b) Shell连接(mongo命令)c) Linux系统中的安装启动和连接 1.MongoDB简介 MongoDB是一个开源、高性能、…

【网络安全入门】学习网络安全必须知道的100 个网络基础知识

前言 话不多说&#xff0c;完整的资料已经上传至CSDN官方&#xff0c;需要的可以点击链接自取【282G】网络安全&黑客技术零基础到进阶全套学习大礼包&#xff0c;免费分享&#xff01; 1 什么是链接? 链接是指两个设备之间的连接。它包括用于一个设备能够与另一个设备…

k8s containerd查看镜像

直接查看crictl image会报错&#xff1a; 1) crictl config runtime-endpoint unix:///run/containerd/containerd.sock 2) vi /etc/crictl.yaml 3) systemctl daemon-reload 此时&#xff0c;再查看image:

Python —— UI自动化之八大元素定位

1、基础元素定位 1、id定位 使用html中标签的id元素去定位&#xff0c;在一般定位中优先选择&#xff0c;举例&#xff1a; from time import sleep from selenium import webdriver from selenium.webdriver.common.by import Bydriver webdriver.Firefox() driver.get(&q…

CI/CD工具中的CI和CD的含义

CI/CD工具中的CI和CD的含义&#xff1f; CI/CD 是现代软件开发方法中广泛使用的一种方法。其中&#xff0c;CI 代表持续集成&#xff08;Continuous Integration&#xff09;&#xff0c;CD 则有两层含义&#xff0c;一是持续交付&#xff08;Continuous Delivery&#xff09;…

Pyside6 QPushButton

Pyside6 QPushButton QPushButton使用QPushButton继承关系QPushButton的函数(Function)和信号(Signal)QPushButton信号 QPushButton例程界面设计clicked信号测试pressed信号测试released信号测试toggled信号测试按键长按测试按键长按间隔测试完整程序界面程序主程序 按键或命令…

redis学习(二)——redis常见命令及基础数据类型

数据类型 基础数据类型 字符串 String abcMap集合 Hsah {name:“zhangsan”,age:18}列表 List [a, b, c, d]Set集合 Set {a,b,c}有序Set集合 SortSet {a:1,b:2,c:3} 特殊数据类型 GEO 地理坐标 {A:(100.2,35.1)}BitMap 位图&#xff0c;只存储0和1 01101011101HyperLog 基数…

详解CAN通信的标识符掩码和标识符列表两种过滤机制

CAN 通信的应用非常广泛&#xff0c;本文不涉及CAN通信的基础配置&#xff0c;重点分析一下STM32和GD32的CAN通信两种ID过滤方式。 首先&#xff0c;不管是STM32还是GD32&#xff0c;实现CAN通信ID过滤的机制和原理一定是一样的&#xff0c;只是用到的寄存器有差别。 1. ID过…

计算机视觉简介(1)

任何计算机视觉处理流程都始于成像系统&#xff0c;它从景物中捕获反射出来的光线&#xff0c;并将光信号转换成计算机可以读取和处理的图像格式 在计算机成像技术发展的早期&#xff0c;图像通过把胶卷或印刷图像素 化后获得&#xff1b;而现在图 像通常直接由数码相机获取&a…

消息驱动 —— SpringCloud Stream

Stream 简介 Spring Cloud Stream 是用于构建消息驱动的微服务应用程序的框架&#xff0c;提供了多种中间件的合理配置 Spring Cloud Stream 包含以下核心概念&#xff1a; Destination Binders&#xff1a;目标绑定器&#xff0c;目标指的是 Kafka 或者 RabbitMQ&#xff0…

记录一次springboot使用定时任务中@Async没有生效的场景

环境说明 jdk21springboot 3.0.11 springcloud 2022.0.0 spring-cloud-alibaba 2022.0.0.0 在开发一个定时触发的任务的时候&#xff0c;由于开发执行任务的函数比较耗费时间&#xff0c;所以采用异步解决问题。 发现并没有按照预期的触发 经询问后&#xff0c;发现当前类的…