Reconocimiento de voz en C# con VK Cloud: Ganchos de bajo nivel y NAudio
Un desarrollador creó una aplicación de consola en C# que captura voz mediante micrófono, envía el audio a la API de reconocimiento de voz de VK Cloud y inserta el texto reconocido en el portapapeles. La activación se gestiona mediante ganchos de teclado globales: mantener presionada la tecla Ctrl derecha inicia la grabación, soltarla desencadena el procesamiento. NAudio maneja la grabación WAV (16 kHz, 16 bits, mono), mientras HttpClient gestiona las solicitudes a la API con un tiempo de espera de 15 segundos.
El código es específico de Windows y requiere ejecutarse como administrador debido a SetWindowsHookEx. El token de VK Cloud está codificado como una constante — se trata de una clave de servicio desde tu panel personal.
Ganchos globales para activar la grabación
El gancho de teclado de bajo nivel (WH_KEYBOARD_LL) intercepta los eventos WM_KEYDOWN/WM_KEYUP. Presionar la tecla Ctrl derecha (0xA3) inicia o detiene la grabación de forma asíncrona. Presionar ESC cierra la aplicación.
Declaraciones P/Invoke clave:
[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);
En HookCallback, se verifica vkCode desde lParam. Si !isRecording && !isProcessing, se inicia un Task.Run(() => StartRecordingAsync()).
Captura y procesamiento de audio
WaveInEvent se configura a 16.000 Hz. DataAvailable escribe los datos en un archivo temporal voice_temp.wav usando WaveFileWriter. Tras detenerse, se comprueba el tamaño del archivo (>1.000 bytes), luego se procede al reconocimiento.
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);
En StopRecordingAndProcessAsync: se liberan los recursos, se espera 200 ms, luego se llama a RecognizeSpeechAsync con un token Bearer.
Integración con la API de reconocimiento de voz de VK Cloud
Solicitud POST a https://voice.mcs.mail.ru/asr con tipo de contenido audio/wave. La respuesta se parsea usando 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);
El manejo de errores incluye Unauthorized (token caducado), tiempos de espera y códigos de estado HTTP. Los registros aparecen en la consola con emojis para retroalimentación visual.
- Éxito: El texto se coloca en el portapapeles, el registro muestra "✅ Reconocido".
- Archivo corto: Se omite si tiene menos de 1.000 bytes.
- Error de API: Sonido de alerta + detalles mostrados.
Inserción de texto mediante hilo STA
Clipboard.SetText requiere un contexto STA (Single-Threaded Apartment). Se crea un hilo separado con SetApartmentState(STA), luego Join() espera la finalización.
Thread staThread = new Thread(() => {
Clipboard.SetText(text, TextDataFormat.Text);
});
staThread.SetApartmentState(ApartmentState.STA);
staThread.Start();
staThread.Join();
Otras alternativas incluyen SendKeys o Automatización de interfaz, pero este método funciona de forma confiable mediante Ctrl+V.
Puntos clave
- Ganchos globales: WH_KEYBOARD_LL requiere privilegios de administrador e inyecta en cualquier proceso.
- Formato de audio: 16 kHz / 16 bits / mono — óptimo para ASR de VK Cloud.
- Asincronía: Task.Run evita bloqueos en el gancho.
- Seguridad: Token incrustado en el código — pensado solo para uso personal; nunca lo subas a repositorios.
- Limitaciones: Salida basada en portapapeles en lugar de entrada directa; costo de almacenamiento ~3 rublos por artículo.
— Editorial Team
Aún no hay comentarios.