diff --git a/config/hypr/hyprland.conf b/config/hypr/hyprland.conf index 624d037..10da9b9 100644 --- a/config/hypr/hyprland.conf +++ b/config/hypr/hyprland.conf @@ -135,6 +135,7 @@ bind = , I, exec, hyprctl dispatch submap reset; ctl idle bind = , K, exec, hyprctl dispatch submap reset; ctl keyboard next bind = , M, exec, hyprctl dispatch submap reset; ctl media bind = , P, exec, hyprctl dispatch submap reset; ctl power +bind = , S, exec, hyprctl dispatch submap reset; ctl dictate bind = , T, exec, hyprctl dispatch submap reset; theme bind = , catchall, submap, reset diff --git a/flake.lock b/flake.lock index 5d38df8..e56aba1 100644 --- a/flake.lock +++ b/flake.lock @@ -39,24 +39,6 @@ "type": "github" } }, - "flake-utils": { - "inputs": { - "systems": "systems" - }, - "locked": { - "lastModified": 1731533236, - "narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=", - "owner": "numtide", - "repo": "flake-utils", - "rev": "11707dc2f618dd54ca8739b309ec4fc024de578b", - "type": "github" - }, - "original": { - "owner": "numtide", - "repo": "flake-utils", - "type": "github" - } - }, "home-manager": { "inputs": { "nixpkgs": [ @@ -220,46 +202,9 @@ "neovim-nightly": "neovim-nightly", "nixos-hardware": "nixos-hardware", "nixpkgs": "nixpkgs_3", - "whisper-dictation": "whisper-dictation", "zen-browser": "zen-browser" } }, - "systems": { - "locked": { - "lastModified": 1681028828, - "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", - "owner": "nix-systems", - "repo": "default", - "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", - "type": "github" - }, - "original": { - "owner": "nix-systems", - "repo": "default", - "type": "github" - } - }, - "whisper-dictation": { - "inputs": { - "flake-utils": "flake-utils", - "nixpkgs": [ - "nixpkgs" - ] - }, - "locked": { - "lastModified": 1771198514, - "narHash": "sha256-5Dd0zVTh+nUf3lxrDpgQSHbr0pU40hGh/hUuRbwG790=", - "owner": "jacopone", - "repo": "whisper-dictation", - "rev": "20190a148d8bce6f0d0b78a56cc22afe02844e8f", - "type": "github" - }, - "original": { - "owner": "jacopone", - "repo": "whisper-dictation", - "type": "github" - } - }, "zen-browser": { "inputs": { "home-manager": "home-manager_2", diff --git a/flake.nix b/flake.nix index 7a3ee18..8f56f65 100644 --- a/flake.nix +++ b/flake.nix @@ -11,11 +11,7 @@ zen-browser.url = "github:0xc000022070/zen-browser-flake"; claude-code.url = "github:ryoppippi/claude-code-overlay"; neovim-nightly.url = "github:nix-community/neovim-nightly-overlay"; - whisper-dictation = { - url = "github:jacopone/whisper-dictation"; - inputs.nixpkgs.follows = "nixpkgs"; - }; - }; +}; outputs = { @@ -25,7 +21,6 @@ zen-browser, claude-code, neovim-nightly, - whisper-dictation, ... }: let @@ -92,7 +87,7 @@ home-manager.lib.homeManagerConfiguration { pkgs = mkPkgs hostConfig.platform [ ]; extraSpecialArgs = { - inherit zen-browser whisper-dictation hostConfig; + inherit zen-browser hostConfig; }; modules = [ ./home/home.nix ]; }; @@ -134,6 +129,10 @@ "tailscale" "libfprint-2-tod1-goodix" "brgenml1lpr" + "cuda_cccl" + "cuda_cudart" + "libcublas" + "cuda_nvcc" ] ); } @@ -144,7 +143,7 @@ home-manager.backupFileExtension = "bak"; home-manager.users.barrett = import ./home/home.nix; home-manager.extraSpecialArgs = { - inherit zen-browser whisper-dictation; + inherit zen-browser; hostConfig = xps15Config; }; } diff --git a/home/modules/dictation.nix b/home/modules/dictation.nix index 5060c3f..a71bed4 100644 --- a/home/modules/dictation.nix +++ b/home/modules/dictation.nix @@ -3,19 +3,17 @@ pkgs, config, hostConfig, - whisper-dictation, ... }: let + whisper = pkgs.whisper-cpp.override { cudaSupport = hostConfig.gpu == "nvidia"; }; modelDir = "${config.home.homeDirectory}/.local/share/whisper-models"; - model = "ggml-base.bin"; + model = "ggml-medium.bin"; modelUrl = "https://huggingface.co/ggerganov/whisper.cpp/resolve/main/${model}"; in { - home.packages = [ - whisper-dictation.packages.${hostConfig.platform}.default - ]; + home.packages = [ whisper ]; home.activation.downloadWhisperModel = lib.hm.dag.entryAfter [ "writeBoundary" ] '' if [ ! -f "${modelDir}/${model}" ]; then @@ -23,29 +21,4 @@ in run ${pkgs.curl}/bin/curl -L -o "${modelDir}/${model}" "${modelUrl}" fi ''; - - xdg.configFile."whisper-dictation/config.yaml".text = builtins.toJSON { - whisper = { - model = "base"; - language = "auto"; - }; - hotkey = { - key = "KEY_DOT"; - modifiers = [ "KEY_LEFTMETA" ]; - }; - }; - - systemd.user.services.whisper-dictation = { - Unit = { - Description = "Whisper Dictation speech-to-text daemon"; - After = [ "graphical-session.target" "ydotoold.service" ]; - PartOf = [ "graphical-session.target" ]; - }; - Service = { - ExecStart = "${whisper-dictation.packages.${hostConfig.platform}.default}/bin/whisper-dictation"; - Restart = "on-failure"; - RestartSec = 5; - }; - Install.WantedBy = [ "graphical-session.target" ]; - }; } diff --git a/hosts/xps15/configuration.nix b/hosts/xps15/configuration.nix index 4062b5d..2d0b521 100644 --- a/hosts/xps15/configuration.nix +++ b/hosts/xps15/configuration.nix @@ -95,24 +95,10 @@ in "libvirt" "storage" "power" - "input" ]; shell = pkgs.zsh; }; - hardware.uinput.enable = true; - - systemd.user.services.ydotoold = { - description = "ydotool daemon"; - wantedBy = [ "graphical-session.target" ]; - partOf = [ "graphical-session.target" ]; - serviceConfig = { - ExecStart = "${pkgs.ydotool}/bin/ydotoold"; - Restart = "on-failure"; - RestartSec = 3; - }; - }; - programs.chromium = { enable = true; extraOpts = { diff --git a/scripts/ctl b/scripts/ctl index b713e12..58f96ec 100755 --- a/scripts/ctl +++ b/scripts/ctl @@ -533,6 +533,51 @@ power) "$shutdown") systemctl poweroff ;; esac ;; +dictate) + require pw-record whisper-cli wl-copy notify-send + dtmp="${XDG_RUNTIME_DIR:-/tmp}/dictation" + mkdir -p "$dtmp" + dpid="$dtmp/rec_pid" + daudio="$dtmp/recording.wav" + dmodel="${DICTATE_MODEL:-medium}" + dmodel_dir="${XDG_DATA_HOME:-$HOME/.local/share}/whisper-models" + dmodel_file="$dmodel_dir/ggml-$dmodel.bin" + + if [ -f "$dpid" ] && kill -0 "$(cat "$dpid")" 2>/dev/null; then + kill "$(cat "$dpid")" 2>/dev/null + rm -f "$dpid" + sleep 0.2 + if [ ! -s "$daudio" ]; then + notify-send -a ctl "no audio" + exit 1 + fi + notify-send -a ctl "transcribing..." + text=$(whisper-cli \ + --model "$dmodel_file" \ + --language "${DICTATE_LANG:-en}" \ + --no-prints --no-timestamps \ + "$daudio" 2>/dev/null) + rm -f "$daudio" + text=$(printf '%s' "$text" | sed 's/^[[:space:]]*//;s/[[:space:]]*$//' | tr -s ' ') + if [ -z "$text" ]; then + notify-send -a ctl "no speech detected" + exit 1 + fi + printf '%s' "$text" | wl-copy + notify-send -a ctl "$text" + exit 0 + fi + + if [ ! -f "$dmodel_file" ]; then + notify-send -a ctl "downloading whisper $dmodel model..." + mkdir -p "$dmodel_dir" + curl -L -o "$dmodel_file" \ + "https://huggingface.co/ggerganov/whisper.cpp/resolve/main/ggml-$dmodel.bin" + fi + notify-send -a ctl "recording..." + pw-record "$daudio" & + printf '%s' "$!" > "$dpid" + ;; idle) require notify-send if systemctl --user is-active --quiet hypridle.service; then @@ -546,7 +591,7 @@ idle) fi ;; *) - echo "Usage: ctl {screenshot|keyboard|audio|wifi|brightness|volume|media|wallpaper|power|idle|clip}" >&2 + echo "Usage: ctl {screenshot|keyboard|audio|wifi|brightness|volume|media|wallpaper|power|idle|clip|dictate}" >&2 exit 1 ;; esac