C# 使用开会的音频实时转换为文本的库,并且要区分麦克风和扬声器的输出音频,有那些方式,实现一个de...

Creado el: 11 de junio de 2025

Respondido usando O3 Pro por Chat01

Pregunta

C# 使用开会的音频实时转换为文本的库,并且要区分麦克风和扬声器的输出音频,有那些方式,实现一个demo

Pensamiento

Investigating languages

I’m evaluating C# libraries for real-time conference audio transcription, distinguishing microphone and speaker outputs, and offering demo code ideas.

Choosing options

I’m piecing together various libraries to handle real-time meeting transcription in C#, focusing on audio capture and speech-to-text. This involves examining NAudio, Azure Speech SDK, Google Cloud Speech-to-Text, and offline solutions like Vosk or Whisper.

Reasoned for 15m 30s

Respuesta

下面按“思路 → 方案 → 参考 Demo 代码”的顺序整理,帮助你用 C# 在会议场景下 实时把声音转文字,同时 区分麦克风(Mic)与扬声器回放(Loopback/Speaker) 的输出。


1 · 整体思路

模块关键点推荐做法
音频采集Windows 上把两个通路分离NAudio<br/>• WasapiCapture → 麦克风<br/>• WasapiLoopbackCapture → 扬声器(系统回环) github.com
实时传输把原始 PCM 流推送给语音识别引擎选择兼容 Push/Stream 输入的 SDK(见下)
语音识别准确率、延迟、成本、离/在线下面列出 5 条主流库
并发处理两路同时转写 & 标记来源启两条后台任务/线程,各自独立实例,输出时加标签 [Mic] / [Speaker]
后处理去噪、打断点、整合字幕可再做段落合并、时间戳对齐等

2 · 可选语音转写库(C# 可用)

类型库 / 服务特点备注
云端 SaaSAzure AI Speech SDK官方 .NET SDK;支持 PushAudioInputStream;有自动标点、说话人分离2025 年版 SDK 更新了多级识别示例 learn.microsoft.com learn.microsoft.com
Google Cloud Speech‑to‑TextgRPC + .Google.Cloud.Speech.V1 NuGet;中文表现好需启用“实时流式识别”
Amazon Transcribe Streaming官方 AWSSDK.TranscribeStreaming计费按秒
OpenAI Realtime API (Whisper‑v3)200 ms 级延迟;支持 C# WebSocket 客户端公测版 2024‑09 发布 openai.com
本地离线VoskLGPL;模型小,CPU 即可跑VoskSpeechRecognizer NuGet
Whisper.cpp / whisper.netOpenAI 模型本地推理;可 GPU内存占用 ~1 GB;延迟略高

若对 延迟最敏感:优先 Azure / OpenAI Realtime
离线、隐私:用 Vosk 或 Whisper.cpp


3 · 关键技术点

  1. 设备枚举
    csharp
    var devices = MMDeviceEnumerator.EnumerateAudioEndPoints(DataFlow.All, DeviceState.Active); foreach (var d in devices) Console.WriteLine($"{d.FriendlyName}  {d.DataFlow}");
  2. 双通路采集(NAudio)
    csharp
    var mic = new WasapiCapture(); // 默认麦克风 var speaker = new WasapiLoopbackCapture(); // 系统回放 mic.StartRecording(); speaker.StartRecording();
  3. 推送到 Azure Speech
    • 创建 AudioConfigPushAudioInputStream
    • DataAvailablestream.Write(buffer, 0, bytesRecorded);
    • 同时注册 recognizer.Recognized += … 读取文本
  4. 并发 + 标签
    Task.Run 分别启动 MicJob()SpeakerJob()
    回调里输出:Console.WriteLine($"[Mic] {e.Result.Text}");

4 · 完整 Demo(基于 Azure Speech SDK + NAudio)

NuGetMicrosoft.CognitiveServices.SpeechNAudio
前置:在 Azure 门户创建语音资源,取 speechKeyserviceRegion

csharp
using 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] 可以看到,谢谢!

5 · 替换为其他引擎

  • 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()


6 · 实战小贴士

场景建议
回声消除仍可能把扬声器声音漏进 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

7 · 结论

  • NAudio + 任意支持流式识别的语音 SDK 是最直接的“两路采集 + 实时转写”组合。
  • 如果想本地离线完全免费,可换 Vosk / Whisper;对低延迟和准确率要求极高时,考虑 Azure Speech 或 OpenAI Realtime。
  • 上面 Demo 可直接复制运行,按需替换 WasapiCapture 设备或语音服务即可。祝项目顺利!

Pregunta

能把整个代码流程都输出吗

Pensamiento

Crafting a comprehensive code

I'm assembling a detailed C# console application. This includes device enumeration, capturing audio from mic and loopback, streaming to Azure, handling concurrency, and ensuring smooth shutdown.

Reasoned for 13m 1s

Respuesta

下面给出 可直接 dotnet run 运行的完整示例项目(.NET 8 Console App)。
代码已包含:

  1. 双通路音频采集(麦克风 / 扬声器回环)
  2. 将 PCM 数据实时推送到 Azure AI Speech(流式识别)
  3. 识别回调统一打印到终端,标记来源 [Mic] / [Speaker]
  4. 自动清理资源、捕获异常

只需把 YOUR_KEY / YOUR_REGION 换成自己的 Azure 语音资源配置即可运行。


目录结构

text
RealtimeTranscriptionDemo/ ├── RealtimeTranscriptionDemo.csproj └── Program.cs <-- 主入口(全部逻辑都在这里)

1 · 项目文件 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>

2 · 主程序 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(); // ==================================================================== // &darr;&darr;&darr; 核心方法封装 &darr;&darr;&darr; // ==================================================================== 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(); }

3 · 运行步骤

以下命令假设你已安装 .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 发回剩余结果后退出。


4 · 关键点回顾

步骤说明
① 设备采集WasapiCapture 默认使用系统首选麦克风;WasapiLoopbackCapture 则抓取当前默认输出设备的回环。
② 数据管线每路都创建 PushAudioInputStreamAudioConfigSpeechRecognizer<br>回调里写入 pushStream.Write(),SDK 线程安全。
③ 并发RunChannelAsync 独立运行,两者互不干扰。<br>终端输出加 [Mic] / [Speaker] 区分。
④ 资源管理using 语法确保 SpeechRecognizerAudioConfigWasapiCapture 都正确释放。
⑤ 可扩展若需本地离线 (Vosk / Whisper.cpp) 或其他云(Google / AWS),只要把 ② 识别器创建 换成对应 SDK 即可,采集逻辑复用。

可能的改进

  • 回声消除:在麦克风通路前使用 WebRTC AEC 或硬件 AEC,减少扬声器音被再次识别。
  • 说话人分离:Azure 可开启 Speaker Diarization,或后处理把 [Mic] 内容再做分段。
  • GUI:用 WPF / WinUI 3 包装,可实时显示字幕、统计字数、保存 SRT 文件等。

👆 这就是“整个代码流程”的完整可运行版本。复制、替换密钥后即可体验。如需针对其他语音引擎或平台(macOS / Linux)再做适配,欢迎继续提问!

Compartir este Q&A