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的封装库

C# 通过winmm枚举音频设备_第1张图片

(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
{
    /// 
    /// 声道数
    /// 
    public ushort Channels { set; get; }
    /// 
    /// 采样率
    /// 
    public uint SampleRate { set; get; }
    /// 
    /// 位深
    /// 
    public ushort BitsPerSample { set; get; }
};

以及声音设备实体

class AudioDevice
{
    /// 
    /// 设备Id
    /// 
    public uint Id { set; get; }
    /// 
    /// 设备名称
    /// 
    public string Name { set; get; } = "";
    /// 
    /// 声道数
    /// 
    public int Channels { set; get; }
    /// 
    /// 支持的格式
    /// 
    public IEnumerable<SampleFormat>? SupportedFormats { set; get; }
};

3、实现枚举

音频采集设备

/// 
/// 枚举声音采集设备
/// 
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;
        }
    }
}
/// 
/// 通过id获取声音采集设备信息
/// 
/// 设备id
/// 设备对象
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;
}

音频播放设备

 /// 
 /// 枚举声音播放设备
 /// 
 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;
         }
     }
 }
/// 
/// 通过id获取声音播放设备信息
/// 
/// 设备id
/// 设备对象
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
    {
        /// 
        /// 枚举声音采集设备
        /// 
        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;
                }
            }
        }

        /// 
        /// 枚举声音播放设备
        /// 
        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;
                }
            }
        }

        /// 
        /// 通过id获取声音采集设备信息
        /// 
        /// 设备id
        /// 设备对象
        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;
        }
        /// 
        /// 通过id获取声音播放设备信息
        /// 
        /// 设备id
        /// 设备对象
        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);
}

C# 通过winmm枚举音频设备_第2张图片


总结

以上就是今天要讲的内容,使用winnm枚举设备还是比较简单的,唯一麻烦一点的地方就是支持格式的获取,但是通过C#使用字符串处理,实现也变得很简单。总的来说,这算是一种获取音频设备信息的方法,能够满足一些使用场景的需求。

你可能感兴趣的:(.Net,音视频,c#,开发语言,音视频,windows,音频)