Back to Home

C# speech recognition VK Cloud with hooks

Article describes the implementation of a utility for speech recognition in C# using VK Cloud API, NAudio and global keyboard hooks. The application records audio on hotkeys, sends to the server and inserts text via clipboard. Full code with error handling.

Voice input in C#: hooks + VK Cloud ASR
Advertisement 728x90

Speech Recognition in C# with VK Cloud: Low-Level Hooks and NAudio

A developer created a console application in C# that captures speech via microphone, sends audio to the VK Cloud Speech API, and inserts recognized text into the clipboard. Activation is managed through global keyboard hooks: holding down the right Ctrl key starts recording, releasing it triggers processing. NAudio handles WAV recording (16 kHz, 16-bit, mono), while HttpClient manages API requests with a 15-second timeout.

The code is Windows-specific and requires running as administrator due to SetWindowsHookEx. The VK Cloud token is hardcoded as a constant—this is a service key from your personal dashboard.

Global Hooks for Recording Activation

The low-level keyboard hook (WH_KEYBOARD_LL) intercepts WM_KEYDOWN/WM_KEYUP events. Pressing the right Ctrl key (0xA3) asynchronously starts/stops recording. Pressing ESC exits the application.

Google AdInline article slot

Key P/Invoke declarations:

[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);

In HookCallback, vkCode from lParam is checked. If !isRecording && !isProcessing, a Task.Run(() => StartRecordingAsync()) is initiated.

Audio Capture and Processing

WaveInEvent is configured at 16,000 Hz. DataAvailable writes data to a temporary voice_temp.wav file using WaveFileWriter. After stopping, the file size is checked (>1,000 bytes), then recognition proceeds.

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);

In StopRecordingAndProcessAsync: resources are disposed, a 200ms delay occurs, then RecognizeSpeechAsync is called with a Bearer token.

Integration with VK Cloud Speech API

POST request to https://voice.mcs.mail.ru/asr with audio/wave content type. Response is parsed using 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);

Error handling includes Unauthorized (expired token), timeouts, and HTTP status codes. Logs appear in the console with emojis for visual feedback.

Google AdInline article slot
  • Success: Text is placed in clipboard, log shows "✅ Recognized".
  • Short file: Skipped if under 1,000 bytes.
  • API error: Beep sound + details displayed.

Inserting Text via STA Thread

Clipboard.SetText requires an STA (Single-Threaded Apartment) context. A separate thread is created with SetApartmentState(STA), then Join() waits for completion.

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

Alternative approaches include SendKeys or UI Automation, but this method works reliably via Ctrl+V.

Key Points

  • Global hooks: WH_KEYBOARD_LL requires admin privileges and injects into any process.
  • Audio format: 16 kHz / 16-bit / mono — optimal for VK Cloud ASR.
  • Asynchronicity: Task.Run prevents blocking the hook.
  • Security: Token embedded in code — intended for personal use only; never commit to repositories.
  • Limitations: Clipboard-based output instead of direct input; storage cost ~3 RUB per article.

— Editorial Team

Advertisement 728x90

Read Next