用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键可退出应用程序。
关键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字节,若满足条件则继续执行语音识别。
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状态码异常。所有日志信息均输出至控制台,并配以表情符号增强视觉反馈。
- 成功识别:文本自动粘贴至剪贴板,日志显示「✅ 已识别」
- 文件过短:若音频小于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
暂无评论。