Voz
APX tiene una capa de voz unificada: una fachada TTS (texto a voz) para hablar texto
y un sidecar STT (voz a texto) basado en Whisper para transcribir el audio del micrófono.
La CLI apx voice expone ambos. La ventana de Desktop
y el Deck usan los mismos motores subyacentes.
Comandos
Sección titulada «Comandos»# Synthesize and playapx voice say "Hello, world!"apx voice say "Hello" --provider piperapx voice say "Hello" --provider gemini --voice Aoedeapx voice say "Hello" --no-play # generate file, skip playback
# Listen (mic → STT → super-agent → TTS reply)apx voice listen # stop on silence (sox required)apx voice listen --seconds 5 # fixed-duration captureapx voice listen --seconds 5 --no-play # transcribe + agent, no audio playback
# List configured providersapx voice providers--provider sobrescribe el default configurado solo para esa llamada. --voice
sobrescribe el ID de voz dentro de un proveedor (soportado por OpenAI, ElevenLabs
y Gemini). --no-play escribe el archivo de audio en ~/.apx/tmp/tts/ e imprime
la ruta sin reproducirlo.
$ apx voice providers TTS providers default: piper ✓ piper local es_AR-daniela-high.onnx ✓ elevenlabs cloud eleven_multilingual_v2 key ••••a91f ✓ openai cloud tts-1 key ••••7c2d ⚠ gemini cloud no key — run apx config set voice.tts.gemini.api_key • mock local silent WAV (tests only) STT ✓ whisper local model base lang es
Proveedores
Sección titulada «Proveedores»| ID | ¿Local? | ¿Clave necesaria? | Calidad | Notas |
|---|---|---|---|---|
piper | sí | no | Buena | Recomendación por defecto. Requiere el binario piper + modelo .onnx. |
elevenlabs | no | sí | Excelente | Tier gratuito: 10 k caracteres/mes. Modelo eleven_multilingual_v2. |
openai | no | sí | Buena | Reutiliza engines.openai.api_key. Modelo tts-1. |
gemini | no | sí | Buena | Devuelve L16 PCM crudo — APX lo envuelve en WAV automáticamente. Soporta tags de emoción. |
custom:<slug> | depende | opcional | Depende | Cualquier servidor de voz compatible con OpenAI (ej. una instancia local de QVox/Qwen3-TTS). Soporta tags de emoción. |
mock | sí | no | Silencioso | Placeholder de WAV silencioso. Útil solo para tests. |
El proveedor auto prueba en orden: piper → elevenlabs → openai → gemini → mock.
Los proveedores custom solo se usan si los agregás al orden de la cadena. En una
instalación nueva sin ningún proveedor configurado, auto cae hasta mock
(silencio). Configurá al menos un proveedor real antes de esperar audio.
Configuración
Sección titulada «Configuración»La configuración de los proveedores vive en ~/.apx/config.json bajo voice.tts:
{ "voice": { "tts": { "provider": "piper", "piper": { "bin": "piper", "model": "/home/you/.apx/voices/es_AR-daniela-high.onnx" }, "elevenlabs": { "api_key": "...", "model": "eleven_multilingual_v2", "voice_id": "..." }, "openai": { "api_key": "...", "model": "tts-1", "voice": "alloy", "format": "mp3" }, "gemini": { "api_key": "...", "model": "gemini-2.5-flash-preview-tts", "voice": "Aoede" } } }}Cambiá el proveedor activo con:
apx config set voice.tts.provider piperTambién podés gestionar los proveedores desde el panel Web
bajo Voces (/m/voice).
Rutas de configuración
Sección titulada «Rutas de configuración»Piper (local, sin internet)
Sección titulada «Piper (local, sin internet)»Piper corre totalmente offline. Necesitás el binario piper y un modelo de voz .onnx.
-
Instalá el binario de Piper:
Ventana de terminal curl -L https://github.com/rhasspy/piper/releases/latest/download/piper_macos_aarch64.tar.gz \-o /tmp/piper.tar.gzsudo tar xzf /tmp/piper.tar.gz -C /usr/local/bin --strip-components=1Ventana de terminal curl -L https://github.com/rhasspy/piper/releases/latest/download/piper_linux_x86_64.tar.gz \-o /tmp/piper.tar.gzsudo tar xzf /tmp/piper.tar.gz -C /usr/local/bin --strip-components=1 -
Descargá un modelo de voz. Para español argentino (recomendado):
Ventana de terminal mkdir -p ~/.apx/voicescd ~/.apx/voicescurl -LO https://huggingface.co/rhasspy/piper-voices/resolve/main/es/es_AR/daniela/high/es_AR-daniela-high.onnxcurl -LO https://huggingface.co/rhasspy/piper-voices/resolve/main/es/es_AR/daniela/high/es_AR-daniela-high.onnx.json -
Apuntá APX hacia él:
Ventana de terminal apx config set voice.tts.provider piperapx config set voice.tts.piper.model "$HOME/.apx/voices/es_AR-daniela-high.onnx" -
Probá:
Ventana de terminal apx voice say "hola mundo" --provider piper
Gemini cloud (lo más rápido con una clave existente)
Sección titulada «Gemini cloud (lo más rápido con una clave existente)»apx config set voice.tts.provider geminiapx config set voice.tts.gemini.api_key '<YOUR_GEMINI_KEY>'apx voice say "hola mundo" --provider geminiElevenLabs
Sección titulada «ElevenLabs»apx config set voice.tts.provider elevenlabsapx config set voice.tts.elevenlabs.api_key '<YOUR_11L_KEY>'apx config set voice.tts.elevenlabs.voice_id '<VOICE_ID>'apx voice say "hola mundo" --provider elevenlabsProveedores custom compatibles con OpenAI (QVox / Qwen3-TTS)
Sección titulada «Proveedores custom compatibles con OpenAI (QVox / Qwen3-TTS)»Además de los proveedores incorporados, podés apuntar APX a cualquier servidor
de voz compatible con OpenAI — por ejemplo una instancia local de QVox /
Qwen3-TTS. Los proveedores custom viven bajo voice.tts.custom.<slug> y se
sirven todos a través del mismo adaptador openai; cada uno aparece en la
cadena con el id de motor custom:<slug>.
{ "voice": { "tts": { "custom": { "qvox": { "base_url": "http://127.0.0.1:5111/v1", "api_key": "", "model": "qwen3-tts", "voice": "default", "label": "QVox (local)" } } } }}base_url es solo configuración — nunca está hardcodeado, así que
custom:<slug> funciona con cualquier servidor compatible con OpenAI, no solo
QVox. El manejo de claves y de los requests difiere del OpenAI estándar cuando
hay base_url configurado:
- El motor usa únicamente su propia
api_key(a menudo vacía para un servidor local/abierto) — nunca cae haciaengines.openai.api_keyniOPENAI_API_KEY, así que tu clave de OpenAI nunca se filtra a un endpoint de terceros. - Campos extra que la API estándar de OpenAI no acepta —
instruct(la voz/ estilo base),languageytemperature— se envían solo a los endpoints custom. - El formato de respuesta por defecto para endpoints custom es
wav(OpenAI estándar usamp3por defecto).
Agregá y gestioná proveedores custom desde el panel Web
bajo Voces (/m/voice) — Agregar/Quitar un proveedor custom, editar
base_url y los campos avanzados de modelo/voz ahí en vez de editar el JSON a mano.
Tags de emoción (capacidad por motor)
Sección titulada «Tags de emoción (capacidad por motor)»Algunos backends de TTS — hoy los proveedores custom:<slug> y gemini —
pueden interpretar marcadores [tag] inline en el texto y cambiar la emoción
hablada por segmento (ej. [excited] ¡Listo! [calm] Lo dejé anotado.). Esto es
una capacidad por motor, opt-in, no una feature hardcodeada de QVox:
activala con voice.tts.<id>.emotions.enabled (o
voice.tts.custom.<slug>.emotions.enabled para proveedores custom).
{ "voice": { "tts": { "custom": { "qvox": { "base_url": "http://127.0.0.1:5111/v1", "emotions": { "enabled": true, "tags": ["happy", "sad", "excited", "angry", "calm", "whisper", "shout", "laugh", "cry", "narrator", "neutral"] } } } } }}tags es opcional — si se omite, cae al conjunto canónico de arriba (refleja
el set de tags por defecto de QVox).
Cuando las emociones están habilitadas para el motor que efectivamente va a
hablar la respuesta, APX inyecta una guía breve en el system prompt del modo
voz para que el agente conozca la sintaxis [tag] y la use con moderación. La
guía siempre coincide con el primer motor habilitado en la cadena configurada
— no cualquier motor con capacidad de tags — así el agente nunca emite tags
que termine hablando un motor distinto (sin soporte de tags).
Los tags son una señal exclusiva de TTS:
- Se mantienen en el texto que recibe el motor de TTS, para que un motor con soporte de tags pueda actuar sobre ellos.
- Se quitan de todo lo que el usuario lee — el bubble del chat, el historial
de mensajes y el índice de RAG — vía
stripEmotionTags(). En un motor que no soporta tags, esta misma función limpia cualquier marcador suelto antes de la síntesis para que nunca se lean en voz alta literalmente.
Desde el módulo Voces del panel Web, cada fila de proveedor con capacidad de tags muestra un toggle compacto de Emotions para activar/desactivar la capacidad por motor sin abrir el diálogo completo de configuración.
El endpoint unificado de turno de voz
Sección titulada «El endpoint unificado de turno de voz»POST /voice/turn es un único round-trip bidireccional:
- STT — transcribe el audio entrante (o acepta
textdirectamente, salteando el STT). - Agente — corre el super-agente sobre el texto transcrito.
- TTS — sintetiza la respuesta y devuelve una ruta a un archivo de audio.
# Drive from curl with pre-transcribed textcurl -X POST http://127.0.0.1:7430/voice/turn \ -H "Authorization: Bearer $(cat ~/.apx/daemon.token)" \ -H "Content-Type: application/json" \ -d '{"text": "What tasks are open?", "channel": "voice"}'Respuesta:
{ "user_text": "What tasks are open?", "reply_text": "You have 3 open tasks…", "reply_audio_path": "/home/you/.apx/tmp/tts/reply-abc123.wav", "reply_duration_s": 4.1, "reply_mime": "audio/wav", "provider": "piper"}La ventana de Desktop y el Deck
usan internamente este endpoint. El campo channel (voice, deck,
desktop, telegram) controla el formato de la respuesta: los canales voice/deck reciben
respuestas cortas y aptas para hablar; telegram recibe texto formateado en Markdown.
Voz a texto (STT)
Sección titulada «Voz a texto (STT)»El motor STT local está basado en Whisper y se adapta a tu hardware en vez de obligarte a elegir entre CTranslate2/MLX/whisper.cpp vos mismo:
| Hardware | Backend recomendado | Modelo |
|---|---|---|
| Apple Silicon (Metal) | mlx (mlx-whisper, GPU/Neural Engine) | mlx-community/whisper-large-v3-turbo |
| NVIDIA (CUDA) | faster (faster-whisper, CUDA) | large-v3 |
| AMD / Radeon | faster (CPU — CTranslate2 no soporta ROCm) | small |
| Solo CPU | faster (CPU) | small |
transcription.local.backend puede ser auto (default, usa la tabla de
arriba), mlx o faster. Desde el módulo Voces del panel Web,
la card de STT muestra un bloque de Hardware detectado (badge de
aceleración — Metal/CUDA/Vulkan-ROCm/CPU — más nombre de GPU y memoria total)
y un selector de Aceleración/Motor para elegir el backend explícitamente;
la lista de modelos se adapta al backend elegido y muestra el estado de
descarga en disco por modelo.
Venv dedicado (aísla mlx de tu Python de sistema)
Sección titulada «Venv dedicado (aísla mlx de tu Python de sistema)»mlx-whisper trae consigo torch/scipy/numba, lo que puede contaminar o
generar conflictos con tus otros proyectos de Python si se instala en el
intérprete de sistema o user-site. APX en cambio es dueño de un virtualenv
dedicado en ~/.apx/runtime/whisper-venv: lo crea, instala
faster-whisper/mlx-whisper ahí adentro, y el proceso del servidor whisper
arranca usando el intérprete de ese venv. Si el venv todavía no existe (ej.
una instalación vieja), APX cae al python3 del sistema — el camino legacy
que instala los paquetes de Whisper en el user-site.
El idioma se fuerza desde la config, no se autodetecta
Sección titulada «El idioma se fuerza desde la config, no se autodetecta»STT resuelve el idioma efectivo de transcripción con esta prioridad: un
transcription.local.language explícito, si no config.user.language, si no
"auto" (Whisper autodetecta). El camino de captura de Desktop antes siempre
mandaba un header de idioma auto a /transcribe/chunk, lo que sobreescribía
tu idioma configurado y perjudicaba la precisión en clips cortos y nombres
propios. /transcribe/chunk ahora solo sobreescribe el idioma resuelto cuando
quien llama fija uno real (no "auto") — así que tu idioma configurado siempre
se mantiene salvo que pidas autodetección explícitamente.
Reproducción
Sección titulada «Reproducción»apx voice say usa el primer reproductor del sistema disponible:
afplay (macOS) → paplay → aplay → play (sox) → ffplay.
Si no se encuentra ninguno, el archivo se escribe y se imprime la ruta — sin error, pero sin sonido. Instalá cualquiera de los anteriores para tener reproducción.
Solución de problemas
Sección titulada «Solución de problemas»apx voice providers muestra solo mock
No hay ningún proveedor real configurado. Seguí una de las rutas de configuración de arriba.
El archivo se genera pero no hay sonido
No hay ningún reproductor de audio del sistema en tu PATH. En macOS afplay está siempre presente.
En Linux instalá sox (apt install sox) o pulseaudio-utils.
La salida de Gemini suena distorsionada o no se reproduce
Las versiones más viejas de APX no envolvían el L16 PCM crudo en un header WAV. Actualizá APX o
convertí con ffmpeg -f s16le -ar 24000 -ac 1 -i raw.pcm out.wav.
apx voice listen devuelve texto vacío
sox es requerido para la grabación con detección de silencio. Instalalo o usá
--seconds N para especificar una duración fija en su lugar.
$ apx voice say "hello" --provider piper ✓ provider piper (local) ✓ voice en_US-amy-medium • sintetizando 1 oración … 0.4s ♪ reproduciendo 1.2s de audio ✓ listo
Relacionado
Sección titulada «Relacionado»- Desktop — ventana flotante que usa TTS/STT
- Deck — modo de voz de la app companion
- Panel Web → módulo Voces — UI para la configuración de proveedores