C#通过实现winmm枚举音频设备

 更新时间:2023年10月09日 08:30:25   作者:CodeOfCC  
使用C#做音频录制时需要获取音频设备信息,其中比较简单的就是使用winmm,所以本文就为大家介绍一下C#如何通过实现winmm枚举音频设备,需要的可以参考下

前言

使用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;
}

二、完整代码

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#使用字符串处理,实现也变得很简单。总的来说,这算是一种获取音频设备信息的方法,能够满足一些使用场景的需求。

到此这篇关于C#通过实现winmm枚举音频设备的文章就介绍到这了,更多相关C# winmm枚举音频设备内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • 浅析c# 接口

    浅析c# 接口

    这篇文章主要介绍了c# 接口的相关资料,文中讲解非常细致,代码帮助大家更好的理解和学习,感兴趣的朋友可以了解下。
    2020-07-07
  • C#获取程序文件相关信息的方法

    C#获取程序文件相关信息的方法

    这篇文章主要介绍了C#获取程序文件相关信息的方法,可实现获取程序版本号、版权声明、程序文本信息等,需要的朋友可以参考下
    2014-09-09
  • C#实现航班查询及预订功能

    C#实现航班查询及预订功能

    这篇文章给大家介绍利用C#完成航班机票信息查询,航班机票预定等功能。代码简单易懂,非常不错,具有参考借鉴价值,需要的朋友参考下吧
    2018-02-02
  • C# WebApi 接口返回值不困惑:返回值类型详解

    C# WebApi 接口返回值不困惑:返回值类型详解

    这篇文章主要介绍了C# WebApi 接口返回值不困惑:返回值类型详解,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2018-07-07
  • DevExpress之ChartControl实现柱状图演示实例

    DevExpress之ChartControl实现柱状图演示实例

    这篇文章主要介绍了DevExpress中ChartControl实现柱状图演示方法,实例展示了相关绘图函数的具体用法,具有一定的实用价值,需要的朋友可以参考下
    2014-10-10
  • C# ListBox中的Item拖拽代码分享

    C# ListBox中的Item拖拽代码分享

    在本文中我们给大家分享了关于C#的ListBox中的Item拖拽的功能代码分享,对此有需要的朋友参考学习下。
    2018-03-03
  • C# 使用BitBlt进行窗口抓图的示例

    C# 使用BitBlt进行窗口抓图的示例

    这篇文章主要介绍了C# 使用BitBlt进行窗口抓图的示例,帮助大家更好的理解和使用c#,感兴趣的朋友可以了解下
    2021-01-01
  • C#实现跨进程条件变量的示例代码

    C#实现跨进程条件变量的示例代码

    C#提供的多进程同步对象有互斥锁和信号量,但是并没有条件变量,虽然信号量条件变量一定程度可以等效,但是具体的使用还是会有区别,本文提供了一种条件变量的实现方法,可以用于进程间的同步控制,需要的朋友可以参考下
    2024-07-07
  • unity实现动态排行榜

    unity实现动态排行榜

    这篇文章主要为大家详细介绍了unity实现动态排行榜,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2021-07-07
  • C#连接Informix数据库的问题

    C#连接Informix数据库的问题

    这篇文章主要介绍了C#连接Informix数据库的问题,本文给大家介绍的非常详细,对大家的工作或学习具有一定的参考借鉴价值,需要的朋友可以参考下
    2020-03-03

最新评论