From 51cd52d13d9c5b62e1ef8b60118198e2e007bfb5 Mon Sep 17 00:00:00 2001
From: m3tm3re
Date: Fri, 2 Jan 2026 12:21:32 +0100
Subject: [PATCH] feat: add stt-ptt package for voice-to-text and
text-to-speech
Implements speech-to-text (via whisper-cpp) and text-to-speech (via espeak)
functionality with key bindings. Replaces coreutils with busybox for lighter
dependencies and removes explicit buildInputs since all paths are hardcoded.
---
README.md | 1 +
docs/README.md | 2 +
docs/modules/home-manager/cli/stt-ptt.md | 265 +++++++++++++++++++++++
docs/packages/stt-ptt.md | 202 +++++++++++++++++
modules/home-manager/cli/default.nix | 1 +
modules/home-manager/cli/stt-ptt.nix | 107 +++++++++
pkgs/default.nix | 1 +
pkgs/stt-ptt/default.nix | 91 ++++++++
8 files changed, 670 insertions(+)
create mode 100644 docs/modules/home-manager/cli/stt-ptt.md
create mode 100644 docs/packages/stt-ptt.md
create mode 100644 modules/home-manager/cli/stt-ptt.nix
create mode 100644 pkgs/stt-ptt/default.nix
diff --git a/README.md b/README.md
index 78d1022..31aaf19 100644
--- a/README.md
+++ b/README.md
@@ -46,6 +46,7 @@ nix run git+https://code.m3ta.dev/m3tam3re/nixpkgs#zellij-ps
| `mem0` | AI memory assistant with vector storage |
| `msty-studio` | Msty Studio application |
| `pomodoro-timer` | Pomodoro timer utility |
+| `stt-ptt` | Push to Talk Speech to Text |
| `tuxedo-backlight` | Backlight control for Tuxedo laptops |
| `zellij-ps` | Project switcher for Zellij |
diff --git a/docs/README.md b/docs/README.md
index 9cdecb8..d420855 100644
--- a/docs/README.md
+++ b/docs/README.md
@@ -34,6 +34,7 @@ Documentation for all custom packages:
- [mem0](./packages/mem0.md) - AI memory assistant with vector storage
- [msty-studio](./packages/msty-studio.md) - Msty Studio application
- [pomodoro-timer](./packages/pomodoro-timer.md) - Pomodoro timer utility
+- [stt-ptt](./packages/stt-ptt.md) - Push to Talk Speech to Text using Whisper
- [tuxedo-backlight](./packages/tuxedo-backlight.md) - Backlight control for Tuxedo laptops
- [zellij-ps](./packages/zellij-ps.md) - Project switcher for Zellij
@@ -49,6 +50,7 @@ Configuration modules for NixOS and Home Manager:
#### Home Manager Modules
- [Overview](./modules/home-manager/overview.md) - Home Manager modules overview
- [CLI Tools](./modules/home-manager/cli/) - CLI-related modules
+ - [stt-ptt](./modules/home-manager/cli/stt-ptt.md) - Push to Talk Speech to Text
- [zellij-ps](./modules/home-manager/cli/zellij-ps.md) - Zellij project switcher
- [Coding](./modules/home-manager/coding/) - Development-related modules
- [editors](./modules/home-manager/coding/editors.md) - Editor configurations
diff --git a/docs/modules/home-manager/cli/stt-ptt.md b/docs/modules/home-manager/cli/stt-ptt.md
new file mode 100644
index 0000000..6237d32
--- /dev/null
+++ b/docs/modules/home-manager/cli/stt-ptt.md
@@ -0,0 +1,265 @@
+# stt-ptt Home Manager Module
+
+Push to Talk Speech to Text for Home Manager.
+
+## Overview
+
+This module configures stt-ptt, a push-to-talk speech-to-text tool using whisper.cpp. It handles model downloads, environment configuration, and package installation.
+
+## Quick Start
+
+```nix
+{config, ...}: {
+ imports = [m3ta-nixpkgs.homeManagerModules.default];
+
+ cli.stt-ptt = {
+ enable = true;
+ };
+}
+```
+
+This will:
+- Install stt-ptt with default whisper-cpp
+- Download the `ggml-large-v3-turbo` model on first activation
+- Set environment variables for model path and notification timeout
+
+## Module Options
+
+### `cli.stt-ptt.enable`
+
+Enable the stt-ptt module.
+
+- Type: `boolean`
+- Default: `false`
+
+### `cli.stt-ptt.whisperPackage`
+
+The whisper-cpp package to use for transcription.
+
+- Type: `package`
+- Default: `pkgs.whisper-cpp`
+
+**Pre-built variants:**
+
+```nix
+# CPU (default)
+whisperPackage = pkgs.whisper-cpp;
+
+# Vulkan GPU acceleration (pre-built)
+whisperPackage = pkgs.whisper-cpp-vulkan;
+```
+
+**Override options** (can be combined):
+
+| Option | Description |
+|--------|-------------|
+| `cudaSupport` | NVIDIA CUDA acceleration |
+| `rocmSupport` | AMD ROCm acceleration |
+| `vulkanSupport` | Vulkan GPU acceleration |
+| `coreMLSupport` | Apple CoreML (macOS only) |
+| `metalSupport` | Apple Metal (macOS ARM only) |
+
+```nix
+# NVIDIA CUDA support
+whisperPackage = pkgs.whisper-cpp.override { cudaSupport = true; };
+
+# AMD ROCm support
+whisperPackage = pkgs.whisper-cpp.override { rocmSupport = true; };
+
+# Vulkan support (manual override)
+whisperPackage = pkgs.whisper-cpp.override { vulkanSupport = true; };
+```
+
+### `cli.stt-ptt.model`
+
+The Whisper model to use. Models are automatically downloaded from HuggingFace on first activation.
+
+- Type: `string`
+- Default: `"ggml-large-v3-turbo"`
+
+Available models (sorted by size):
+
+| Model | Size | Notes |
+|-------|------|-------|
+| `ggml-tiny` | 75MB | Fastest, lowest quality |
+| `ggml-tiny.en` | 75MB | English-only, slightly faster |
+| `ggml-base` | 142MB | Fast, basic quality |
+| `ggml-base.en` | 142MB | English-only |
+| `ggml-small` | 466MB | Balanced speed/quality |
+| `ggml-small.en` | 466MB | English-only |
+| `ggml-medium` | 1.5GB | Good quality |
+| `ggml-medium.en` | 1.5GB | English-only |
+| `ggml-large-v1` | 2.9GB | High quality (original) |
+| `ggml-large-v2` | 2.9GB | High quality (improved) |
+| `ggml-large-v3` | 2.9GB | Highest quality |
+| `ggml-large-v3-turbo` | 1.6GB | High quality, optimized speed (recommended) |
+
+Quantized versions (`q5_0`, `q5_1`, `q8_0`) are also available for reduced size.
+
+### `cli.stt-ptt.notifyTimeout`
+
+Notification timeout in milliseconds for the recording indicator.
+
+- Type: `integer`
+- Default: `3000`
+- Example: `5000` (5 seconds), `0` (persistent)
+
+## Usage
+
+After enabling, bind `stt-ptt start` and `stt-ptt stop` to a key:
+
+```bash
+# Start recording
+stt-ptt start
+
+# Stop recording and transcribe (types result)
+stt-ptt stop
+```
+
+### Keybinding Examples
+
+#### Hyprland
+
+```nix
+wayland.windowManager.hyprland.settings = {
+ bind = [
+ "SUPER, V, exec, stt-ptt start"
+ ];
+ bindr = [
+ "SUPER, V, exec, stt-ptt stop"
+ ];
+};
+```
+
+Or in `hyprland.conf`:
+
+```conf
+# Press to start recording, release to transcribe
+bind = SUPER, V, exec, stt-ptt start
+bindr = SUPER, V, exec, stt-ptt stop
+```
+
+#### Sway
+
+```conf
+bindsym --no-repeat $mod+v exec stt-ptt start
+bindsym --release $mod+v exec stt-ptt stop
+```
+
+## Configuration Examples
+
+### Basic Setup
+
+```nix
+cli.stt-ptt = {
+ enable = true;
+};
+```
+
+### Fast English Transcription
+
+```nix
+cli.stt-ptt = {
+ enable = true;
+ model = "ggml-base.en";
+ notifyTimeout = 2000;
+};
+```
+
+### High Quality with NVIDIA GPU
+
+```nix
+cli.stt-ptt = {
+ enable = true;
+ model = "ggml-large-v3";
+ whisperPackage = pkgs.whisper-cpp.override { cudaSupport = true; };
+};
+```
+
+### Vulkan GPU Acceleration
+
+```nix
+cli.stt-ptt = {
+ enable = true;
+ model = "ggml-large-v3-turbo";
+ whisperPackage = pkgs.whisper-cpp-vulkan;
+};
+```
+
+### AMD GPU with ROCm
+
+```nix
+cli.stt-ptt = {
+ enable = true;
+ model = "ggml-large-v3-turbo";
+ whisperPackage = pkgs.whisper-cpp.override { rocmSupport = true; };
+};
+```
+
+### Balanced Setup
+
+```nix
+cli.stt-ptt = {
+ enable = true;
+ model = "ggml-small";
+ notifyTimeout = 3000;
+};
+```
+
+## File Locations
+
+| Path | Description |
+|------|-------------|
+| `~/.local/share/stt-ptt/models/` | Downloaded Whisper models |
+| `~/.cache/stt-ptt/stt.wav` | Temporary audio recording |
+| `~/.cache/stt-ptt/stt.pid` | PID file for recording process |
+
+## Environment Variables
+
+The module sets these automatically:
+
+| Variable | Value |
+|----------|-------|
+| `STT_MODEL` | `~/.local/share/stt-ptt/models/.bin` |
+| `STT_NOTIFY_TIMEOUT` | Configured timeout in ms |
+
+## Requirements
+
+- Wayland compositor (wtype is Wayland-only)
+- PipeWire for audio recording
+- Desktop notification daemon
+
+## Troubleshooting
+
+### Model Download Failed
+
+The model downloads on first `home-manager switch`. If it fails:
+
+```bash
+# Manual download
+mkdir -p ~/.local/share/stt-ptt/models
+curl -L -o ~/.local/share/stt-ptt/models/ggml-large-v3-turbo.bin \
+ https://huggingface.co/ggerganov/whisper.cpp/resolve/main/ggml-large-v3-turbo.bin
+```
+
+### Transcription Too Slow
+
+Use a smaller model or enable GPU acceleration:
+
+```nix
+cli.stt-ptt = {
+ enable = true;
+ model = "ggml-tiny.en"; # Much faster
+};
+```
+
+### Text Not Appearing
+
+1. Ensure you're on Wayland: `echo $XDG_SESSION_TYPE`
+2. Check if wtype works: `wtype "test"`
+3. Some apps may need focus; try clicking the text field first
+
+## Related
+
+- [stt-ptt Package](../../../packages/stt-ptt.md) - Package documentation
+- [Using Modules Guide](../../../guides/using-modules.md) - Module usage patterns
diff --git a/docs/packages/stt-ptt.md b/docs/packages/stt-ptt.md
new file mode 100644
index 0000000..43d8dca
--- /dev/null
+++ b/docs/packages/stt-ptt.md
@@ -0,0 +1,202 @@
+# stt-ptt
+
+Push to Talk Speech to Text using Whisper.
+
+## Description
+
+stt-ptt is a simple push-to-talk speech-to-text tool that uses whisper.cpp for transcription. It records audio via PipeWire, transcribes it using a local Whisper model, and types the result using wtype (Wayland).
+
+## Features
+
+- **Push to Talk**: Start/stop recording with simple commands
+- **Local Processing**: Uses whisper.cpp for fast, offline transcription
+- **Wayland Native**: Types transcribed text using wtype
+- **Configurable**: Model path and notification timeout via environment variables
+- **Lightweight**: Minimal dependencies, no cloud services
+
+## Installation
+
+### Via Home Manager Module (Recommended)
+
+See [stt-ptt Home Manager Module](../modules/home-manager/cli/stt-ptt.md) for the recommended setup with automatic model download.
+
+### Via Overlay
+
+```nix
+{pkgs, ...}: {
+ home.packages = [pkgs.stt-ptt];
+}
+```
+
+### Direct Reference
+
+```nix
+{pkgs, ...}: {
+ home.packages = [
+ inputs.m3ta-nixpkgs.packages.${pkgs.system}.stt-ptt
+ ];
+}
+```
+
+## Usage
+
+### Basic Commands
+
+```bash
+# Start recording
+stt-ptt start
+
+# Stop recording and transcribe
+stt-ptt stop
+```
+
+### Keybinding Setup
+
+The tool is designed to be bound to a key (e.g., hold to record, release to transcribe).
+
+#### Hyprland
+
+```nix
+# In your Hyprland config
+wayland.windowManager.hyprland.settings = {
+ bind = [
+ # Press Super+V to start, release to stop and transcribe
+ "SUPER, V, exec, stt-ptt start"
+ ];
+ bindr = [
+ # Release trigger
+ "SUPER, V, exec, stt-ptt stop"
+ ];
+};
+```
+
+Or in `hyprland.conf`:
+
+```conf
+bind = SUPER, V, exec, stt-ptt start
+bindr = SUPER, V, exec, stt-ptt stop
+```
+
+#### Sway
+
+```conf
+# Hold to record, release to transcribe
+bindsym --no-repeat $mod+v exec stt-ptt start
+bindsym --release $mod+v exec stt-ptt stop
+```
+
+#### i3 (X11 - requires xdotool instead of wtype)
+
+Note: stt-ptt uses wtype which is Wayland-only. For X11, you would need to modify the script to use xdotool.
+
+### Environment Variables
+
+| Variable | Description | Default |
+|----------|-------------|---------|
+| `STT_MODEL` | Path to Whisper model file | `~/.local/share/stt-ptt/models/ggml-large-v3-turbo.bin` |
+| `STT_NOTIFY_TIMEOUT` | Notification timeout in ms | `3000` |
+
+## Requirements
+
+- **whisper-cpp**: Speech recognition engine
+- **wtype**: Wayland text input (Wayland compositor required)
+- **libnotify**: Desktop notifications
+- **pipewire**: Audio recording
+
+## Model Setup
+
+Download a Whisper model from [HuggingFace](https://huggingface.co/ggerganov/whisper.cpp/tree/main):
+
+```bash
+# Create model directory
+mkdir -p ~/.local/share/stt-ptt/models
+
+# Download model (example: large-v3-turbo)
+curl -L -o ~/.local/share/stt-ptt/models/ggml-large-v3-turbo.bin \
+ https://huggingface.co/ggerganov/whisper.cpp/resolve/main/ggml-large-v3-turbo.bin
+```
+
+Or use the Home Manager module which handles this automatically.
+
+## Available Models
+
+| Model | Size | Quality | Speed |
+|-------|------|---------|-------|
+| `ggml-tiny` / `ggml-tiny.en` | 75MB | Basic | Fastest |
+| `ggml-base` / `ggml-base.en` | 142MB | Good | Fast |
+| `ggml-small` / `ggml-small.en` | 466MB | Better | Medium |
+| `ggml-medium` / `ggml-medium.en` | 1.5GB | High | Slower |
+| `ggml-large-v3-turbo` | 1.6GB | High | Fast |
+| `ggml-large-v3` | 2.9GB | Highest | Slowest |
+
+Models ending in `.en` are English-only and slightly faster for English text.
+
+## Platform Support
+
+- Linux with Wayland (primary)
+- Requires PipeWire for audio
+- X11 not supported (wtype is Wayland-only)
+
+## Build Information
+
+- **Version**: 0.1.0
+- **Type**: Shell script wrapper
+- **License**: MIT
+
+## Troubleshooting
+
+### Model Not Found
+
+Error: `Error: Model not found at /path/to/model`
+
+**Solution**: Download a model or use the Home Manager module:
+
+```bash
+curl -L -o ~/.local/share/stt-ptt/models/ggml-large-v3-turbo.bin \
+ https://huggingface.co/ggerganov/whisper.cpp/resolve/main/ggml-large-v3-turbo.bin
+```
+
+### No Audio Recorded
+
+**Solution**: Ensure PipeWire is running:
+
+```bash
+systemctl --user status pipewire
+```
+
+### Text Not Typed
+
+**Solution**: Ensure you're on Wayland and wtype has access:
+
+```bash
+# Check if running on Wayland
+echo $XDG_SESSION_TYPE # Should print "wayland"
+```
+
+### Slow Transcription
+
+**Solution**: Use a smaller model or enable GPU acceleration:
+
+```nix
+cli.stt-ptt = {
+ enable = true;
+ model = "ggml-base.en"; # Smaller, faster model
+};
+```
+
+Or with GPU acceleration:
+
+```nix
+cli.stt-ptt = {
+ enable = true;
+ # Choose one:
+ whisperPackage = pkgs.whisper-cpp-vulkan; # Vulkan (pre-built)
+ # whisperPackage = pkgs.whisper-cpp.override { cudaSupport = true; }; # NVIDIA
+ # whisperPackage = pkgs.whisper-cpp.override { rocmSupport = true; }; # AMD
+};
+```
+
+## Related
+
+- [stt-ptt Home Manager Module](../modules/home-manager/cli/stt-ptt.md) - Module documentation
+- [Adding Packages](../guides/adding-packages.md) - How to add new packages
diff --git a/modules/home-manager/cli/default.nix b/modules/home-manager/cli/default.nix
index 8149fb7..52b4631 100644
--- a/modules/home-manager/cli/default.nix
+++ b/modules/home-manager/cli/default.nix
@@ -1,6 +1,7 @@
# CLI/Terminal-related Home Manager modules
{
imports = [
+ ./stt-ptt.nix
./zellij-ps.nix
];
}
diff --git a/modules/home-manager/cli/stt-ptt.nix b/modules/home-manager/cli/stt-ptt.nix
new file mode 100644
index 0000000..e260950
--- /dev/null
+++ b/modules/home-manager/cli/stt-ptt.nix
@@ -0,0 +1,107 @@
+{
+ config,
+ lib,
+ pkgs,
+ ...
+}:
+with lib; let
+ cfg = config.cli.stt-ptt;
+
+ # Build stt-ptt package with the selected whisper package
+ sttPttPackage = pkgs.stt-ptt.override {
+ whisper-cpp = cfg.whisperPackage;
+ };
+
+ modelDir = "${config.xdg.dataHome}/stt-ptt/models";
+ modelPath = "${modelDir}/${cfg.model}.bin";
+
+ # HuggingFace URL for whisper.cpp models
+ modelUrl = "https://huggingface.co/ggerganov/whisper.cpp/resolve/main/${cfg.model}.bin";
+in {
+ options.cli.stt-ptt = {
+ enable = mkEnableOption "Push to Talk Speech to Text using Whisper";
+
+ whisperPackage = mkOption {
+ type = types.package;
+ default = pkgs.whisper-cpp;
+ description = ''
+ The whisper-cpp package to use. Available options:
+
+ Pre-built variants:
+ - `pkgs.whisper-cpp` - CPU-based inference (default)
+ - `pkgs.whisper-cpp-vulkan` - Vulkan GPU acceleration
+
+ Override options (can be combined):
+ - `cudaSupport` - NVIDIA CUDA support
+ - `rocmSupport` - AMD ROCm support
+ - `vulkanSupport` - Vulkan support
+ - `coreMLSupport` - Apple CoreML (macOS only)
+ - `metalSupport` - Apple Metal (macOS ARM only)
+
+ Example overrides:
+ - `pkgs.whisper-cpp.override { cudaSupport = true; }` - NVIDIA GPU
+ - `pkgs.whisper-cpp.override { rocmSupport = true; }` - AMD GPU
+ - `pkgs.whisper-cpp.override { vulkanSupport = true; }` - Vulkan
+ '';
+ example = literalExpression "pkgs.whisper-cpp.override { cudaSupport = true; }";
+ };
+
+ model = mkOption {
+ type = types.str;
+ default = "ggml-large-v3-turbo";
+ description = ''
+ The Whisper model to use. Models are downloaded from HuggingFace.
+
+ Available models (sorted by size/quality):
+ - `ggml-tiny` / `ggml-tiny.en` - 75MB, fastest, lowest quality
+ - `ggml-base` / `ggml-base.en` - 142MB, fast, basic quality
+ - `ggml-small` / `ggml-small.en` - 466MB, balanced
+ - `ggml-medium` / `ggml-medium.en` - 1.5GB, good quality
+ - `ggml-large-v1` - 2.9GB, high quality (original)
+ - `ggml-large-v2` - 2.9GB, high quality (improved)
+ - `ggml-large-v3` - 2.9GB, highest quality
+ - `ggml-large-v3-turbo` - 1.6GB, high quality, optimized speed (recommended)
+
+ Models ending in `.en` are English-only and slightly faster for English.
+ Quantized versions (q5_0, q5_1, q8_0) are also available for reduced size.
+ '';
+ example = "ggml-base.en";
+ };
+
+ notifyTimeout = mkOption {
+ type = types.int;
+ default = 3000;
+ description = ''
+ Notification timeout in milliseconds for the recording indicator.
+ Set to 0 for persistent notifications.
+ '';
+ example = 5000;
+ };
+ };
+
+ config = mkIf cfg.enable {
+ home.packages = [sttPttPackage];
+
+ home.sessionVariables = {
+ STT_MODEL = modelPath;
+ STT_NOTIFY_TIMEOUT = toString cfg.notifyTimeout;
+ };
+
+ # Create model directory and download model if not present
+ home.activation.downloadWhisperModel = lib.hm.dag.entryAfter ["writeBoundary"] ''
+ MODEL_DIR="${modelDir}"
+ MODEL_PATH="${modelPath}"
+ MODEL_URL="${modelUrl}"
+
+ $DRY_RUN_CMD mkdir -p "$MODEL_DIR"
+
+ if [ ! -f "$MODEL_PATH" ]; then
+ echo "Downloading Whisper model: ${cfg.model}..."
+ $DRY_RUN_CMD ${pkgs.curl}/bin/curl -L -o "$MODEL_PATH" "$MODEL_URL" || {
+ echo "Failed to download model from $MODEL_URL"
+ echo "Please download manually and place at: $MODEL_PATH"
+ }
+ fi
+ '';
+ };
+}
diff --git a/pkgs/default.nix b/pkgs/default.nix
index b11832b..6f794c9 100644
--- a/pkgs/default.nix
+++ b/pkgs/default.nix
@@ -7,6 +7,7 @@
mem0 = pkgs.callPackage ./mem0 {};
msty-studio = pkgs.callPackage ./msty-studio {};
pomodoro-timer = pkgs.callPackage ./pomodoro-timer {};
+ stt-ptt = pkgs.callPackage ./stt-ptt {};
tuxedo-backlight = pkgs.callPackage ./tuxedo-backlight {};
zellij-ps = pkgs.callPackage ./zellij-ps {};
}
diff --git a/pkgs/stt-ptt/default.nix b/pkgs/stt-ptt/default.nix
new file mode 100644
index 0000000..3e35576
--- /dev/null
+++ b/pkgs/stt-ptt/default.nix
@@ -0,0 +1,91 @@
+{
+ lib,
+ stdenv,
+ writeShellScriptBin,
+ whisper-cpp,
+ wtype,
+ libnotify,
+ pipewire,
+ busybox,
+}: let
+ script = writeShellScriptBin "stt-ptt" ''
+ #!/usr/bin/env bash
+ # stt-ptt - Push to Talk Speech to Text
+
+ CACHE_DIR="''${XDG_CACHE_HOME:-$HOME/.cache}/stt-ptt"
+ MODEL_DIR="''${XDG_DATA_HOME:-$HOME/.local/share}/stt-ptt/models"
+ AUDIO="$CACHE_DIR/stt.wav"
+ PID_FILE="$CACHE_DIR/stt.pid"
+
+ # Configurable via environment
+ STT_MODEL="''${STT_MODEL:-$MODEL_DIR/ggml-large-v3-turbo.bin}"
+ STT_NOTIFY_TIMEOUT="''${STT_NOTIFY_TIMEOUT:-3000}"
+
+ NOTIFY="${libnotify}/bin/notify-send"
+ PW_RECORD="${pipewire}/bin/pw-record"
+ WHISPER="${whisper-cpp}/bin/whisper-cli"
+ WTYPE="${wtype}/bin/wtype"
+ MKDIR="${busybox}/bin/mkdir"
+ RM="${busybox}/bin/rm"
+ CAT="${busybox}/bin/cat"
+ KILL="${busybox}/bin/kill"
+ TR="${busybox}/bin/tr"
+ SED="${busybox}/bin/sed"
+
+ # Ensure cache directory exists
+ "$MKDIR" -p "$CACHE_DIR"
+
+ case "''${1:-}" in
+ start)
+ "$RM" -f "$AUDIO" "$PID_FILE"
+ "$NOTIFY" -t "$STT_NOTIFY_TIMEOUT" -a "stt-ptt" "Recording..."
+ "$PW_RECORD" --rate=16000 --channels=1 "$AUDIO" &
+ echo $! > "$PID_FILE"
+ ;;
+ stop)
+ [[ -f "$PID_FILE" ]] && "$KILL" "$("$CAT" "$PID_FILE")" 2>/dev/null
+ "$RM" -f "$PID_FILE"
+
+ if [[ -f "$AUDIO" ]]; then
+ if [[ ! -f "$STT_MODEL" ]]; then
+ "$NOTIFY" -t "$STT_NOTIFY_TIMEOUT" -a "stt-ptt" "Error: Model not found at $STT_MODEL"
+ "$RM" -f "$AUDIO"
+ exit 1
+ fi
+ text=$("$WHISPER" -m "$STT_MODEL" -f "$AUDIO" -np -nt 2>/dev/null | "$TR" -d '\n' | "$SED" 's/^[[:space:]]*//;s/[[:space:]]*$//')
+ "$RM" -f "$AUDIO"
+ [[ -n "$text" ]] && "$WTYPE" -- "$text"
+ fi
+ ;;
+ *)
+ echo "Usage: stt-ptt {start|stop}"
+ echo ""
+ echo "Environment variables:"
+ echo " STT_MODEL - Path to whisper model (default: \$XDG_DATA_HOME/stt-ptt/models/ggml-large-v3-turbo.bin)"
+ echo " STT_NOTIFY_TIMEOUT - Notification timeout in ms (default: 3000)"
+ exit 1
+ ;;
+ esac
+ '';
+in
+ stdenv.mkDerivation {
+ pname = "stt-ptt";
+ version = "0.1.0";
+
+ dontUnpack = true;
+
+ # No buildInputs needed - all runtime deps are hardcoded with full nix store paths in the script
+
+ installPhase = ''
+ mkdir -p "$out/bin"
+ ln -s ${script}/bin/stt-ptt "$out/bin/stt-ptt"
+ '';
+
+ meta = with lib; {
+ description = "Push to Talk Speech to Text using Whisper";
+ homepage = "https://code.m3ta.dev/m3tam3re/nixpkgs";
+ license = licenses.mit;
+ platforms = platforms.linux;
+ mainProgram = "stt-ptt";
+ };
+ }