Skip to content
thomaspeklakPublic

About

No description, website, or topics provided.

Resources

Stars

4 stars

Watchers

0 watching

Forks

Latest commit

 

History

12 Commits

Folders and files

Repository files navigation

Dictate

Speech-to-text dictation tool for Linux/Wayland, designed to run via keyboard shortcut. Press your hotkey to start recording, press again to stop. Your speech is transcribed locally with faster-whisper, optionally cleaned up by Claude, and copied to your clipboard.

Features

  • Toggle-based recording: First invocation starts recording, second stops and processes
  • Model selection: Switch Whisper models with --set-model (e.g., whisper/small)
  • Local transcription: Uses faster-whisper (Whisper implementation in CTranslate2)
  • AI text cleanup: Optional grammar and punctuation cleanup via Claude CLI
  • Multi-language support: Use --language (e.g., en, de)
  • Direct typing: Optionally type text directly at cursor position (in addition to clipboard)
  • Desktop notifications: Progress updates via system notifications
  • Configurable: Model, language, audio device, and prompt template

Requirements

System Dependencies

Install these via your package manager:

# Fedora
sudo dnf install portaudio-devel wl-clipboard libnotify

# For --type flag (optional)
sudo dnf install wtype

# Ubuntu/Debian
sudo apt install portaudio19-dev wl-clipboard libnotify-bin

# For --type flag (optional)
sudo apt install wtype

Claude CLI

The Claude CLI is required for text cleanup (unless using --raw mode):

# Install via npm
npm install -g @anthropic-ai/claude-code

# Authenticate
claude login

Python

Python 3.11+ is required. Using uv is recommended.

Installation

Clone the repository:

git clone https://github.com/yourusername/dictate.git
cd dictate

No additional installation steps needed - uv handles Python dependencies automatically.

Usage

This tool is designed to be triggered via a keyboard shortcut (see Setting Up a Keyboard Shortcut below). The same shortcut both starts and stops recording.

How the Toggle Works

  1. First press: Starts recording (shows notification)
  2. Second press: Stops recording → transcribes → processes → copies to clipboard

Testing from the Shell

When testing from a terminal, run in the background with & (otherwise Ctrl+C would kill the recording process):

# Start recording
uv run ./dictate.py &

# Stop and process (run again in any terminal)
uv run ./dictate.py

Command-Line Arguments

Argument Short Description
--language -l Language code for transcription (e.g., de, fr, es). Default: en
--raw -r Skip Claude processing, output raw transcription only
--type -t Type text at cursor position via wtype (in addition to clipboard)
--set-model Persist model id (whisper/*) and pre-download required en/de checkpoints
--list-models Show supported model ids (currently Whisper only)

Examples

# Transcribe in German
uv run ./dictate.py -l de

# Raw transcription without AI cleanup
uv run ./dictate.py --raw

# Type directly at cursor AND copy to clipboard
uv run ./dictate.py --type

# Combine flags
uv run ./dictate.py -l de -r -t

# Show model ids
uv run ./dictate.py --list-models

# Switch model (saved to ~/.config/dictate/config.toml)
uv run ./dictate.py --set-model whisper/small

Setting Up a Keyboard Shortcut

Hyprland

Add to your hyprland.conf or keybinds config:

# Dictate - English (Super+Ctrl+Alt+E to toggle)
bind = $mainMod CTRL ALT, e, exec, uv run ~/code/dictate/dictate.py --type

# Dictate - German
bind = $mainMod CTRL ALT, d, exec, uv run ~/code/dictate/dictate.py --type -l de

# Raw mode (no Claude processing)
bind = $mainMod SHIFT CTRL ALT, e, exec, uv run ~/code/dictate/dictate.py --type --raw

Sway

Add to your ~/.config/sway/config:

# Dictate - English (Super+Ctrl+Alt+E to toggle)
bindsym $mod+Ctrl+Alt+e exec uv run ~/code/dictate/dictate.py --type

# Dictate - German
bindsym $mod+Ctrl+Alt+d exec uv run ~/code/dictate/dictate.py --type -l de

GNOME

  1. Open Settings → Keyboard → Keyboard Shortcuts → View and Customize Shortcuts
  2. Scroll to Custom Shortcuts and click +
  3. Set:
    • Name: Dictate
    • Command: uv run /full/path/to/dictate.py --type
    • Shortcut: Your preferred key (e.g., Super+Ctrl+Alt+E)

Other Desktop Environments

Configure your DE's keybinding system to run the script. The same shortcut toggles recording on/off.

Configuration

Create or edit ~/.config/dictate/config.toml.

dictate still falls back to the legacy project-local ./config.toml if no user config exists.

[stt]
model = "whisper/small"   # provider/model

[whisper]
device = "cpu"         # cpu or cuda
compute_type = "int8"  # int8, int16, float16, float32
language = "en"        # Default language

[audio]
# Uncomment to specify a specific audio input device index
# device = 0

[agent]
# Custom prompt template (uses {text} placeholder)
# prompt_template = """Your custom prompt here: {text}"""

Whisper Model IDs

Use these with --set-model:

Model id Size Speed Accuracy
whisper/tiny 75 MB Fastest Lower
whisper/base 142 MB Fast Good
whisper/small 466 MB Medium Better
whisper/medium 1.5 GB Slower High
whisper/large-v3 3 GB Slowest Highest

Language-aware selection:

  • For English (en*), tiny/base/small/medium automatically use .en checkpoints.
  • For German and other languages, multilingual checkpoints are used.

For CUDA acceleration, set device = "cuda" and use compute_type = "float16".

How It Works

  1. First run: Creates a PID file (/tmp/dictate.pid) and starts recording audio
  2. Second run: Detects running instance via PID file, sends SIGUSR1 signal to stop
  3. Processing pipeline:
    • Audio is resampled to 16kHz
    • Transcription runs with configured stt.model (Whisper via faster-whisper)
    • Claude CLI cleans up grammar/punctuation (unless --raw)
    • Result is copied to clipboard (and typed if --type)
  4. Notifications: Desktop notifications show progress at each stage

Troubleshooting

No audio input detected

List available audio devices and set the correct index in config.toml:

import pyaudio
p = pyaudio.PyAudio()
for i in range(p.get_device_count()):
    info = p.get_device_info_by_index(i)
    if info["maxInputChannels"] > 0:
        print(f"{i}: {info['name']}")

Claude CLI errors

Ensure you're authenticated: claude login

Use --raw mode to bypass Claude processing entirely.

CUDA not detected

Install CUDA-compatible versions:

uv pip install nvidia-cublas-cu12 nvidia-cudnn-cu12

License

MIT

About

No description, website, or topics provided.

Resources

Stars

4 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages