# Hyprvoice - Voice-Powered Typing for Hyprland / Wayland Press a toggle key, speak, and get instant text input. Built natively for Wayland/Hyprland - no X11 hacks or workarounds, just clean integration with modern Linux desktops. ## Features - **Toggle workflow**: Press once to start recording, press again to stop and inject text - **Wayland native**: Purpose-built for Wayland compositors - no legacy X11 dependencies or hacky workarounds - **Real-time feedback**: Desktop notifications for recording states and transcription status - **Multiple transcription backends**: OpenAI Whisper (planned: whisper.cpp for local processing, and more) - **Smart text injection**: Clipboard save/restore with direct typing fallback - **Daemon architecture**: Lightweight control plane with efficient pipeline management **Status:** Beta - core functionality complete and tested, ready for early adopters ## Installation ### From AUR (Arch Linux) ```bash # Using your preferred AUR helper yay -S hyprvoice-bin # or paru -S hyprvoice-bin # Enable user service systemctl --user enable --now hyprvoice.service ``` ### Download Binary 1. Download from [GitHub Releases](https://github.com/leonardotrapani/hyprvoice/releases) 2. Install: ```bash wget https://github.com/leonardotrapani/hyprvoice/releases/latest/download/hyprvoice-linux-x86_64 mkdir -p ~/.local/bin mv hyprvoice-linux-x86_64 ~/.local/bin/hyprvoice chmod +x ~/.local/bin/hyprvoice # Add to PATH (add to ~/.bashrc or ~/.zshrc) export PATH="$HOME/.local/bin:$PATH" ``` ### Build from Source ```bash git clone https://github.com/leonardotrapani/hyprvoice.git cd hyprvoice # Install Go dependencies go mod download # Build the binary go build -o hyprvoice ./cmd/hyprvoice # Install locally (optional) sudo cp hyprvoice /usr/local/bin/ # Or install to user directory mkdir -p ~/.local/bin cp hyprvoice ~/.local/bin/ export PATH="$HOME/.local/bin:$PATH" # Add to ~/.bashrc or ~/.zshrc ``` ## Requirements - **Wayland desktop** (Hyprland, Niri, GNOME, KDE, etc.) - **PipeWire audio system** with tools - **System packages**: ```bash # Arch Linux sudo pacman -S pipewire pipewire-pulse pw-record wl-clipboard # Ubuntu/Debian sudo apt install pipewire-pulse pipewire-bin wl-clipboard # Fedora sudo dnf install pipewire-utils wl-clipboard ``` **For text injection:** ```bash # Arch Linux sudo pacman -S wtype # Ubuntu/Debian sudo apt install wtype # Alternative: ydotool (if wtype unavailable) # Follow ydotool setup for user permissions ``` **Optional:** - `notify-send` (desktop notifications) - `systemd --user` (daemon service) ## Quick Start 1. **Setup daemon service:** ```bash # Enable and start the user service (reccomended) systemctl --user enable --now hyprvoice.service # Or run manually in background hyprvoice serve & ``` 2. **Configure Hyprland keybind:** ```bash # Add to ~/.config/hypr/hyprland.conf bind = SUPER, R, exec, hyprvoice toggle ``` 3. **Test voice input:** ```bash # Check daemon status hyprvoice status # Toggle recording (or use Super+R) hyprvoice toggle # Speak something... hyprvoice toggle # Stop and transcribe ``` ## Quick Reference ### Common Commands ```bash # Start the daemon hyprvoice serve # Toggle recording on/off hyprvoice toggle # Check current status hyprvoice status # Get protocol version hyprvoice version # Stop the daemon (if not using systemd service) hyprvoice stop ``` ### Keybinding Pattern Most setups use this toggle pattern in window manager config: ```bash bind = SUPER, R, exec, hyprvoice toggle ``` ## Keyboard Shortcuts Setup ### Hyprland Add to your `~/.config/hypr/hyprland.conf`: ```bash # Hyprvoice - Voice to Text (toggle recording) bind = SUPER, R, exec, hyprvoice toggle # Optional: Status check bind = SUPER SHIFT, R, exec, hyprvoice status && notify-send "Hyprvoice" "$(hyprvoice status)" ``` ## Usage Examples ### Basic Toggle Workflow 1. **Press keybind** → Recording starts (notification appears) 2. **Speak your text** → Audio captured in real-time 3. **Press keybind again** → Recording stops, transcription begins 4. **Text appears** → Injected at cursor position or clipboard ### CLI Usage ```bash # Start daemon manually (if not using systemd service) hyprvoice serve # In another terminal: toggle recording hyprvoice toggle # ... speak ... hyprvoice toggle # Check what's happening hyprvoice status ``` ## Configuration Configuration is automatically loaded from `~/.config/hyprvoice/config.toml`. The daemon creates this file with sensible defaults and helpful comments on first run. Changes to the config file are applied immediately without restarting the daemon. ### Transcription Providers Hyprvoice supports multiple transcription backends: #### Generated Configuration Example The daemon automatically creates `~/.config/hyprvoice/config.toml` with helpful comments: ```toml # Hyprvoice Configuration # This file is automatically generated with defaults. # Edit values as needed - changes are applied immediately without daemon restart. # Audio Recording Configuration [recording] sample_rate = 16000 # Audio sample rate in Hz (16000 recommended for speech) channels = 1 # Number of audio channels (1 = mono, 2 = stereo) format = "s16" # Audio format (s16 = 16-bit signed integers) buffer_size = 8192 # Internal buffer size in bytes (larger = less CPU, more latency) device = "" # PipeWire audio device (empty = use default microphone) channel_buffer_size = 30 # Audio frame buffer size (frames to buffer) timeout = "5m" # Maximum recording duration (e.g., "30s", "2m", "5m") # Speech Transcription Configuration [transcription] provider = "openai" # Transcription service ("openai" only currently supported) api_key = "" # OpenAI API key (or set OPENAI_API_KEY environment variable) language = "" # Language code (empty for auto-detect, "en", "it", "es", "fr", etc.) model = "whisper-1" # OpenAI model name ("whisper-1" recommended) # Text Injection Configuration [injection] mode = "fallback" # Injection method ("clipboard", "type", "fallback") restore_clipboard = true # Restore original clipboard after injection wtype_timeout = "5s" # Timeout for direct typing via wtype clipboard_timeout = "3s" # Timeout for clipboard operations # Desktop Notification Configuration [notifications] enabled = true # Enable desktop notifications type = "desktop" # Notification type ("desktop", "log", "none") -- always keep "desktop" unless debugging ``` #### whisper.cpp Local (Planned) -> Not yet implemented Private, offline transcription using local models: ```toml [transcription] provider = "whisper_cpp" model_path = "~/models/ggml-base.en.bin" threads = 4 ``` #### Recording Configuration Audio capture settings: ```toml [recording] sample_rate = 16000 # Audio sample rate in Hz channels = 1 # Number of audio channels (1 for mono) format = "s16" # Audio format (s16 recommended) buffer_size = 8192 # Internal buffer size in bytes device = "" # PipeWire device (empty for default) channel_buffer_size = 30 # Audio frame buffer size timeout = "5m" # Maximum recording duration (prevents runaway recordings) ``` **Recording Timeout:** - Prevents accidental long recordings that could consume resources - Default: 5 minutes (`"5m"`) - Format: Go duration strings like `"30s"`, `"2m"`, `"10m"` - Recording automatically stops when timeout is reached #### Text Injection Configurable text injection with multiple modes: ```toml [injection] mode = "fallback" # "clipboard", "type", or "fallback" restore_clipboard = true wtype_timeout = "5s" clipboard_timeout = "3s" ``` **Injection Modes:** - **`fallback`** (default): Try direct typing first, fallback to clipboard - **`type`**: Direct typing using wtype only - **`clipboard`**: Copy to clipboard only **Behavior:** - `restore_clipboard = true`: Save and restore original clipboard content - Smart fallback ensures text injection always succeeds when possible #### Notifications Desktop notification settings: ```toml [notifications] enabled = true # Enable/disable notifications type = "desktop" # "desktop", "log", or "none" ``` **Notification Types:** - **`desktop`**: Use notify-send for desktop notifications - **`log`**: Log messages to console only - **`none`**: Disable all notifications Always keep `type = "desktop"` unless debugging. ### Configuration Hot-Reloading The daemon automatically watches the config file for changes and applies them immediately: - **Notification settings**: Applied instantly - **Injection settings**: Applied to current and future operations - **Recording/Transcription settings**: Applied to new recording sessions - **Invalid configs**: Rejected with error notification, daemon continues with previous config ### Service Configuration #### Systemd Service The daemon runs as a user service: ```bash # Create service file mkdir -p ~/.config/systemd/user cat > ~/.config/systemd/user/hyprvoice.service << 'EOF' [Unit] Description=Hyprvoice voice-to-text daemon After=pipewire.service [Service] Type=simple ExecStart=/usr/local/bin/hyprvoice serve Restart=on-failure RestartSec=5 [Install] WantedBy=default.target EOF # Enable and start systemctl --user daemon-reload systemctl --user enable --now hyprvoice.service ``` ### File Locations - **Socket**: `~/.cache/hyprvoice/control.sock` - IPC communication - **PID file**: `~/.cache/hyprvoice/hyprvoice.pid` - Process tracking - **Config**: `~/.config/hyprvoice/config.toml` - User settings (planned) ## Development Status | Component | Status | Notes | | ---------------------- | ------ | ----------------------------------------------------- | | Core daemon & IPC | ✅ | Unix socket control plane | | Recording workflow | ✅ | Toggle recording via PipeWire | | Audio capture | ✅ | Efficient PipeWire integration | | Desktop notifications | ✅ | Status feedback via notify-send | | OpenAI transcription | ✅ | HTTP API integration | | Text injection | ✅ | Clipboard + wtype with fallback | | Configuration system | ✅ | TOML-based user settings with hot-reload | | Unit test coverage | ✅ | Comprehensive test suite (100% pass) | | Installation (AUR etc) | ⏳ | Installation via AUR and easy setup | | Light dictation models | ⏳ | Alternatives to whispers for light and fast dictation | | whisper.cpp support | ⏳ | Local model inference | **Legend**: ✅ Complete · ⏳ Planned ## Architecture Overview Hyprvoice uses a **daemon + pipeline** architecture for efficient resource management: - **Control Daemon**: Lightweight IPC server managing lifecycle - **Pipeline**: Stateful audio processing (recording → transcribing → injecting) - **State Machine**: `idle → recording → transcribing → injecting → idle` ### System Architecture ```mermaid flowchart LR subgraph Client CLI["CLI/Tool"] end subgraph Daemon D["Control Daemon (lifecycle + IPC)"] end subgraph Pipeline A["Audio Capture"] T["Transcribing"] I["Injecting (wtype + clipboard)"] end N["notify-send/log"] CLI -- unix socket --> D D -- start/stop --> A A -- frames --> T T -- status --> D D -- events --> N D -- inject action --> T T --> I I -->|done| D ``` ```mermaid stateDiagram-v2 [*] --> idle idle --> recording: toggle recording --> transcribing: first_frame transcribing --> injecting: inject_action injecting --> idle: done recording --> idle: abort injecting --> idle: abort ``` ### How It Works 1. **Toggle recording** → Pipeline starts, audio capture begins 2. **Audio streaming** → PipeWire frames buffered for transcription 3. **Toggle stop** → Recording ends, transcription starts 4. **Text injection** → Result typed or copied to clipboard 5. **Return to idle** → Pipeline cleaned up, ready for next session ### Data Flow 1. `toggle` (daemon) → create pipeline → recording 2. First frame arrives → transcribing (daemon may notify `Transcribing` later) 3. Audio frames → audio buffer (collect all audio during session) 4. Second `toggle` during transcribing → send `inject` action → transcribe collected audio → injecting (simulated) 5. Complete → idle; pipeline stops; daemon clears reference 6. Notifications at key transitions ## Troubleshooting ### Common Issues #### Daemon Issues **Daemon won't start:** ```bash # Check if already running hyprvoice status # Check for stale files ls -la ~/.cache/hyprvoice/ # Clean up and restart rm -f ~/.cache/hyprvoice/hyprvoice.pid rm -f ~/.cache/hyprvoice/control.sock hyprvoice serve ``` **Command not found:** ```bash # Check installation which hyprvoice # Add to PATH if using ~/.local/bin echo 'export PATH="$HOME/.local/bin:$PATH"' >> ~/.bashrc source ~/.bashrc ``` #### Audio Issues **No audio recording:** ```bash # Check PipeWire is running systemctl --user status pipewire # Test microphone pw-record --help pw-record test.wav # Check microphone permissions and levels ``` **Audio device issues:** ```bash # List available audio devices pw-cli list-objects | grep -A5 -B5 Audio # Check microphone is not muted in system settings ``` #### Notification Issues **No desktop notifications:** ```bash # Test notify-send directly notify-send "Test" "This is a test notification" # Install if missing sudo pacman -S libnotify # Arch sudo apt install libnotify-bin # Ubuntu/Debian ``` #### Text Injection Issues **Text not appearing:** - Ensure cursor is in a text field when toggling off recording - Check that `wtype` and `wl-clipboard` tools are installed: ```bash # Test wtype directly wtype "test text" # Test clipboard tools echo "test" | wl-copy wl-paste ``` - Verify Wayland compositor supports text input protocols - Check injection mode in configuration (fallback mode is most robust) **Clipboard issues:** ```bash # Install wl-clipboard if missing sudo pacman -S wl-clipboard # Arch sudo apt install wl-clipboard # Ubuntu/Debian # Test clipboard functionality wl-copy "test text" wl-paste ``` ### Debug Mode ```bash # Run daemon with verbose output hyprvoice serve # Check logs from systemd service (or just see results from hyprvoice serve) journalctl --user -u hyprvoice.service -f # Test individual commands hyprvoice toggle hyprvoice status ``` ## Development ### Building from Source ```bash git clone https://github.com/leonardotrapani/hyprvoice.git cd hyprvoice # Install Go dependencies go mod download # Build CGO_ENABLED=1 go build -o hyprvoice ./cmd/hyprvoice # Run tests go test ./... # Install locally sudo cp hyprvoice /usr/local/bin/ ``` ### Project Structure ``` hyprvoice/ ├── cmd/hyprvoice/ # CLI application entry point ├── internal/ │ ├── bus/ # IPC (Unix socket) + PID management │ ├── daemon/ # Control daemon (lifecycle management) │ ├── injection/ # Text injection (clipboard + wtype) │ ├── notify/ # Desktop notification integration │ ├── pipeline/ # Audio processing pipeline + state machine │ ├── recording/ # PipeWire audio capture │ └── transcriber/ # Transcription adapters (OpenAI, whisper.cpp) ├── go.mod # Go module definition └── README.md ``` ### Development Workflow ```bash # Terminal 1: Run daemon with logs go run ./cmd/hyprvoice serve # Terminal 2: Test commands go run ./cmd/hyprvoice toggle go run ./cmd/hyprvoice status go run ./cmd/hyprvoice stop ``` ### IPC Protocol Simple single-character commands over Unix socket: - `t` - Toggle recording on/off - `s` - Get current status - `v` - Get protocol version - `q` - Quit daemon gracefully ## Contributing Contributions welcome! Please: - Follow existing code conventions and patterns - Add tests for new functionality when available - Update documentation for user-facing changes - Test on Hyprland/Wayland before submitting PRs ## License MIT License - see [LICENSE.md](LICENSE.md) for details.