Powrót do strony głównej

C# rozpoznawanie mowy VK Cloud z hookami

Artykuł opisuje realizację narzędzia do rozpoznawania mowy w C# z wykorzystaniem VK Cloud API, NAudio i globalnych hooków klawiaturowych. Aplikacja nagrywa audio za pomocą klawiszy skrótu, wysyła na serwer i wstawia tekst przez schowek. Pełny kod z obsługą błędów.

Wprowadzanie głosowe w C#: hooki + VK Cloud ASR
Advertisement 728x90

Rozpoznawanie mowy w C# z VK Cloud: niskopoziomowe hooki i NAudio

Rozwój aplikacji konsolowej w C#, która przekształca głos przez mikrofon, wysyła dźwięk do API rozpoznawania mowy VK Cloud i wkleja rozpoznaną treść do schowka. Sterowanie odbywa się za pomocą globalnych hooków klawiatury: przytrzymanie prawego Ctrl aktywuje nagrywanie, puścienie — przetwarzanie. Do nagrywania wykorzystano NAudio (format WAV: 16 kHz, 16 bitów, mono), a żądania API są realizowane przez HttpClient z timeoutem 15 sekund.

Kod jest przeznaczony dla systemu Windows i wymaga uruchomienia z uprawnieniami administratora ze względu na użycie SetWindowsHookEx. Token do VK Cloud jest wprowadzany jako stała — klucz usługowy z panelu użytkownika.

Globalne hooki do aktywacji nagrywania

Niskopoziomowy hook klawiatury (WH_KEYBOARD_LL) przechwytuje zdarzenia WM_KEYDOWN/WM_KEYUP. Prawy klawisz Ctrl (0xA3) uruchamia lub zatrzymuje nagrywanie asynchronicznie. Klawisz ESC kończy działanie aplikacji.

Google AdInline article slot

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

W HookCallback sprawdzana jest wartość vkCode z lParam. Jeśli !isRecording && !isProcessing, uruchamiany jest Task.Run(() => StartRecordingAsync()).

Przechwytywanie i przetwarzanie dźwięku

WaveInEvent konfiguruje się na częstotliwość 16 000 Hz. Zdarzenie DataAvailable zapisuje dane do tymczasowego pliku voice_temp.wav za pomocą WaveFileWriter. Po zakończeniu nagrywania sprawdzany jest rozmiar pliku (>1000 bajtów), po czym następuje rozpoznawanie mowy.

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

W StopRecordingAndProcessAsync: zwalniane są zasoby, dodatkowa opóźnienie 200 ms, następnie wywoływana jest funkcja RecognizeSpeechAsync z tokenem Bearer.

Integracja z API rozpoznawania mowy VK Cloud

Zapytanie POST na https://voice.mcs.mail.ru/asr z treścią typu audio/wave. Odpowiedź parsowana jest przez 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);

Obsługa błędów: Unauthorized (token wygasł), timeout, błędy HTTP. Logi wyświetlane są w konsoli z emotikonami do wizualnej sygnalizacji.

Google AdInline article slot
  • Sukces: tekst wklejony do schowka, log "✅ Rozpoznano".
  • Krótki plik: pominięcie (<1000 bajtów).
  • Błąd API: sygnał dźwiękowy + szczegółowe informacje.

Wklejanie tekstu przez wątek STA

Clipboard.SetText wymaga środowiska STA (Single-Threaded Apartment). Tworzony jest osobny wątek z ustawionym stanem SetApartmentState(STA), a Join() oczekuje na jego zakończenie.

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

Alternatywa: SendKeys lub UI Automation, ale obecny sposób działa poprzez Ctrl+V.

Co warto pamiętać

  • Globalne hooki: WH_KEYBOARD_LL wymaga uprawnień administratora i może być wstrzykiwany do dowolnego procesu.
  • Format dźwięku: 16 kHz / 16 bitów / mono — optymalny dla ASR VK Cloud.
  • Asynchroniczność: Task.Run zapobiega blokadzie hooka.
  • Bezpieczeństwo: token w kodzie — tylko do użytku prywatnego, nie wypychaj go do repozytorium.
  • Ograniczenia: schowek zamiast bezpośredniego wklejenia, koszt magazynowania ok. 3 zł za artykuł.

— Editorial Team

Advertisement 728x90

Czytaj dalej