C# 通过winmm枚举音频设备

news2024/10/5 17:26:33

文章目录

  • 前言
  • 一、如何实现?
    • 1、DllImport接口
    • (1)、方法
    • (2)、结构体
    • 2、定义实体
    • 3、实现枚举
  • 二、完整代码
  • 三、使用示例
  • 总结


前言

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


一、如何实现?

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

1、DllImport接口

导入相关的api

(1)、方法

[DllImport("winmm.dll")]
public static extern uint waveInGetNumDevs();
[DllImport("winmm.dll")]
public static extern uint waveOutGetNumDevs();
[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);

(2)、结构体

[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
public struct WAVEINCAPS
{
    public ushort wMid;
    public ushort wPid;
    public uint vDriverVersion;
    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)]
    public string szPname;
    public uint dwFormats;
    public ushort wChannels;
}

[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
public struct WAVEOUTCAPS
{
    public ushort wMid;
    public ushort wPid;
    public uint vDriverVersion;
    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)]
    public string szPname;
    public uint dwFormats;
    public ushort wChannels;
    public WAVECAPS dwSupport;
}
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;
}


二、完整代码

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;
    }
    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
    public struct WAVEINCAPS
    {
        public ushort wMid;
        public ushort wPid;
        public uint vDriverVersion;
        [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)]
        public string szPname;
        public uint dwFormats;
        public ushort wChannels;
    }

    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
    public struct WAVEOUTCAPS
    {
        public ushort wMid;
        public ushort wPid;
        public uint vDriverVersion;
        [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)]
        public string szPname;
        public uint dwFormats;
        public ushort wChannels;
        public WAVECAPS dwSupport;
    }
    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 uint waveInGetNumDevs();
    [DllImport("winmm.dll")]
    public static extern uint waveOutGetNumDevs();
    [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.coloradmin.cn/o/1069539.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

PMP该如何备考?

我觉得最主要的就是需要打造属于自己的学习计划&#xff0c;因为每个人的学习能力是不一样的&#xff0c;没有好的学习方法&#xff0c;就会导致学习不好&#xff0c;最终获不得成绩&#xff0c;拿不下证书。 所以接下来就说一下我自己的一些学习方法&#xff0c;如对你有用的…

家政小程序开发|家政预约维修保洁系统搭建

家政预约小程序开发&#xff0c;简单易用家政服务公司小程序&#xff0c;客户&#xff0b;员工&#xff0b;派单&#xff0b;合同&#xff0b;财务&#xff0b;营销获客一键搞定&#xff01; 那么家政小程序都有什么功能&#xff0c;今天我就给大家介绍下&#xff1b; 1、地理…

自学(黑客)技术方法 必看 ——网络安全

如果你想自学网络安全&#xff0c;首先你必须了解什么是网络安全&#xff01;&#xff0c;什么是黑客&#xff01;&#xff01; 1.无论网络、Web、移动、桌面、云等哪个领域&#xff0c;都有攻与防两面性&#xff0c;例如 Web 安全技术&#xff0c;既有 Web 渗透2.也有 Web 防…

智能视频监控,究竟“智”在哪里?

当人们一提到智能视频监控时&#xff0c;就会想起高清摄像头、人脸识别等技术。其实不然&#xff0c;真正智能视频监控不仅仅是这些技术算法&#xff0c;更重要的是如何将这些算法融入到应用场景中&#xff0c;更好地去服务大众、起到降本增效的作用。 首先&#xff0c;智能视…

Nginx支持SNI证书,已经ssl_server_name的使用

整理了一些网上的资料&#xff0c;这里记录一下&#xff0c;供大家参考 什么是SNI&#xff1f; 传统的应用场景中&#xff0c;一台服务器对应一个IP地址&#xff0c;一个域名&#xff0c;使用一张包含了域名信息的证书。随着云计算技术的普及&#xff0c;在云中的虚拟机有了一…

自定义无边框窗口

效果&#xff1a; 可拖动拉伸 ui&#xff1a;设计如下 样式表&#xff1a;在ui CustomDialog 里设置的 #widget_title{background: #E6F1EB;border-top-left-radius: 20px;border-top-right-radius: 20px;}#widget_client{background-color: rgb(255, 255, 255);border-bottom…

游乐园票务小程序商城的作用是什么

游乐园是众多儿童喜欢的场所&#xff0c;尤其大城市&#xff0c;场所多且规模大&#xff0c;成年人也会前往&#xff0c;对园方来说自然是好的&#xff0c;然而在实际经营中&#xff0c;也会面临一些痛点。 通过【雨科】平台制作游乐园商城&#xff0c;电脑手机端小程序端打造品…

小说推文和短剧推广的收益模式

先说授权方式&#xff0c;可以使用”巨量推文“进行授权 申请授权后怎么获取收益呢 小说推文分为cpa拉新和cps推广的形式 cpa拉新的价格大概未4-10多块钱一个固定价格&#xff0c;cps则按充值比例进行分成&#xff0c;cps的充值分成比例大概60%-90%左右 短剧推广也是一样分…

低功耗对于IOT来说是必备技术吗?

万物互联的时代&#xff0c;现代人已普遍接受电视、音箱等电器设备具备智能化能力&#xff0c;也是在这个趋势下&#xff0c;我们身边越来越多的 iot 设备联网和交互成为刚需。 但 iot 设备也面临到一些非常显著的痛点&#xff0c;例如iot设备的内存、处理器等核心元件无法与手…

【单元测试】如何使用 JUnit5 框架?

JUnit5 单元测试框架使用教程 一、Junit5 是什么&#xff1f; Junit5是一个用于在Java平台上进行单元测试的框架。JUnit 5 框架主要由三部分组成&#xff1a;JUnit Platform、JUnit Jupiter 和 JUnit Vintage。 JUnit Platform&#xff1a;定义了测试引擎的 API&#xff0c;是…

python代码封装二进制文件并使用C#调用方案

思路 首先使用Cython库将python代码生成二进制文件pyd&#xff0c;然后使用C#中的pythonnet的Nuget包来进行调用&#xff0c;python代码中可以使用第三方类库。 Cython使用 Cython的安装 在命令行中使用如下语句即可安装Cython pip install cythonpyd文件格式 Cython用于…

服务器中勒索病毒怎么解决?勒索病毒解密,数据恢复

服务器中勒索病毒是一件低频、高概率的事情。而且一旦用户的服务器中招以后&#xff0c;想要处理无论是经济成本还是时间成本都非常的高。也会对企业的生产经营造成很大的影响。所以绝大多数企业主都很关心服务器中勒索病毒后怎么解决。针对这个问题&#xff0c;云天数据恢复中…

ArcGIS API for JavaScript部署开发

官方快速上手教程&#xff1a;https://developers.arcgis.com/javascript/latest/ 官方 API 参考文档&#xff1a;https://developers.arcgis.com/javascript/latest/api-reference 文章目录 0.前言1.引入ArcGIS API for JavaScript部署开发1.1在线引入&#xff08;via CDN&…

任务工单发送失败重试方案设计

需求背景&#xff1a; 该系统为一个工单系统&#xff0c;其中任务工单为该系统中的一个模块&#xff1b;任务工单它是需要周期性调度的一种任务类型&#xff1b;可以按照用户配置的时间周期定时性触发的。由于任务需要发送到对应的工作人员上&#xff0c;所以这里需要先对员工进…

学习Origin

最近&#xff0c;在学习Origin软件&#xff0c;网上资源还是很多的。我简单地记录了Origin的一些知识点&#xff0c;来督促自己的学习。 了解一下Origin的作用。 Origin入门教程&#xff08;一&#xff09;&#xff1a;一文学会Origin (sousepad.com) 该文讲述了Origin的一些基…

从城市吉祥物进化到虚拟人IP需要哪些步骤?

在2023年成都全国科普日主场活动中&#xff0c;推出了全国首个科普数字形象大使“科普熊猫”&#xff0c;科普熊猫作为成都科普吉祥物&#xff0c;是如何进化为虚拟人IP&#xff0c;通过动作捕捉、AR等技术&#xff0c;活灵活现地出现在大众眼前的&#xff1f; 以广州虚拟动力虚…

性能测试工具概念指的是什么

性能测试工具是一类用于模拟多种负载情况下应用程序行为的软件。它们可用于测量应用程序的响应时间、吞吐量、资源利用率和稳定性。本文将介绍性能测试工具常见几种类型有哪些! 性能测试工具通常分为以下几种类型&#xff1a; 1、负载测试工具&#xff1a; 这些工具用于模拟多个…

SQL多表设计--一对多(外键)

-- 完成部门和员工的-- 选择当前db03 这个数据库use db03;-- 查看当前选中的数据库select database();-- 创建员工表create table tb_emp (id int unsigned primary key auto_increment comment ID,username varchar(20) not null unique comment 用户名,password varchar(32)…

【网路安全 --- pikachu靶场安装】超详细的pikachu靶场安装教程(提供靶场代码及工具)

一&#xff0c;资源下载 所用到的工具是&#xff1a; VMware16.0 虚拟机 windows server 2003 phpstudy 2018 pikachu 靶场代码 notepadd 文本编辑器 360zip VMware 虚拟机 参照以下博客安装&#xff0c;如果已安装则忽略 【网络安全 --- 工具安装】VMware 16.0 详细安装过…

滚雪球学Java(42):探索对象的奥秘:解析Java中的Object类

&#x1f3c6;本文收录于「滚雪球学Java」专栏&#xff0c;专业攻坚指数级提升&#xff0c;助你一臂之力&#xff0c;带你早日登顶&#x1f680;&#xff0c;欢迎大家关注&&收藏&#xff01;持续更新中&#xff0c;up&#xff01;up&#xff01;up&#xff01;&#xf…