欧美成人精品手机在线观看_69视频国产_动漫精品第一页_日韩中文字幕网 - 日本欧美一区二区

LOGO OA教程 ERP教程 模切知識(shí)交流 PMS教程 CRM教程 開(kāi)發(fā)文檔 其他文檔  
 
網(wǎng)站管理員

.NET 音頻采集

freeflydom
2024年11月18日 10:46 本文熱度 637

本文介紹Windows下聲音數(shù)據(jù)的采集,用于本地錄音、視訊會(huì)議、投屏等場(chǎng)景

聲音錄制有麥克風(fēng)、揚(yáng)聲器以及混合錄制三類方式,麥克風(fēng)和揚(yáng)聲器單獨(dú)錄制的場(chǎng)景更多點(diǎn),混合錄制更多的是用于本地錄音

我們基于NAudio實(shí)現(xiàn),開(kāi)源組件NAudio已經(jīng)很穩(wěn)定的實(shí)現(xiàn)了各類播放、錄制、轉(zhuǎn)碼等功能,WaveIn,WaveInEvent,WasapiCapture,WasapiLoopbackCapture, WaveOut, WaveStream, WaveFileWriter, WaveFileReader, AudioFileReader都是比較常見(jiàn)的類,下面詳細(xì)介紹下錄制模塊的實(shí)現(xiàn)

麥克風(fēng)錄制

1.WaveInEvent

通過(guò)WaveInEvent類,我們可以捕獲麥克風(fēng)輸入:

 1     private WaveInEvent _waveIn;
 2     private WaveFileWriter _writer;
 3     private void MainWindow_Loaded(object sender, RoutedEventArgs e)
 4     {
 5         _waveIn = new WaveInEvent();
 6         //441采樣率,單通道
 7         _waveIn.WaveFormat = new WaveFormat(44100, 1);
 8         _writer = new WaveFileWriter("recordedAudio.wav", _waveIn.WaveFormat);
 9         _waveIn.DataAvailable += (s, a) =>
10         {
11             _writer.Write(a.Buffer, 0, a.BytesRecorded);
12         };
13         // 列出所有可用的錄音設(shè)備
14         for (int i = 0; i < WaveIn.DeviceCount; i++)
15         {
16             var deviceInfo = WaveIn.GetCapabilities(i);
17             OutputTextBlock.Text += $"Device {i}: {deviceInfo.ProductName}\r\n";
18         }
19     }
20     private void StartRecordButton_OnClick(object sender, RoutedEventArgs e)
21     {
22         _waveIn.StartRecording();
23     }
24     private void StopRecordButton_OnClick(object sender, RoutedEventArgs e)
25     {
26         _waveIn.StopRecording();
27         _waveIn.Dispose();
28         _writer.Close();
29     }

在每次錄制到數(shù)據(jù)時(shí),將數(shù)據(jù)寫(xiě)入文件。上面是實(shí)現(xiàn)保存錄音的DEMO

2.WaveIn

還有WaveIn,和WaveInEvent是一樣接口IWaveIn。如果是Windows窗口應(yīng)用,可以直接使用WaveIn,但需要傳入窗口句柄。控制臺(tái)應(yīng)用是無(wú)法支持WaveIn的

WaveIn構(gòu)造參數(shù)需要傳入窗口句柄,默認(rèn)不傳的話NAudio會(huì)創(chuàng)建一個(gè)窗口:


 1     internal void Connect(WaveInterop.WaveCallback callback)
 2     {
 3       if (this.Strategy == WaveCallbackStrategy.NewWindow)
 4       {
 5         this.waveOutWindow = new WaveWindow(callback);
 6         this.waveOutWindow.CreateControl();
 7         this.Handle = this.waveOutWindow.Handle;
 8       }
 9       else
10       {
11          .........
12       }
13     }

另外這里的WaveWindow是winform窗口,internal class WaveWindow : Form

WaveIn 使用回調(diào)函數(shù)(Callback)來(lái)處理音頻數(shù)據(jù),這種回調(diào)函數(shù)會(huì)在 Windows 收到音頻數(shù)據(jù)時(shí)通過(guò)消息機(jī)制調(diào)度。這通常意味著你需要管理并處理這些回調(diào)函數(shù),以確保音頻數(shù)據(jù)的正確捕捉和處理。然而這也意味著需要更多的底層工作和線程安全控制。

而在控制臺(tái)這類非GUI應(yīng)用,就建議使用WaveInEvent了,它未使用窗口消息,而是通過(guò)while循環(huán)監(jiān)聽(tīng)buffers數(shù)據(jù),通過(guò)判斷buffer.Done是否完成來(lái)觸發(fā)輸出buffer數(shù)據(jù)事件DataAvailable。

所以性能來(lái)說(shuō)WaveIn從線程處理上會(huì)占優(yōu)很多,未做過(guò)對(duì)比測(cè)試(待補(bǔ)充

3.WasapiCapture

除了WaveIn API,還可以使用WasapiCapture, 它與WaveIn的使用方式是一致的, 可以用來(lái)錄制麥克風(fēng)WaveInAPI雖然沒(méi)有獨(dú)占、共享功能,但也需要處理并發(fā)問(wèn)題,即多個(gè)錄音實(shí)例訪問(wèn)同一個(gè)麥克風(fēng)設(shè)備的話會(huì)存在并發(fā)訪問(wèn)問(wèn)題。

WasapiCapture是WASAPI About WASAPI - Win32 apps | Microsoft Learn,全稱Windows Audio Session Application Programming Interface (Windows音頻會(huì)話應(yīng)用編程接口) ,它在Windows Vista引入 、提供了一些關(guān)鍵的改進(jìn)

比如,提供更低的音頻延遲和高性能音頻處理,可以提供共享模式和獨(dú)占模式

在共享模式下,可以與多個(gè)應(yīng)用程序共享一個(gè)音頻設(shè)備;WasapiCapture.ShareMode = AudioClientShareMode.Shared;

在獨(dú)占模式下,應(yīng)用程序可以完全控制音頻設(shè)備,降低延遲 AudioClientShareMode.Exclusive

看看WasapiCapture DEMO,都是基于IWaveIn接口實(shí)現(xiàn),所以代碼無(wú)差別:


 1     private WaveFileWriter _writer;
 2     private WasapiCapture _capture;
 3     private void MainWindow_Loaded(object sender, RoutedEventArgs e)
 4     {
 5         _capture = new WasapiCapture();
 6         _writer = new WaveFileWriter("recordedAudio.wav", _capture.WaveFormat);
 7         _capture.DataAvailable += (s, a) =>
 8         {
 9             _writer.Write(a.Buffer, 0, a.BytesRecorded);
10         };
11         // 列出所有可用的錄音設(shè)備
12         for (int i = 0; i < WaveIn.DeviceCount; i++)
13         {
14             var deviceInfo = WaveIn.GetCapabilities(i);
15             OutputTextBlock.Text += $"Device {i}: {deviceInfo.ProductName}\r\n";
16         }
17     }

錄制麥克風(fēng)音頻,WasapiCapture 是最佳選擇,專為低延遲、高性能設(shè)計(jì)

另外,如果音頻采集時(shí)需要重采樣,可以使用BufferedWaveProvider緩存DataAvailable事件過(guò)來(lái)的原始音頻數(shù)據(jù),


 1     //創(chuàng)建BufferedWaveProvider,緩存原始音頻數(shù)據(jù)
 2     var bufferedProvider = new BufferedWaveProvider(provider.NAudioWaveFormat)
 3     {
 4         DiscardOnBufferOverflow = true,
 5         ReadFully = false
 6     };
 7     provider.WaveIn.DataAvailable += (s, e) =>
 8     {
 9         //將音頻數(shù)據(jù)寫(xiě)入 BufferedWaveProvider
10         bufferedProvider.AddSamples(e.Buffer, 0, e.BytesRecorded);
11     };
12     //獲取采樣接口
13     var sampleProvider = bufferedProvider.ToSampleProvider();
14     sampleProvider = new WdlResamplingSampleProvider(sampleProvider, TargetFormat.SampleRate);
15     //重采樣后的音頻數(shù)據(jù)
16     _waveProvider = sampleProvider.ToWaveProvider16();

BufferedWaveProvider、SampleToWaveProvider16均是實(shí)現(xiàn)IWaveProvider通用接口,可提供音頻格式以及獲取數(shù)據(jù)接口


 1   public interface IWaveProvider
 2   {
 3     /// <summary>Gets the WaveFormat of this WaveProvider.</summary>
 4     /// <value>The wave format.</value>
 5     WaveFormat WaveFormat { get; }
 6 
 7     /// <summary>Fill the specified buffer with wave data.</summary>
 8     /// <param name="buffer">The buffer to fill of wave data.</param>
 9     /// <param name="offset">Offset into buffer</param>
10     /// <param name="count">The number of bytes to read</param>
11     /// <returns>the number of bytes written to the buffer.</returns>
12     int Read(byte[] buffer, int offset, int count);
13   }

將重采樣的數(shù)據(jù)寫(xiě)入本地文件保存:


 1     /// <summary>
 2     /// 目標(biāo)音頻格式
 3     /// </summary>
 4     public WaveFormat TargetFormat { get; }
 5     public void Save()
 6     {
 7         using var writer = new WaveFileWriter("recordedAudio.wav", TargetFormat);
 8         // 將重采樣后的數(shù)據(jù)寫(xiě)入文件
 9         byte[] buffer = new byte[TargetFormat.AverageBytesPerSecond];
10         int bytesRead;
11         while ((bytesRead = _waveProvider.Read(buffer, 0, buffer.Length)) > 0)
12         {
13             writer.Write(buffer, 0, bytesRead);
14         }
15     }

這樣,我們使用 WasapiCapture 捕獲音頻數(shù)據(jù),并將這些數(shù)據(jù)實(shí)時(shí)重采樣到指定采樣率如44.1kHz(常見(jiàn)的采樣率有441和480),單聲道格式。錄音結(jié)束后,重采樣后的音頻數(shù)據(jù)再被保存到一個(gè)WAV文件中。

另外如果是單通道聲音,可以轉(zhuǎn)換成多通道即立體聲:

1     // Mono to Stereo
2     if (simpleFormat.Channels == 1)
3     {
4         sampleProvider = sampleProvider.ToStereo();
5     }

ToStereo返回的MonoToStereoSampleProvider,會(huì)將單通道聲音數(shù)據(jù),轉(zhuǎn)換為雙通道的音頻格式。但實(shí)際上,采樣器MonoToStereoSampleProvider內(nèi)部只有一份source數(shù)據(jù),在Read時(shí)外部參數(shù)Samples直接除以2即變成了1,左右聲道均輸出此音頻數(shù)據(jù)。

麥克風(fēng)錄制,推薦首選WasapiCapture。

揚(yáng)聲器錄制

錄制揚(yáng)聲器聲音即聲卡輸出,借助WasapiLoopbackCapture可簡(jiǎn)單實(shí)現(xiàn),使用方式與WasapiCapture沒(méi)區(qū)別。部分代碼:


 1     var capture = new WasapiLoopbackCapture();
 2     var writer = new WaveFileWriter("recordedAudio.wav", capture.WaveFormat);
 3     capture.DataAvailable += (s, a) =>
 4     {
 5         writer.Write(a.Buffer, 0, a.BytesRecorded);
 6     };
 7     capture.StartRecording();
 8     // 列出所有可用的揚(yáng)聲器設(shè)備
 9     for (int i = 0; i < WaveOut.DeviceCount; i++)
10     {
11         var deviceInfo = WaveOut.GetCapabilities(i);
12         OutputTextBlock.Text += $"Device {i}: {deviceInfo.ProductName}\r\n";
13     }

1. 音頻可視化

值得另外說(shuō)的,揚(yáng)聲器錄制有一類廠測(cè)場(chǎng)景,上位機(jī)工廠測(cè)試軟件測(cè)試揚(yáng)聲器,需要顯示聲道的音頻曲線

音頻波形圖或者頻譜圖,可以通過(guò)DataAvailable拿到的字節(jié)數(shù)組,根據(jù)可視化圖X坐標(biāo)需要顯示的點(diǎn)列數(shù)量,在數(shù)組中獲取數(shù)據(jù)然后映射到可視化圖表坐標(biāo)Y值上。詳細(xì)的可參考這篇 [C#] 使用 NAudio 實(shí)現(xiàn)音頻可視化_c#聲音頻譜-CSDN博客,它實(shí)現(xiàn)的是曲線,也可以另外換成柱狀圖。

錄制揚(yáng)聲器,有些場(chǎng)景需要關(guān)閉本地?fù)P聲器外放。投屏軟件有這個(gè)場(chǎng)景,會(huì)將當(dāng)前設(shè)備A的聲卡音頻數(shù)據(jù)傳輸?shù)狡渌O(shè)備B上播放,但設(shè)備A不想重復(fù)播放聲音。因?yàn)樵O(shè)備A播放聲音的話,會(huì)議室會(huì)有混音,并且投屏設(shè)備A一般是筆記本、設(shè)備B是會(huì)議大屏,揚(yáng)聲器質(zhì)量和功率是不如專業(yè)的交互大屏的,大屏揚(yáng)聲器價(jià)格會(huì)貴點(diǎn)。

1     var volume = playbackDevice.AudioEndpointVolume;
2     // 記錄原音量,用于結(jié)束錄制時(shí)恢復(fù)音量
3     float originalVolume = volume.MasterVolumeLevelScalar;
4     // 靜音播放設(shè)備
5     volume.MasterVolumeLevelScalar = 0;

2.保持揚(yáng)聲器活躍

同時(shí),錄制揚(yáng)聲器是一個(gè)持續(xù)活動(dòng),為避免因無(wú)音頻信號(hào)導(dǎo)致設(shè)備自動(dòng)關(guān)閉或進(jìn)入低功耗狀態(tài),在不想關(guān)閉音頻設(shè)備而又沒(méi)有實(shí)際音頻播放任務(wù)時(shí),會(huì)用沉默音頻保持設(shè)備活躍。可以按如下操作

1).創(chuàng)建一個(gè)WasapiOut實(shí)例,指定使用共享模式:var wasapiOut = new WasapiOut(device, AudioClientShareMode.Shared, true, 50);

2).獲取音頻設(shè)備MMDevice的AudioClient對(duì)象

using var audioClient = device.AudioClient;
wasapiOut.Init(new SilenceProvider(audioClient.MixFormat));

3).在啟動(dòng)WasapiLoopbackCapture錄制時(shí),將此靜音波形播放對(duì)象啟動(dòng),持續(xù)生成靜音信號(hào)

混音錄制

也有必要介紹下混音錄制,雖然場(chǎng)景較少。

初始化多個(gè)麥克風(fēng)、揚(yáng)聲器錄制器,然后同上面重采樣操作,創(chuàng)建一個(gè) BufferedWaveProvider (bufferedProvider),用于存儲(chǔ)輸入的音頻數(shù)據(jù)。

訂閱訂閱 IWaveIn 的 DataAvailable 事件,將數(shù)據(jù)都塞進(jìn)緩存音頻緩存器

最后返回16位浮點(diǎn)波形數(shù)據(jù)存儲(chǔ)器,IWaveProvider數(shù)據(jù)獲取方式同上面重采樣操作。


 1     public MixAudioCapture(params IWaveIn[] audioWaveCaptures)
 2     {
 3         _audioWaveCaptures = audioWaveCaptures;
 4         var sampleProviders = new List<ISampleProvider>();
 5         foreach (var waveIn in audioWaveCaptures)
 6         {
 7             var bufferedProvider = new BufferedWaveProvider(waveIn.WaveFormat)
 8             {
 9                 DiscardOnBufferOverflow = true,
10                 ReadFully = false
11             };
12             waveIn.DataAvailable += (s, e) =>
13             {
14                 bufferedProvider.AddSamples(e.Buffer, 0, e.BytesRecorded);
15             };
16             var sampleProvider = bufferedProvider.ToSampleProvider();
17             sampleProviders.Add(sampleProvider);
18         }
19         var waveProviders = sampleProviders.Select(m => m.ToWaveProvider());
20         // 混音后的音頻數(shù)據(jù)
21         _waveProvider = new MixingWaveProvider32(waveProviders).ToSampleProvider().ToWaveProvider16();
22     }

一般混音的同時(shí),也會(huì)重采樣。看具體場(chǎng)景操作吧。

上方示例代碼詳見(jiàn)Demo kybs00/AudioCaptureDemo: 音頻采集DEMO (github.com)

參考:

簡(jiǎn)要介紹WASAPI播放音頻的方法 - PeacoorZomboss - 博客園 (cnblogs.com)

[C#] 使用 NAudio 實(shí)現(xiàn)音頻可視化_c#聲音頻譜-CSDN博客

naudio/NAudio: Audio and MIDI library for .NET (github.com)



該文章在 2024/11/18 10:46:14 編輯過(guò)
關(guān)鍵字查詢
相關(guān)文章
正在查詢...
點(diǎn)晴ERP是一款針對(duì)中小制造業(yè)的專業(yè)生產(chǎn)管理軟件系統(tǒng),系統(tǒng)成熟度和易用性得到了國(guó)內(nèi)大量中小企業(yè)的青睞。
點(diǎn)晴PMS碼頭管理系統(tǒng)主要針對(duì)港口碼頭集裝箱與散貨日常運(yùn)作、調(diào)度、堆場(chǎng)、車(chē)隊(duì)、財(cái)務(wù)費(fèi)用、相關(guān)報(bào)表等業(yè)務(wù)管理,結(jié)合碼頭的業(yè)務(wù)特點(diǎn),圍繞調(diào)度、堆場(chǎng)作業(yè)而開(kāi)發(fā)的。集技術(shù)的先進(jìn)性、管理的有效性于一體,是物流碼頭及其他港口類企業(yè)的高效ERP管理信息系統(tǒng)。
點(diǎn)晴WMS倉(cāng)儲(chǔ)管理系統(tǒng)提供了貨物產(chǎn)品管理,銷(xiāo)售管理,采購(gòu)管理,倉(cāng)儲(chǔ)管理,倉(cāng)庫(kù)管理,保質(zhì)期管理,貨位管理,庫(kù)位管理,生產(chǎn)管理,WMS管理系統(tǒng),標(biāo)簽打印,條形碼,二維碼管理,批號(hào)管理軟件。
點(diǎn)晴免費(fèi)OA是一款軟件和通用服務(wù)都免費(fèi),不限功能、不限時(shí)間、不限用戶的免費(fèi)OA協(xié)同辦公管理系統(tǒng)。
Copyright 2010-2025 ClickSun All Rights Reserved