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


二、完整代码

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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

CodeOfCC

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值