C#通過(guò)實(shí)現(xiàn)winmm枚舉音頻設(shè)備
前言
使用C#做音頻錄制時(shí)需要獲取音頻設(shè)備信息,比如使用ffmpeg進(jìn)行錄制需要先獲取音頻設(shè)備名稱,再Windows上獲取音頻設(shè)備的方法有不少,其中比較簡(jiǎn)單的就是使用winmm,這是一套比較舊的api但是使用方法簡(jiǎn)單,當(dāng)然有個(gè)缺陷就是音頻名稱不能超過(guò)32個(gè)字符,超過(guò)會(huì)被截?cái)?,?dāng)然如果作為winmm的采集或播放配套使用則不會(huì)有問(wèn)題。
一、如何實(shí)現(xiàn)
需要先導(dǎo)入winmm的api,以及定義存放音頻設(shè)備信息的實(shí)體,最后通過(guò)調(diào)用api實(shí)現(xiàn)枚舉設(shè)備功能。
1、添加依賴
由于編寫(xiě)dllimport比較麻煩,而且nuget已經(jīng)有封裝好的庫(kù),所以沒(méi)必要重復(fù)造輪子,直接nuget安裝然后補(bǔ)充一些必要的接口即可。
(1)nuget安裝winmm的封裝庫(kù)

(2)補(bǔ)充接口
由于Vanara.PInvoke.Multimedia對(duì)waveInGetDevCaps和waveOutGetDevCaps導(dǎo)入的有點(diǎn)問(wèn)題,所以需要自己dllimport。還需要添加一些相關(guān)的枚舉用于獲取聲音格式。
[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、定義實(shí)體
需要定義聲音格式
class SampleFormat
{
/// <summary>
/// 聲道數(shù)
/// </summary>
public ushort Channels { set; get; }
/// <summary>
/// 采樣率
/// </summary>
public uint SampleRate { set; get; }
/// <summary>
/// 位深
/// </summary>
public ushort BitsPerSample { set; get; }
};以及聲音設(shè)備實(shí)體
class AudioDevice
{
/// <summary>
/// 設(shè)備Id
/// </summary>
public uint Id { set; get; }
/// <summary>
/// 設(shè)備名稱
/// </summary>
public string Name { set; get; } = "";
/// <summary>
/// 聲道數(shù)
/// </summary>
public int Channels { set; get; }
/// <summary>
/// 支持的格式
/// </summary>
public IEnumerable<SampleFormat>? SupportedFormats { set; get; }
};3、實(shí)現(xiàn)枚舉
音頻采集設(shè)備
/// <summary>
/// 枚舉聲音采集設(shè)備
/// </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>
/// 通過(guò)id獲取聲音采集設(shè)備信息
/// </summary>
/// <param name="id">設(shè)備id</param>
/// <returns>設(shè)備對(duì)象</returns>
public static AudioDevice? GetWaveInDeviceById(uint id)
{
WAVEINCAPS pwic;//聲音設(shè)備功能信息
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;
}音頻播放設(shè)備
/// <summary>
/// 枚舉聲音播放設(shè)備
/// </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>
/// 通過(guò)id獲取聲音播放設(shè)備信息
/// </summary>
/// <param name="id">設(shè)備id</param>
/// <returns>設(shè)備對(duì)象</returns>
public static AudioDevice? GetWaveOutDeviceById(uint id)
{
WAVEOUTCAPS pwic;//聲音設(shè)備功能信息
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: 音頻設(shè)備枚舉
* @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>
/// 枚舉聲音采集設(shè)備
/// </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>
/// 枚舉聲音播放設(shè)備
/// </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>
/// 通過(guò)id獲取聲音采集設(shè)備信息
/// </summary>
/// <param name="id">設(shè)備id</param>
/// <returns>設(shè)備對(duì)象</returns>
public static AudioDevice? GetWaveInDeviceById(uint id)
{
WAVEINCAPS pwic;//聲音設(shè)備功能信息
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>
/// 通過(guò)id獲取聲音播放設(shè)備信息
/// </summary>
/// <param name="id">設(shè)備id</param>
/// <returns>設(shè)備對(duì)象</returns>
public static AudioDevice? GetWaveOutDeviceById(uint id)
{
WAVEOUTCAPS pwic;//聲音設(shè)備功能信息
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("音頻采集設(shè)備" + i.Id + ":" + i.Name);
}
foreach (var i in Winmm.WaveOutDevices)
{
Console.WriteLine("音頻播放設(shè)備"+i.Id+":" +i.Name);
}
總結(jié)
以上就是今天要講的內(nèi)容,使用winnm枚舉設(shè)備還是比較簡(jiǎn)單的,唯一麻煩一點(diǎn)的地方就是支持格式的獲取,但是通過(guò)C#使用字符串處理,實(shí)現(xiàn)也變得很簡(jiǎn)單??偟膩?lái)說(shuō),這算是一種獲取音頻設(shè)備信息的方法,能夠滿足一些使用場(chǎng)景的需求。
到此這篇關(guān)于C#通過(guò)實(shí)現(xiàn)winmm枚舉音頻設(shè)備的文章就介紹到這了,更多相關(guān)C# winmm枚舉音頻設(shè)備內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
C#中把任意類型的泛型集合轉(zhuǎn)換成SQLXML數(shù)據(jù)格式的實(shí)例
本文主要分享了C#中把任意類型的泛型集合轉(zhuǎn)換成SQLXML數(shù)據(jù)格式的實(shí)例代碼。具有很好的參考價(jià)值,需要的朋友可以看下2016-12-12
C#利用OLEDB實(shí)現(xiàn)將DataTable寫(xiě)入Excel文件中
這篇文章主要為大家詳細(xì)介紹了C#如何利用OLEDB實(shí)現(xiàn)將DataTable寫(xiě)入Excel文件中,文中的示例代碼簡(jiǎn)潔易懂,具有一定的借鑒價(jià)值,需要的可以參考一下2023-02-02
C#實(shí)現(xiàn)TreeView節(jié)點(diǎn)拖拽的方法
這篇文章主要介紹了C#實(shí)現(xiàn)TreeView節(jié)點(diǎn)拖拽的方法,涉及C#針對(duì)TreeView節(jié)點(diǎn)的動(dòng)態(tài)添加及移除技巧,具有一定參考借鑒價(jià)值,需要的朋友可以參考下2015-09-09
C#基于UDP實(shí)現(xiàn)的P2P語(yǔ)音聊天工具
這篇文章主要是一個(gè)應(yīng)用,使用udp傳送語(yǔ)音和文本等信息。在這個(gè)系統(tǒng)中沒(méi)有服務(wù)端和客戶端,相互通訊都是直接相互聯(lián)系的,能夠很好的實(shí)現(xiàn)效果2015-09-09
C#校驗(yàn)時(shí)間格式的場(chǎng)景分析
本文通過(guò)場(chǎng)景分析給大家講解C#里如何簡(jiǎn)單的校驗(yàn)時(shí)間格式,本次的場(chǎng)景屬于比較常見(jiàn)的收單API,對(duì)第三方的訂單進(jìn)行簽名驗(yàn)證,然后持久化到數(shù)據(jù)庫(kù),需要的朋友跟隨小編一起看看吧2022-08-08
C# 創(chuàng)建MDB數(shù)據(jù)庫(kù)、并存放表格數(shù)據(jù)的案例
這篇文章主要介紹了C# 創(chuàng)建MDB數(shù)據(jù)庫(kù)、并存放表格數(shù)據(jù)的案例,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2021-01-01

