返回首页

C# 语音识别 VK Cloud 带钩子

文章描述了使用 VK Cloud API、NAudio 和全局键盘钩子在 C# 中实现语音识别实用工具。该应用在热键上录制音频,发送到服务器并通过剪贴板插入文本。完整代码带错误处理。

C# 中的语音输入:钩子 + VK Cloud ASR
Advertisement 728x90

用C#与VK云实现语音识别:低级钩子与NAudio集成

一位开发者创建了一个C#控制台应用程序,通过麦克风捕获语音,将音频发送至VK云语音识别API,并将识别出的文本插入剪贴板。录音的触发通过全局键盘钩子管理:按住右Ctrl键开始录音,松开则启动处理流程。NAudio负责WAV格式录音(16 kHz,16位,单声道),HttpClient则处理带有15秒超时的API请求。

该代码仅适用于Windows系统,且必须以管理员身份运行,因为使用了SetWindowsHookEx函数。VK云的访问令牌以常量形式硬编码——这是您个人仪表板中的服务密钥。

录音激活的全局钩子

低级键盘钩子(WH_KEYBOARD_LL)会拦截WM_KEYDOWN/WM_KEYUP事件。按下右Ctrl键(0xA3)可异步启动或停止录音。按下ESC键可退出应用程序。

Google AdInline article slot

关键P/Invoke声明如下:

[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern IntPtr SetWindowsHookEx(int idHook, LowLevelKeyboardProc lpfn, IntPtr hMod, uint dwThreadId);

[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool UnhookWindowsHookEx(IntPtr hhk);

在HookCallback中,检查lParam传来的vkCode。如果当前未在录音且未在处理,则启动一个Task.Run(() => StartRecordingAsync())任务。

音频捕获与处理

WaveInEvent配置为16,000 Hz采样率。当DataAvailable事件触发时,音频数据被写入临时文件voice_temp.wav,使用WaveFileWriter完成写入。停止录音后,检查文件大小是否超过1,000字节,若满足条件则继续执行语音识别。

Google AdInline article slot
var waveFormat = new WaveFormat(16000, 16, 1);
waveIn = new WaveInEvent { WaveFormat = waveFormat };
writer = new WaveFileWriter(OutputFileName, waveIn.WaveFormat);
waveIn.DataAvailable += (s, e) => writer?.Write(e.Buffer, 0, e.BytesRecorded);

在StopRecordingAndProcessAsync方法中,释放资源后延迟200毫秒,随后调用RecognizeSpeechAsync并传入Bearer认证令牌。

与VK云语音识别API集成

https://voice.mcs.mail.ru/asr发送POST请求,内容类型为audio/wave。响应结果使用Newtonsoft.Json.Linq解析,提取result.texts[0].punctuated_text字段获取带标点的识别文本。

var request = new HttpRequestMessage(HttpMethod.Post, "https://voice.mcs.mail.ru/asr");
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", token);
request.Content = content;
var response = await httpClient.SendAsync(request);

错误处理涵盖未授权(令牌过期)、超时及HTTP状态码异常。所有日志信息均输出至控制台,并配以表情符号增强视觉反馈。

Google AdInline article slot
  • 成功识别:文本自动粘贴至剪贴板,日志显示「✅ 已识别」
  • 文件过短:若音频小于1,000字节,直接跳过处理
  • API错误:发出蜂鸣声并显示详细错误信息

通过STA线程插入文本

Clipboard.SetText方法需要STA(单线程公寓)环境支持。为此,创建一个新线程并设置其ApartmentState为STA,然后调用Join()等待操作完成。

Thread staThread = new Thread(() => {
    Clipboard.SetText(text, TextDataFormat.Text);
});
staThread.SetApartmentState(ApartmentState.STA);
staThread.Start();
staThread.Join();

替代方案包括SendKeys或UI自动化,但此方法通过模拟Ctrl+V操作,稳定可靠。

核心要点

  • 全局钩子:WH_KEYBOARD_LL需管理员权限,可注入任意进程
  • 音频格式:16 kHz / 16位 / 单声道 —— 最适配VK云语音识别引擎
  • 异步设计:使用Task.Run避免阻塞键盘钩子线程
  • 安全性:令牌嵌入代码中 —— 仅限个人使用,切勿提交至代码仓库
  • 局限性:输出依赖剪贴板而非直接输入;每篇语音约产生3卢布存储成本

— Editorial Team

Advertisement 728x90

继续阅读