Files
voice_linux/plans/unified_binary_plan.md

6.3 KiB

Unified Binary + Startup Dialog Plan

Summary

Merge the CLI and GUI into a single voice_linux binary that always opens the GTK3 window. On launch, a startup dialog appears letting the user choose CPU vs GPU and select which whisper model to use. All available ggml models are downloaded to ./models/ so the user can test them.

Current State

Item Status
Two binaries: voice_linux (CLI) and voice_linux_gui (GTK3) Active
GPU passthrough + NVIDIA driver + CUDA toolkit Working
whisper.cpp built with GGML_CUDA=ON Working
--gpu / --cpu flags parsed in both binaries Working
Model path hardcoded in config.ini as model_path= Working
transcribe_init() called once at startup before GTK loop Working

Architecture Changes

flowchart TD
    A[./voice_linux launched] --> B[Load config.ini]
    B --> C[audio_init]
    C --> D{Show Startup Dialog}
    D --> |User picks CPU/GPU + model| E[transcribe_init with selections]
    E --> F[typer_init]
    F --> G[Main GTK window + event loop]
    G --> H[On window close: cleanup]

Key change: transcribe_init() is deferred until after the startup dialog returns the user's selections. Audio init happens first so the mic meter can work, but model loading waits for the dialog.

Whisper Models to Download

All English-optimized ggml models from the whisper.cpp HuggingFace repo. The GTX 1080 Ti has 11 GB VRAM, so all models up to large-v3 fit comfortably.

Model File Size VRAM est.
tiny.en ggml-tiny.en.bin ~75 MB ~200 MB
base.en ggml-base.en.bin ~142 MB ~350 MB
small.en ggml-small.en.bin ~466 MB ~1 GB
medium.en ggml-medium.en.bin ~1.5 GB ~3 GB
large-v3-turbo ggml-large-v3-turbo.bin ~1.6 GB ~3.5 GB
large-v3 ggml-large-v3.bin ~3.1 GB ~6 GB

Base URL: https://huggingface.co/ggerganov/whisper.cpp/resolve/main/

Detailed Steps

Step 1: Download all whisper models

Create a scripts/download_models.sh that downloads each model to ./models/ if not already present. Also callable from build.sh.

Step 2: Add startup dialog to gui_main.c

Insert a modal GtkDialog that runs before transcribe_init():

  • GPU toggle: GtkComboBoxText with entries CPU and GPU. Default to GPU if CUDA device is detected at runtime, otherwise CPU.
  • Model picker: GtkComboBoxText populated by scanning ./models/ggml-*.bin files. Display friendly names derived from filenames. Pre-select the model from config.ini.
  • OK button: closes dialog, returns selections to main().

The dialog is a transient child of a hidden parent window so it appears centered on screen.

GPU detection at dialog time: check if nvidia-smi exits 0, or check if the whisper.cpp CUDA backend reports a device. Simplest approach: try access /dev/nvidia0 — if it exists, default to GPU.

Step 3: Defer transcribe_init

Current flow in gui_main.c main():

config_load -> use_gpu from argv -> audio_init -> transcribe_init -> typer_init -> gtk loop

New flow:

config_load -> audio_init -> show_startup_dialog -> transcribe_init(dialog selections) -> typer_init -> gtk loop

The startup dialog returns:

  • int use_gpu — 0 or 1
  • char model_path[512] — full path to selected model file

These override whatever was in config.ini / argv.

Step 4: Remove CLI binary from default build

  • Keep src/main.c in the repo for reference or future headless use, but do not build it by default.
  • In build.sh: remove the separate CLI gcc invocation. The GUI gcc line outputs ./voice_linux instead of ./voice_linux_gui.
  • In Makefile: change TARGET to build from gui_main.c sources. Remove voice_linux_gui target. Optionally add a cli target for headless builds.
  • The BUILD_GUI env var is no longer needed; remove it.

Step 5: Add use_gpu to config.ini and config.c

Add a use_gpu=1 line to config.ini. Parse it in config_load_file(). This becomes the default for the startup dialog, overridable by the dialog selection. The --gpu / --cpu CLI flags still work as overrides.

Add int use_gpu; to voice_config_t.

Step 6: Update README.md

Simplify run instructions to just ./voice_linux. Remove voice_linux_gui references. Document the startup dialog behavior.

Step 7: Build and test

bash ./build.sh
./voice_linux

Verify:

  • Startup dialog appears with CPU/GPU toggle and model list
  • Selecting GPU + medium.en loads correctly with CUDA backend
  • Main window works as before after dialog closes

Files Modified

File Change
src/gui_main.c Add startup dialog function; defer transcribe_init; update main() flow
src/config.h Add use_gpu field to voice_config_t
src/config.c Parse use_gpu from config file
config.ini Add use_gpu=1
build.sh Single binary output ./voice_linux; remove BUILD_GUI toggle; remove CLI build
Makefile Single target ./voice_linux from gui sources
README.md Update run instructions
scripts/download_models.sh New script to download all models

Files NOT Modified

File Reason
src/main.c Kept for optional headless builds; not built by default
src/transcribe.c API unchanged — transcribe_init / transcribe_cleanup already support the needed flow
src/transcribe.h No changes needed
src/audio.c / src/audio.h No changes needed
src/hotkey.c / src/hotkey.h Only used by CLI; not linked into GUI binary

Risk Notes

  • The startup dialog adds a brief pause before the main window. Model loading (especially large-v3 at 3 GB) takes several seconds — a progress indicator or status label in the main window info area will show loading state.
  • If no models are found in ./models/, the dialog should show an error message and offer to run the download script.
  • The transcribe_init call blocks the GTK main thread during model load. For large models this could be 5-10 seconds. An improvement would be async loading with a progress bar, but for v1 a simple "Loading model..." label update before the blocking call is sufficient.