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、添加依賴
由于編寫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-12C#利用OLEDB實(shí)現(xiàn)將DataTable寫入Excel文件中
這篇文章主要為大家詳細(xì)介紹了C#如何利用OLEDB實(shí)現(xiàn)將DataTable寫入Excel文件中,文中的示例代碼簡(jiǎn)潔易懂,具有一定的借鑒價(jià)值,需要的可以參考一下2023-02-02C#實(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-09C#基于UDP實(shí)現(xiàn)的P2P語(yǔ)音聊天工具
這篇文章主要是一個(gè)應(yīng)用,使用udp傳送語(yǔ)音和文本等信息。在這個(gè)系統(tǒng)中沒(méi)有服務(wù)端和客戶端,相互通訊都是直接相互聯(lián)系的,能夠很好的實(shí)現(xiàn)效果2015-09-09C#校驗(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-08C# 創(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