本文的例子使用NAudio.CoreAudioApi實現(xiàn),全部為C#代碼
以下僅為個人理解,不一定都對,勿信~
閱讀這個文檔,最好具備C++知識,因為會用到Marshal命名空間進行指針操作
一、關(guān)于Windows Core Audio
Windows Core Audio API 是一種非常底層的音頻API,上層應(yīng)用為DirectSound或者WaveXXX接口等,WASAPI是其中的一部分。
Windows Core Audio API 構(gòu)成
Multimedia Device API(MMDevice):表示系統(tǒng)中的音頻設(shè)備節(jié)點(Audio Device Endpoint),Mmdeviceapi.h
Windows Audio Session API(WASAPI):用來操作音頻設(shè)備節(jié)點之間的音頻流(Audio Stream),Audioclient.h,Audiopolicy.h,Audiosessiontypes.h
DeviceTopology API:用來處理音頻設(shè)備節(jié)點拓撲結(jié)構(gòu)。可以設(shè)計音頻設(shè)備節(jié)點之間的數(shù)據(jù)流向,Devicetopology.h
EndpointVolume API:當音頻設(shè)備節(jié)點為聲卡獨占模式(Exclusive-mode)時,使用該API對Volume進行操作,Endpointvolume.h
以上頭文件位置,XXXX\Windows SDK\include,由于效率問題,這些接口以COM形式出現(xiàn),而不是.net或者.net freamwork
我的電腦中,通過VS安裝Windows SDK后,頭文件位置為C:\Program Files (x86)\Windows Kits\10\Include\10.0.22000.0\um
一些常見C#庫都對其有映射,BASS.Net,NAudio。
以上接口調(diào)用順序:
CoCreateInstance -> IMMDeviceEnumerator -> IMMDevice -> WASAPI DTAPI EVAPI
各種例子,Microsoft Learn: Build skills that open doors in your career,Windows SDK and emulator archive | Microsoft Developer
二、用戶模式下的音頻組件 User-Mode Audio Components
User Client:QQ音樂
User Mode:Core Audio(Audio Endpoint)、Direct Sound(High-Level API)
Kernel Mode:驅(qū)動(Audio Driver)、聲音硬件(Audio Adapter)
Audio Endpoint Device:音頻設(shè)備節(jié)點,對聲音硬件的抽象,常見的聲音硬件有喇叭、耳機、話筒、麥克風
三、Audio Endpoint Device
Audio Endpoint:表示一個聲音設(shè)備
Audio Adapter:一個聲音設(shè)備處理聲音的流程,如A/D,D/A,輸出,輸入
MMDvice API用來處理和Audio Endpoint相關(guān)的操作。
實現(xiàn)方式:
C++
使用CoCreateInstance -> IMMDeviceEnumerator -> 其他API,包括:
IMMDevice、IMMEndpoint、IMMNotificationClient(熱插拔設(shè)備)
NAudio
MMDeviceEnumerator -> MMDevice
獲取到MMDevice后,調(diào)用Activate方法激活設(shè)備,隨后調(diào)用WASAPI,DTAPI,EVAPI
獲取到Audio Device后,就可以創(chuàng)建AudioClient實例進行WASAPI操作了
四、Audio Session
Audio Stream:音頻流,可以為Recording 或者 Playing
Audio Session:每一個Audio Stream都對應(yīng)一個Audio Stream,每一個Audio Session都對應(yīng)一個Audio Device
與上文提到的IMMNotificationClient,對應(yīng)Session也有事件處理接口IAudioSessionEvents,這些接口主要處理狀態(tài)變化、音量變化、開始Session、結(jié)束Session等和聲音處理相關(guān)的事件
獲取到Audio Device后可以通過AudioSessionManager來確認系統(tǒng)中有哪些Session
五、Audio Stream
獲取到Audio Device后,如果Activate成功,就可以得到AudioClient實例
通過AudioClient實例不僅可以獲取到Audio Session,還可獲取到Audio Stream文章來源:http://www.zghlxwxcb.cn/news/detail-797271.html
Audio Stream就是實際的音頻數(shù)據(jù),通過WASAPI對其進行操作文章來源地址http://www.zghlxwxcb.cn/news/detail-797271.html
六、Audio Render Client 一個播放音頻的例子
// 使用NAudio.WaveFileReader讀取wav文件
// 使用FileStream也可以,不過需要自己解析Wave Chunk
// 例子中使用的wav文件為48000,32bit,2-channel,PCM
// 因此每一幀的大小 frame = 32bit / 8 * 2 = 8 bytes
var reader = new WaveFileReader(odl.FileName);
// 每次讀取48000幀,也就是1秒鐘
// 每幀大小為4bytes
int frame = 48000;
var bytes = new Byte[frame * 8];
reader.Read(bytes, 0, frame * 8);
// 以下為Core Audio部分,使用的是NAudio.CoreAudioAPI的C#映射
// 獲取Core Audio Device遍歷接口
var enumer = new NAudio.CoreAudioApi.MMDeviceEnumerator();
// 獲取默認的Audio Device,Render表示輸出設(shè)備,Multimedia表示多媒體設(shè)備
// 備注:Capture是錄音設(shè)備,Console是交互設(shè)備,Communication是通訊設(shè)備
var device = enumer.GetDefaultAudioEndpoint(DataFlow.Render, Role.Multimedia);
// 讀取文件的WAVE配置,并初始化給輸出設(shè)備
var format = reader.WaveFormat;
// Audio Device只是表示聲音設(shè)備,并不是真正處理聲音的抽象
// Audio Client才是處理聲音的抽象
var audioClient = device.AudioClient;
audioClient.Initialize(
AudioClientShareMode.Shared,
AudioClientStreamFlags.None,
10000000,
0,
format,
Guid.Empty);
// 獲取輸出設(shè)備每次可以填充的音頻緩沖區(qū)大小
// 單位是Frame,F(xiàn)rame與Byte的對應(yīng)關(guān)系要根據(jù)WaveFormat計算
var bufferSize = audioClient.BufferSize;
// 獲取輸出設(shè)備
var renderclient = audioClient.AudioRenderClient;
// 填充第一幀音頻輸出緩沖區(qū)
// 由于Core Audio是COM接口,要用Marshal.Copy完成指針內(nèi)存拷貝
var pData = renderclient.GetBuffer(frame);
Marshal.Copy(bytes, 0, pData, frame * 8);
renderclient.ReleaseBuffer(bufferSize, AudioClientBufferFlags.None);
// 開始播放音頻
audioClient.Start();
// 以下為循環(huán)填充,否則只播放一次緩沖區(qū)數(shù)據(jù)
var sec = 1;
var stop = false;
while (!stop)
{
Thread.Sleep(1000);
reader.Read(bytes, 0, frame * 8);
pData = renderclient.GetBuffer(frame);
Marshal.Copy(bytes, 0, pData, frame * 8);
renderclient.ReleaseBuffer(bufferSize, AudioClientBufferFlags.None);
}
audioClient.Stop();
renderclient.Dispose();
renderclient = null;
audioClient.Dispose();
audioClient = null;
device.Dispose();
device = null;
七、Audio Capture 一個錄音的例子
// 與Audio Render部分的邏輯相同
var device = this.captureComboBox.SelectedItem as NAudio.CoreAudioApi.MMDevice;
var format = device.AudioClient.MixFormat;
var writer = new WaveFileWriter("capture.wav", format);
var audioClient = device.AudioClient;
audioClient.Initialize(
AudioClientShareMode.Shared,
AudioClientStreamFlags.AutoConvertPcm,
10000000, // sec
0,
format,
Guid.Empty);
var bufferSize = audioClient.BufferSize;
var captureclient = audioClient.AudioCaptureClient;
var sec = 1;
audioClient.Start();
while (!stop)
{
// 0 表示沒有錄音
var packageSize = captureclient.GetNextPacketSize();
while (packageSize != 0)
{
int numFramestoRead = 0;
AudioClientBufferFlags flags = AudioClientBufferFlags.None;
IntPtr package = captureclient.GetBuffer(out numFramestoRead, out flags);
captureclient.ReleaseBuffer(numFramestoRead);
var length = numFramestoRead * 8;
var buffer = new byte[length];
Marshal.Copy(package, buffer, 0, length);
writer.Write(buffer, 0, length);
packageSize = captureclient.GetNextPacketSize();
}
}
writer.Close();
writer.Dispose();
writer = null;
audioClient.Stop();
captureclient.Dispose();
captureclient = null;
audioClient.Dispose();
audioClient = null;
device.Dispose();
device = null;
到了這里,關(guān)于Windows(C#)音頻開發(fā)-Windows Core Audio(WASAPI)的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!