<!-- Markdown mirror. Canonical: https://empryo.com/docs/surfaces/voice-input -->

# Voice input

> Speak prompts instead of typing across Empryo's TUI, desktop app, and headless CLI. Record locally, transcribe with your provider key, then review or send.

Voice input lets you speak a prompt instead of typing it on the TUI, desktop app, or headless CLI. Each surface records a short clip, transcribes it, and inserts the transcript or sends it based on your config.

## The zero-setup way: your OS's dictation

The fastest path needs nothing from Empryo: your OS ships live dictation that types
into any focused field, including Empryo's composers. macOS: turn on
**System Settings → Keyboard → Dictation**, focus the composer, tap the 🎤/Fn key and
speak (words stream in on-device). Windows: **Win+H**. No key, no install, no config.
The built-in pipeline below is for when you want higher accuracy on technical vocabulary,
auto-send, headless runs, or Linux.

## Free and offline: local whisper.cpp

No API key needed. Install whisper.cpp, then let Empryo fetch the model:

```bash
brew install whisper-cpp          # macOS. Linux/Windows: grab the official
                                  # prebuilt from github.com/ggml-org/whisper.cpp/releases
                                  # (whisper-bin-ubuntu-*.tar.gz / whisper-bin-x64.zip)
                                  # and put whisper-cli on PATH. Linux needs libgomp1.
```

In the TUI, `/voice setup` downloads a model into the auto-discovered dir and tells
you when the mic is ready, no config, no key. `/voice setup tiny.en` (~75 MB,
fastest), `base.en` (default, ~142 MB), `small.en` (~466 MB, most accurate English),
or `large-v3-turbo-q5_0` (~547 MB, best quality, all languages). Downloads are
atomic. A torn download is never picked up. Or fetch by hand:

```bash
mkdir -p ~/.local/share/whisper-cpp
curl -L -o ~/.local/share/whisper-cpp/ggml-base.en.bin \
  https://huggingface.co/ggerganov/whisper.cpp/resolve/main/ggml-base.en.bin
```

On Windows the model dir is `%USERPROFILE%\.local\share\whisper-cpp`. This lane is
verified on all three platforms, the same clip transcribes identically on macOS,
Linux, and Windows x64.

Searched dirs: `~/.local/share/whisper-cpp`, `~/.cache/whisper-cpp`, and brew's
`share/whisper-cpp/models`. With several models present the best is picked
(large-v3-turbo quantized first, tiny last). Pin one with `voice.localModel`, a bare
name (`"base.en"`) or a full path. For speed use `tiny.en`, for accuracy
`large-v3-turbo-q5_0`. The desktop app's recordings are converted via ffmpeg when
present.

## Use the TUI composer

If Empryo finds a recorder and an OpenAI or Groq key, press `Ctrl+Y` in the composer, click the mic chip, or enter `/voice`. The shortcut is remappable, see [Remap the mic shortcut](#remap-the-mic-shortcut).

1. Press `Ctrl+Y` or run `/voice` to start recording.
2. Speak while the composer shows `● REC`, the little VU bar beside the clock
   moves with your voice, so "is it hearing me" is answered at a glance.
3. Press `Ctrl+Y` again to stop. Press `Esc` to cancel and discard the recording.
4. Wait for `transcribing…`. The transcript is inserted into the draft for review.
5. Edit the draft if needed, then press `Enter` to send it.

The transcript is appended to any text already in the draft with a space. Set `voice.insertMode` to `"send"` to submit it automatically after transcription.

## Use the desktop app

If the app has microphone permission and an OpenAI or Groq key, click the mic button in the composer or press `⌘Y` (macOS) / `Ctrl+Y`, remappable via `voice.key`.

1. Click the mic button and speak into the system microphone.
2. Click the mic button again to stop and transcribe.
3. Press `Esc` while recording to cancel and discard the audio.
4. Review or edit the transcript in the draft, then submit it.

**Push-to-talk**: hold the mic key down, speak, and release, the recording stops
and transcribes the moment you let go. A quick tap (under 400 ms) behaves as the
classic toggle instead, so both styles work with no setting. The mic icon pulses
with your input level while recording.

The default `voice.insertMode` value is `"insert"`, so the transcript stays in the draft. Set it to `"send"` when you want the surface to submit after transcription.

## Use headless mode

If you have an interactive terminal, run:

```bash
empryo --headless --voice
```

The command records until you press `Enter` (the status line carries the clock and
a live VU bar), transcribes the recording, and uses the transcript as the prompt. If you also provide a typed or piped prompt, Empryo appends the transcript to it:

```bash
empryo --headless --voice "also update the tests"
```

## Configure transcription

Empryo uses your existing provider keys. When a Groq key is available, it uses Groq's fast `whisper-large-v3-turbo`, otherwise it uses OpenAI's `gpt-4o-mini-transcribe`. Set an explicit `provider/model` pair to override that choice.

```jsonc
{
  "voice": {
    "model": "openai/gpt-4o-transcribe",
    "language": "en",
    "insertMode": "insert"
  }
}
```

Use `"insert"` to put the transcript in the draft for review. Use `"send"` to submit it automatically. If you set `model`, keep the provider prefix as `openai/...` or `groq/...` and make sure that provider has a key.

### Remap the mic shortcut

`voice.key` rebinds the mic toggle on the TUI and the desktop app, one spec, both
surfaces:

```jsonc
{
  "voice": { "key": "mod+shift+v" }
}
```

Join tokens with `+`. The last token is the key, everything before it a modifier:

| Token | Meaning |
| --- | --- |
| `ctrl` / `control` | Control |
| `cmd` / `meta` / `command` / `super` | Command / Win |
| `mod` | Command **or** Control, the cross-platform "primary" modifier |
| `alt` / `option` / `opt`, `shift` | Alt / Option, Shift |
| final token | a letter or digit (`y`), an F-key (`f5`), or a DOM key name (`space`) |

Defaults: `ctrl+y` on the TUI, `mod+y` on desktop (⌘Y on macOS, Ctrl+Y elsewhere).
A malformed spec falls back to the default rather than erroring, a bare letter with
no modifier is refused (it would fire on every keystroke), while unmodified F-keys
are fine. The composer hints and the mic tooltip pick up the new binding. Config is
read at startup, restart the surface after changing it. Esc always discards a live
recording and is not remappable.

The keybind lives in the same `config.json` as everything else, global
(`~/.empryo/config.json`) or per project (`.empryo/config.json`).

### Self-hosted transcription

Point `voice.baseURL` at any OpenAI-compatible `/audio/transcriptions` endpoint, a local
whisper server, a LAN box, and your audio never leaves the network. A custom endpoint
needs no provider key, and `model` may then be a bare name:

```jsonc
{
  "voice": {
    "baseURL": "http://localhost:9000/v1",
    "model": "large-v3"
  }
}
```

### Transcribe with an audio-capable chat model

Set `voice.llm` to send the audio to a chat model that can hear instead of an STT
endpoint. Any registered provider works, and `voice.instruction` shapes the output, 
useful for cleaning up filler words or punctuating dictation:

```jsonc
{
  "voice": {
    "llm": "openai/gpt-4o-audio-preview",
    "instruction": "Transcribe faithfully, but drop filler words and add punctuation."
  }
}
```

### Custom recorder

`voice.command` replaces the recorder command entirely (TUI and headless). Every
`{output}` token is substituted with the capture path. The command must record until
interrupted and write a format your endpoint accepts:

```jsonc
{
  "voice": { "command": ["sox", "-d", "-c", "1", "{output}"] }
}
```

## Requirements and troubleshooting

TUI and headless recording uses the first available recorder for your platform:

- macOS: `rec` from SoX or `ffmpeg`. Run `brew install sox` if neither is installed.
- Linux: `arecord` from `alsa-utils`, `ffmpeg`, or `sox`.
- Windows: `ffmpeg`.

If no recorder binary is found, install one of the listed tools and try again. The error includes the platform-specific install hint. The desktop app records through the system mic with `MediaRecorder`, so it needs microphone permission instead of a recorder binary.

Cloud transcription requires an OpenAI or Groq key (Groq has a free tier). Without one,
Empryo falls back to a local whisper.cpp install automatically, see the section above.
To use cloud STT:

```bash
empryo --set-key groq <key>
# or
empryo --set-key openai <key>
```

The TUI, desktop app, and headless CLI cap each recording at 5 minutes or 10 MB. Stop earlier if you do not need the full limit.

## Privacy

Empryo sends audio only to the configured speech-to-text provider. TUI and headless recording uses a private temporary file while the mic is active, then deletes it after transcription or cancellation. The desktop app keeps the recording in memory until transcription finishes.
