2026-11-03

Texto a Voz con AutoHotkey, integración con Loquendo.

Lectura de texto seleccionada a golpe de tecla: integrando motores de voz en AutoHotkey

Si usás AutoHotkey v2 en un entorno de 64 bits y querés integrar síntesis de voz tradicional por comandos rápidos, te enfrentás a una limitación de arquitectura: los procesos de 64 bits no pueden acceder directamente a componentes de 32 bits (como las voces SAPI5 clásicas).

Para solucionar esto sin depender de software pesado, obsoleto o intermediarios externos, podemos implementar un esquema modular basado en un script auxiliar de 32 bits y archivos temporales como búfer. Además, este enfoque permite ampliar el sistema para utilizar motores de voz locales más modernos (como Piper).

1. Estructura central y archivo main.ahk

Para mantener la prolijidad en scripts avanzados, la mejor práctica es utilizar un archivo centralizador que cargue los diferentes módulos funcionales mediante directivas #Include.

Ejemplo de tu main.ahk[cite: 5]:

#Requires AutoHotkey v2.0
#SingleInstance Force
SendMode "Input"
SetWorkingDir A_ScriptDir
SetKeyDelay 10, 10

; Disparo inmediato: ningún hotstring espera tecla terminadora
#Hotstring *
; SendEvent para hotstrings: evita que backspaces y texto nuevo se superpongan
#Hotstring SI

; Inclusión de módulos y scripts secundarios
#Include "frases.ahk"
#Include "hotkeys.ahk"
#Include "hablar_jorge.ahk"
#Include "piper.ahk"

2. El módulo de control: hablar_jorge.ahk

Este módulo maneja las pulsaciones de teclado[cite: 1]. Si seleccionás texto, lo captura; si no hay selección, evalúa el portapapeles[cite: 1]. Guarda el contenido en un archivo temporal para evitar problemas de caracteres en la consola y lanza el proceso auxiliar[cite: 1].

También incluye atajos para detener la locución al instante o repetir lo último leído[cite: 1].

; ==========================================
; Módulo: Hablar con Loquendo Jorge (SAPI5)
; ==========================================

global loquendoPID := 0

^!F6::
{
    global loquendoPID
    
    if (loquendoPID) {
        try ProcessClose(loquendoPID)
        loquendoPID := 0
    }
    
    clipSaved := ClipboardAll()
    A_Clipboard := ""
    Send "^c"
    
    if !ClipWait(0.3) {
        A_Clipboard := clipSaved
    }
    
    textoALeer := A_Clipboard
    A_Clipboard := clipSaved
    
    if (textoALeer != "" && Type(textoALeer) = "String") {
        ; Pausa inicial para evitar que el motor devore la primera sílaba
        textoALeerModificado := "  . " . textoALeer
        
        tempFile := A_Temp . "\loquendo_buffer.txt"
        if FileExist(tempFile)
            FileDelete(tempFile)
        FileAppend(textoALeerModificado, tempFile, "UTF-8")
        
        ahk32 := A_ProgramFiles . "\AutoHotkey\v2\AutoHotkey32.exe"
        if !FileExist(ahk32)
            ahk32 := A_AppData . "\AutoHotkey\v2\AutoHotkey32.exe"
            
        scriptAux := A_ScriptDir . "\hablar_jorge_aux.ahk"
        
        if FileExist(ahk32) and FileExist(scriptAux) {
            Run('"' . ahk32 . '" "' . scriptAux . '"', , "Hide", &loquendoPID)
        }
    }
}

; Ctrl+Alt+F9 → Detener reproducción de audio general
^!F9::
{
    try ProcessClose("ffplay.exe")
    global loquendoPID
    if (loquendoPID) {
        try ProcessClose(loquendoPID)
        loquendoPID := 0
    }
}

; Ctrl+Alt+Shift+F6 → Relee lo último almacenado en el búfer
^+!F6::
{
    global loquendoPID
    if !FileExist(A_Temp "\loquendo_buffer.txt")
        return
    if (loquendoPID) {
        try ProcessClose(loquendoPID)
        loquendoPID := 0
    }
    ahk32 := A_ProgramFiles . "\AutoHotkey\v2\AutoHotkey32.exe"
    if !FileExist(ahk32)
        ahk32 := A_AppData . "\AutoHotkey\v2\AutoHotkey32.exe"
    scriptAux := A_ScriptDir . "\hablar_jorge_aux.ahk"
    if FileExist(ahk32) and FileExist(scriptAux)
        Run('"' . ahk32 . '" "' . scriptAux . '"', , "Hide", &loquendoPID)
}

3. El script auxiliar de 32 bits: hablar_jorge_aux.ahk

Al ejecutarse bajo el motor de 32 bits[cite: 4], este archivo interactúa sin trabas con SAPI5 para localizar la voz e interpretar el búfer de texto plano[cite: 4].

#Requires AutoHotkey v2.0 32-bit
#SingleInstance Ignore

tempFile := A_Temp . "\loquendo_buffer.txt"

if FileExist(tempFile) {
    textoALeer := FileRead(tempFile, "UTF-8")
    
    if (textoALeer != "") {
        try {
            v := ComObject("SAPI.SpVoice")
            for x in v.GetVoices() {
                if InStr(StrLower(x.GetDescription()), "jorge") {
                    v.Voice := x
                    break
                }
            }
            v.Rate := 20
            v.Speak(textoALeer, 0)
        }
    }
}

4. Instalación y configuración de Piper

Piper es un motor de síntesis de voz neuronal rápido y de ejecución 100% local. Para integrarlo con este sistema en Windows, seguí estos pasos:

  1. Creá una carpeta en el directorio raíz de tu disco (por ejemplo, C:\piper).
  2. Descargá la última versión ejecutable de Piper para Windows desde su repositorio oficial en GitHub y descomprimila dentro de C:\piper (necesitarás el archivo piper.exe).
  3. Descargá un modelo de voz compatible en formato ONNX y su archivo de configuración JSON correspondiente (por ejemplo, el modelo en español argentino es_AR-daniela-high.onnx) y guardalos en la misma carpeta C:\piper.
  4. Asegurate de contar con el reproductor ffplay.exe (perteneciente a FFmpeg) ubicado también en C:\piper, ya que es el encargado de procesar y reproducir el flujo de audio crudo en tiempo real.

Una vez colocado todo en la ruta indicada, el módulo piper.ahk se encargará de coordinar la lectura mediante tuberías de consola:

#Requires AutoHotkey v2.0

; Ctrl + Alt + F10 → Lee texto con Piper
^!F10::
{
    clipSaved := ClipboardAll()
    A_Clipboard := ""
    Send "^c"
    
    if !ClipWait(0.3)
        A_Clipboard := clipSaved

    textToRead := A_Clipboard
    A_Clipboard := clipSaved
    clipSaved := ""

    if Trim(textToRead) == ""
        return

    piperExe := "C:\piper\piper.exe"
    modelPath := "C:\piper\es_AR-daniela-high.onnx"
    ffplayExe := "C:\piper\ffplay.exe"
    speedFactor := "0.8"

    tempFile := A_Temp . "\piper_read_buffer.txt"
    if FileExist(tempFile)
        FileDelete(tempFile)
    FileAppend(textToRead, tempFile, "UTF-8")

    ProcessClose("ffplay.exe")

    cmd := Format('"{1}" /c "{2} --model {3} --length-scale {4} --output_raw < {5} | {6} -f s16le -ar 22050 -ch_layout mono -nodisp -autoexit -"', A_ComSpec, piperExe, modelPath, speedFactor, tempFile, ffplayExe)
    Run(cmd, "C:\piper", "Hide")
}

; Ctrl+Alt+Shift+F10 → Relee lo último con Piper
^+!F10::
{
    archivo := A_Temp "\piper_read_buffer.txt"
    if !FileExist(archivo)
        return

    try ProcessClose("ffplay.exe")
    cmd := Format('"{1}" /c "C:\piper\piper.exe --model C:\piper\es_AR-daniela-high.onnx --length-scale 0.8 --output_raw < {2} | C:\piper\ffplay.exe -f s16le -ar 22050 -ch_layout mono -nodisp -autoexit -"', A_ComSpec, archivo)
    Run(cmd, "C:\piper", "Hide")
}

5. Puesta en marcha automática con Windows

Para que tus macros estén operativas apenas enciendas la PC:

  1. Presioná Windows + R, escribí shell:startup y pulsá Enter.
  2. Hacé clic derecho en la carpeta de inicio y elegí Nuevo > Acceso directo.
  3. Seleccioná tu archivo main.ahk, guardalo con un nombre descriptivo y listo.