C# 使用开会的音频实时转换为文本的库,并且要区分麦克风和扬声器的输出音频,有那些方式,实现一个de...
Créé le : 11 juin 2025
Créé le : 11 juin 2025
C# 使用开会的音频实时转换为文本的库,并且要区分麦克风和扬声器的输出音频,有那些方式,实现一个demo
下面按“思路 → 方案 → 参考 Demo 代码”的顺序整理,帮助你用 C# 在会议场景下 实时把声音转文字,同时 区分麦克风(Mic)与扬声器回放(Loopback/Speaker) 的输出。
模块 | 关键点 | 推荐做法 |
---|---|---|
音频采集 | Windows 上把两个通路分离 | 用 NAudio:<br/>• WasapiCapture → 麦克风<br/>• WasapiLoopbackCapture → 扬声器(系统回环) github.com |
实时传输 | 把原始 PCM 流推送给语音识别引擎 | 选择兼容 Push/Stream 输入的 SDK(见下) |
语音识别 | 准确率、延迟、成本、离/在线 | 下面列出 5 条主流库 |
并发处理 | 两路同时转写 & 标记来源 | 启两条后台任务/线程,各自独立实例,输出时加标签 [Mic] / [Speaker] |
后处理 | 去噪、打断点、整合字幕 | 可再做段落合并、时间戳对齐等 |
类型 | 库 / 服务 | 特点 | 备注 |
---|---|---|---|
云端 SaaS | Azure AI Speech SDK | 官方 .NET SDK;支持 PushAudioInputStream ;有自动标点、说话人分离 | 2025 年版 SDK 更新了多级识别示例 learn.microsoft.com learn.microsoft.com |
Google Cloud Speech‑to‑Text | gRPC + .Google.Cloud.Speech.V1 NuGet;中文表现好 | 需启用“实时流式识别” | |
Amazon Transcribe Streaming | 官方 AWSSDK.TranscribeStreaming | 计费按秒 | |
OpenAI Realtime API (Whisper‑v3) | 200 ms 级延迟;支持 C# WebSocket 客户端 | 公测版 2024‑09 发布 openai.com | |
本地离线 | Vosk | LGPL;模型小,CPU 即可跑 | VoskSpeechRecognizer NuGet |
Whisper.cpp / whisper.net | OpenAI 模型本地推理;可 GPU | 内存占用 ~1 GB;延迟略高 |
若对 延迟最敏感:优先 Azure / OpenAI Realtime
若 离线、隐私:用 Vosk 或 Whisper.cpp
csharpvar devices = MMDeviceEnumerator.EnumerateAudioEndPoints(DataFlow.All, DeviceState.Active); foreach (var d in devices) Console.WriteLine($"{d.FriendlyName} {d.DataFlow}");
csharpvar mic = new WasapiCapture(); // 默认麦克风 var speaker = new WasapiLoopbackCapture(); // 系统回放 mic.StartRecording(); speaker.StartRecording();
AudioConfig
⟶ PushAudioInputStream
DataAvailable
里 stream.Write(buffer, 0, bytesRecorded);
recognizer.Recognized += …
读取文本Task.Run
分别启动 MicJob()
、SpeakerJob()
;Console.WriteLine($"[Mic] {e.Result.Text}");
NuGet:
Microsoft.CognitiveServices.Speech
、NAudio
前置:在 Azure 门户创建语音资源,取speechKey
、serviceRegion
csharpusing System; using System.Threading.Tasks; using Microsoft.CognitiveServices.Speech; using Microsoft.CognitiveServices.Speech.Audio; using NAudio.Wave; class Program { const string speechKey = "YOUR_KEY"; const string serviceRegion = "eastus"; static async Task Main() { // 启动两路任务 await Task.WhenAll( RunChannel("Mic", new WasapiCapture()), RunChannel("Speaker", new WasapiLoopbackCapture())); } static Task RunChannel(string tag, WasapiCapture capturer) { var config = SpeechConfig.FromSubscription(speechKey, serviceRegion); config.SpeechRecognitionLanguage = "zh-CN"; // Push 流 var pushStream = AudioInputStream.CreatePushStream(); var audioConfig = AudioConfig.FromStreamInput(pushStream); var recognizer = new SpeechRecognizer(config, audioConfig); // 处理结果 recognizer.Recognized += (s, e) => { if (e.Result.Reason == ResultReason.RecognizedSpeech) Console.WriteLine($"[{tag}] {e.Result.Text}"); }; // NAudio: 每当有音频缓冲,写入 SDK capturer.DataAvailable += (s, e) => pushStream.Write(e.Buffer, 0, e.BytesRecorded); capturer.RecordingStopped += (s, e) => pushStream.Close(); // 停止时关闭流 // 启动 capturer.StartRecording(); return recognizer.StartContinuousRecognitionAsync(); } }
text[Mic] 大家好,会议马上开始。 [Speaker] 现在分享屏幕,请各位确认能否看到。 [Mic] 可以看到,谢谢!
Vosk
用 var rec = new VoskRecognizer(model, 16000f);
;同样在 DataAvailable
推送 rec.AcceptWaveform(buffer, bytes)
OpenAI Realtime
建立 WebSocket ⟶ 发送 Opus/PCM 帧(官方示例提供 WebSocket URL 和流格式)openai.com
Google / Amazon
SDK 都支持 StreamingRecognizeRequest
,只需把 bytes
写入 WriteAsync()
。
场景 | 建议 |
---|---|
回声消除 | 仍可能把扬声器声音漏进 Mic。可启用 Azure MAS 前置处理或硬件回声消除 learn.microsoft.com |
分段 | 监听 e.Result.Reason == NoMatch / EndOfSpeech 可判断停顿,插入换行 |
吞吐量 | 设置 capturer.ShareMode = AudioClientShareMode.Shared ,帧大小 20 ms(≈ 640 B, 16 kHz × 16 bit × 1 ch) |
成本 | 云服务按秒计费,可在发言空闲时 StopRecording() 节省费用 |
兼容性 | macOS 没有 WASAPI Loopback,需 CoreAudio/AVAudioEngine;Linux 需 PulseAudio/ALSA |
WasapiCapture
设备或语音服务即可。祝项目顺利!能把整个代码流程都输出吗
下面给出 可直接 dotnet run
运行的完整示例项目(.NET 8 Console App)。
代码已包含:
[Mic]
/ [Speaker]
只需把 YOUR_KEY
/ YOUR_REGION
换成自己的 Azure 语音资源配置即可运行。
textRealtimeTranscriptionDemo/ ├── RealtimeTranscriptionDemo.csproj └── Program.cs <-- 主入口(全部逻辑都在这里)
RealtimeTranscriptionDemo.csproj
xml<Project Sdk="Microsoft.NET.Sdk"> <PropertyGroup> <OutputType>Exe</OutputType> <TargetFramework>net8.0</TargetFramework> <Nullable>enable</Nullable> <LangVersion>preview</LangVersion> </PropertyGroup> <ItemGroup> <!-- 语音识别 --> <PackageReference Include="Microsoft.CognitiveServices.Speech" Version="1.37.0" /> <!-- 音频采集 --> <PackageReference Include="NAudio" Version="2.2.1" /> </ItemGroup> </Project>
Program.cs
csharp/* * RealtimeTranscriptionDemo — 双通路实时转写 * * - Mic: 默认音频输入设备 (WasapiCapture) * - Speaker: 系统回放回环 (WasapiLoopbackCapture) * * 作者: 你 * 日期: 2025‑06‑11 */ using Microsoft.CognitiveServices.Speech; using Microsoft.CognitiveServices.Speech.Audio; using NAudio.CoreAudioApi; using NAudio.Wave; using System.Threading.Channels; const string SpeechKey = "YOUR_KEY"; // Azure Speech 服务密钥 const string SpeechRegion = "YOUR_REGION"; // 例如 "eastus" const string Lang = "zh-CN"; // 识别语言 // ---------- 主流程 ---------- Console.WriteLine("▶️ RealtimeTranscriptionDemo 开始运行 …\n"); // 创建两个音频捕获器 using var micCapture = new WasapiCapture(); // 麦克风 using var loopCapture = new WasapiLoopbackCapture(); // 扬声器回环 // 创建两个推送流 var micStream = AudioInputStream.CreatePushStream(); var loopStream = AudioInputStream.CreatePushStream(); // 启动两路识别任务 var micTask = RunChannelAsync("Mic", micCapture, micStream); var loopTask = RunChannelAsync("Speaker", loopCapture, loopStream); // 等待 Ctrl‑C 终止 Console.CancelKeyPress += (_, e) => { e.Cancel = true; // 防止进程立刻退出 micCapture.StopRecording(); loopCapture.StopRecording(); Console.WriteLine("\n🛑 捕获停止,正在清理 …"); }; await Task.WhenAll(micTask, loopTask); Console.WriteLine("✅ 已退出。按任意键关闭窗口 …"); Console.ReadKey(); // ==================================================================== // ↓↓↓ 核心方法封装 ↓↓↓ // ==================================================================== async Task RunChannelAsync(string tag, WasapiCapture capture, PushAudioInputStream pushStream) { // 1) 语音识别器初始化 ------------------------------------------------ var config = SpeechConfig.FromSubscription(SpeechKey, SpeechRegion); config.SpeechRecognitionLanguage = Lang; // 通过 Push 流把外部 PCM 注入 SDK using var audioCfg = AudioConfig.FromStreamInput(pushStream); using var recognizer = new SpeechRecognizer(config, audioCfg); // 2) 捕获 NAudio 数据并写入 PushStream ------------------------------ capture.DataAvailable += (_, e) => { // 注意:必须同步写入;SDK 内部线程安全 pushStream.Write(e.Buffer, 0, e.BytesRecorded); }; capture.RecordingStopped += (_, _) => { pushStream.Close(); // 告诉 SDK 音频结束 }; // 3) 处理识别结果 ---------------------------------------------------- recognizer.Recognized += (_, e) => { if (e.Result.Reason == ResultReason.RecognizedSpeech && !string.IsNullOrWhiteSpace(e.Result.Text)) { Console.WriteLine($"[{tag}] {e.Result.Text}"); } }; recognizer.Canceled += (_, e) => { Console.ForegroundColor = ConsoleColor.Red; Console.WriteLine($"[{tag}] ❌ 识别被取消: {e.Reason} — {e.ErrorDetails}"); Console.ResetColor(); }; // 4) 启动录音 & 识别 -------------------------------------------------- capture.StartRecording(); await recognizer.StartContinuousRecognitionAsync(); // 5) 等待录音结束 ---------------------------------------------------- // 如果用户按 Ctrl‑C 会触发 RecordingStopped var tcs = new TaskCompletionSource(); capture.RecordingStopped += (_, _) => tcs.TrySetResult(); await tcs.Task; // 6) 关闭识别器 ------------------------------------------------------- await recognizer.StopContinuousRecognitionAsync(); }
以下命令假设你已安装 .NET SDK 8 (或以上) 并把项目解压到
RealtimeTranscriptionDemo
目录。
bash# 进入目录 cd RealtimeTranscriptionDemo # 还原依赖 & 编译 dotnet build # 运行 dotnet run
启动后,终端会实时打印:
text[Mic] 大家好,欢迎参加会议。 [Speaker] This is the presentation audio coming from Zoom. [Mic] 可以听到,请继续。
停止方法:按
Ctrl + C
。程序会优雅关闭录音并等待 SDK 发回剩余结果后退出。
步骤 | 说明 |
---|---|
① 设备采集 | WasapiCapture 默认使用系统首选麦克风;WasapiLoopbackCapture 则抓取当前默认输出设备的回环。 |
② 数据管线 | 每路都创建 PushAudioInputStream → AudioConfig → SpeechRecognizer 。<br>回调里写入 pushStream.Write() ,SDK 线程安全。 |
③ 并发 | RunChannelAsync 独立运行,两者互不干扰。<br>终端输出加 [Mic] / [Speaker] 区分。 |
④ 资源管理 | using 语法确保 SpeechRecognizer 、AudioConfig 、WasapiCapture 都正确释放。 |
⑤ 可扩展 | 若需本地离线 (Vosk / Whisper.cpp) 或其他云(Google / AWS),只要把 ② 识别器创建 换成对应 SDK 即可,采集逻辑复用。 |
[Mic]
内容再做分段。👆 这就是“整个代码流程”的完整可运行版本。复制、替换密钥后即可体验。如需针对其他语音引擎或平台(macOS / Linux)再做适配,欢迎继续提问!