d28214faad0ecace7d4c0ac1d4d64952dfefc1eb
Hyprvoice
Voice‑powered typing for Wayland/Hyprland — press to toggle, speak, instant paste. Streams audio while you talk and pastes the final text the moment you toggle off → aims to be the fastest feel on Wayland.
Status: Early development (expect rough edges)
TL;DR
- Toggle workflow (Hyprland‑friendly): press to start, press to stop.
- Cloud streaming ASR (MVP) → single final paste into the focused window.
- Daemon with clear states & events; desktop notifications.
- Clipboard‑based injection (save/restore) with
wtype - Unixy pipeline (small pieces, bounded channels).
Requirements
- Go 1.24.5+ (for building from source)
- Wayland + Hyprland
- PipeWire (audio capture)
- systemd --user (service)
- wl-clipboard (clipboard save/restore)
- libnotify/
notify-send(optional notifications) wtypeorydotool(optional text injection fallback)
Other distros may work, but Arch/Hyprland is the primary target for now.
Install (Arch / Hyprland)
# AUR
yay -S hyprvoice # or: yay -S hyprvoice-bin
# Enable user service
systemctl --user enable --now hyprvoice.service
# Hyprland keybind (toggle)
bind = SUPER, R, exec, hyprvoice toggle
Usage
Basic Usage
- Press your toggle key to start; press again to stop.
- Audio streams to the cloud ASR while you speak.
- On stop (or VAD endpoint), Hyprvoice pastes once into the focused window.
- Injection flow: save clipboard → copy final text → send Ctrl+V → restore clipboard.
CLI Commands
# Start the daemon
hyprvoice serve
# Toggle recording on/off
hyprvoice toggle
# Check current status
hyprvoice status
# Get protocol version
hyprvoice version
# Stop the daemon
hyprvoice stop
Status
| Component | State | Notes |
|---|---|---|
| Daemon (control plane) | ✅ | State, IPC, worker orchestration |
| Recording control | ✅ | hyprvoice toggle |
| Desktop notifications | ✅ | notify-send (logs fallback) |
| Audio capture | 🔄 | PipeWire + VAD |
| ASR backends | 🔄 | Cloud streaming now; local Whisper next |
| Text injection | 🔄 | Clipboard paste → wtype → ydotool |
| Service management | 🔄 | systemd --user |
Legend: ✅ done · 🔄 in progress · ⏳ planned
How it works
- Model: pipeline + central state (daemon = control plane).
- State machine:
idle → recording → transcribing → injecting → idle. - Rule: switch to
transcribing** as soon as the first audio frame is sent** to the ASR.
ASCII diagram
+-------------------+ Unix socket IPC +-----------+
CLI cmd → | Control Daemon | <---------------------------- | CLI/Tool |
|-------------------| +-----------+
| State: idle/rec/ |
| transcribing/... | events events
| Event bus (chan) | -----> [Notifications] -----> notify-send/log
| |
| frames finals |
+--+-----------+----+
| |
Audio | | Final Text
Frames v v
+--------+ +--------+ text +-----------+
| Audio |-->| ASR | -------------->| Injection |
| Capture| | Stream | | Worker |
+--------+ +--------+ +-----------+
| ^
+--------------+
backpressure via bounded channels
State (daemon):
idle --toggle--> recording --first frame--> transcribing --final--> injecting --done--> idle
Data flow
toggle→ recording- First frame sent → transcribing
- Cloud ASR returns final → injecting
- Paste once → idle
- Notifications at each transition
Build from source
git clone https://github.com/leonardotrapani/hyprvoice.git
cd hyprvoice
# Build the binary
CGO_ENABLED=1 go build -o hyprvoice ./cmd/hyprvoice
# Run tests (when available)
go test ./...
# Install locally
sudo cp hyprvoice /usr/local/bin/
Dependencies
- Cobra CLI - Command-line interface framework
- Go 1.24.5+ - Programming language runtime
Configuration
File Locations
- Socket:
~/.cache/hyprvoice/control.sock- IPC communication - PID file:
~/.cache/hyprvoice/hyprvoice.pid- Process tracking
Systemd Service
The daemon runs as a user service. To create a systemd service file:
# Create service file at ~/.config/systemd/user/hyprvoice.service
mkdir -p ~/.config/systemd/user
cat > ~/.config/systemd/user/hyprvoice.service << 'EOF'
[Unit]
Description=Hyprvoice 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
Development
Project Structure
hyprvoice/
├── cmd/hyprvoice/ # Main CLI application
├── internal/
│ ├── bus/ # IPC communication (Unix sockets)
│ ├── daemon/ # Main daemon logic and state management
│ ├── notify/ # Desktop notifications
│ └── pipeline/ # Audio processing pipeline
├── go.mod # Go module definition
└── README.md
State Machine
The daemon operates with these states:
- idle → recording → transcribing → injecting → idle
IPC Protocol
Single-character commands over Unix socket:
t- Toggle recordings- Get statusv- Get protocol versionq- Quit daemon
Running in Development
# Terminal 1: Start daemon with logs
go run ./cmd/hyprvoice serve
# Terminal 2: Test commands
go run ./cmd/hyprvoice toggle
go run ./cmd/hyprvoice status
Troubleshooting
Common Issues
Daemon won't start
# Check if already running
hyprvoice status
# Check PID file
ls -la ~/.cache/hyprvoice/
# Remove stale files
rm ~/.cache/hyprvoice/hyprvoice.pid
rm ~/.cache/hyprvoice/control.sock
No notifications
# Test notify-send
notify-send "Test notification"
# Check if libnotify is installed
which notify-send
Permission errors
# Check socket permissions
ls -la ~/.cache/hyprvoice/control.sock
# Recreate cache directory
rm -rf ~/.cache/hyprvoice
mkdir -p ~/.cache/hyprvoice
Debug Mode
# Run with verbose logging
hyprvoice serve 2>&1 | tee hyprvoice.log
Contributing
- All PRs and issues welcome.
- Follow existing code conventions
- Add tests for new functionality
- Update documentation for user-facing changes
License
MIT — see LICENSE.md
Languages
Go
99.3%
Shell
0.7%