diff --git a/pkgs/applications/accessibility/nerd-dictation/default.nix b/pkgs/applications/accessibility/nerd-dictation/default.nix new file mode 100644 index 00000000000000..dc708a21d3bfa3 --- /dev/null +++ b/pkgs/applications/accessibility/nerd-dictation/default.nix @@ -0,0 +1,56 @@ +{ fetchFromGitHub +, substituteAll +, lib +, stdenv +, python3 +, pulseaudio +, xdotool +, ydotool +, unstableGitUpdater +}: + +python3.pkgs.buildPythonApplication rec { + pname = "nerd-dictation"; + version = "unstable-2023-04-19"; + + src = fetchFromGitHub { + owner = "ideasman42"; + repo = "nerd-dictation"; + rev = "96ef5ce64d37c7ebe9a704315665096ac4a697d4"; + hash = "sha256-w8EG6x0ieb9PwetNHkRHDE1FjVEH7QvjxPasaQwOAkg="; + }; + + patches = [ + (substituteAll { + src = ./fix-paths.patch; + parec = "${pulseaudio}/bin/parec"; + xdotool = "${xdotool}/bin/xdotool"; + ydotool = "${ydotool}/bin/ydotool"; + }) + ]; + + propagatedBuildInputs = with python3.pkgs; [ + vosk-python + ]; + + postPatch = '' + cd package/python + ''; + + # No tests. + doCheck = false; + + # Broken for some reason. + dontUsePythonCatchConflicts = true; + + passthru = { + updateScript = unstableGitUpdater { }; + }; + + meta = with lib; { + description = "Simple, hackable offline speech to text"; + homepage = "https://github.com/ideasman42/nerd-dictation"; + license = licenses.gpl2Plus; + maintainers = with maintainers; [ ]; + }; +} diff --git a/pkgs/applications/accessibility/nerd-dictation/fix-paths.patch b/pkgs/applications/accessibility/nerd-dictation/fix-paths.patch new file mode 100644 index 00000000000000..a0c5088d617d73 --- /dev/null +++ b/pkgs/applications/accessibility/nerd-dictation/fix-paths.patch @@ -0,0 +1,31 @@ +diff --git a/nerd-dictation b/nerd-dictation +index 9f4d1ad..f972c4f 100755 +--- a/nerd-dictation ++++ b/nerd-dictation +@@ -138,7 +138,7 @@ def execfile(filepath: str, mod: Optional[ModuleType] = None) -> Optional[Module + # Simulate Input: XDOTOOL + # + def simulate_typing_with_xdotool(delete_prev_chars: int, text: str) -> None: +- cmd = "xdotool" ++ cmd = "@xdotool@" + + # No setup/tear-down. + if delete_prev_chars == SIMULATE_INPUT_CODE_COMMAND: +@@ -169,7 +169,7 @@ def simulate_typing_with_xdotool(delete_prev_chars: int, text: str) -> None: + # Simulate Input: YDOTOOL + # + def simulate_typing_with_ydotool(delete_prev_chars: int, text: str) -> None: +- cmd = "ydotool" ++ cmd = "@ydotool@" + + # No setup/tear-down. + if delete_prev_chars == SIMULATE_INPUT_CODE_COMMAND: +@@ -873,7 +873,7 @@ def recording_proc_with_non_blocking_stdout( + ) -> "Tuple[subprocess.Popen[bytes], IO[bytes]]": + if input_method == "PAREC": + cmd = ( +- "parec", ++ "@parec@", + "--record", + "--rate=%d" % sample_rate, + "--channels=1", diff --git a/pkgs/development/python-modules/vosk-python/default.nix b/pkgs/development/python-modules/vosk-python/default.nix new file mode 100644 index 00000000000000..86a996c1271159 --- /dev/null +++ b/pkgs/development/python-modules/vosk-python/default.nix @@ -0,0 +1,49 @@ +{ buildPythonPackage +, cffi +, srt +, tqdm +, requests +, websockets +, substituteAll +, vosk-api +, lib +}: + +buildPythonPackage rec { + pname = "python-vosk"; + inherit (vosk-api) src version; + + patches = [ + (substituteAll { + src = ./fix-paths.patch; + vosk_api = vosk-api; + }) + ]; + + propagatedBuildInputs = [ + cffi + srt + tqdm + requests + websockets + ]; + + # No tests. + doCheck = false; + + pythonImportsCheck = [ + "vosk" + ]; + + postPatch = '' + cd python + ''; + + meta = with lib; { + description = "Python bindings for VOSK API"; + homepage = "https://github.com/alphacep/vosk-api"; + license = licenses.asl20; + maintainers = with maintainers; [ ]; + platforms = platforms.linux; + }; +} diff --git a/pkgs/development/python-modules/vosk-python/fix-paths.patch b/pkgs/development/python-modules/vosk-python/fix-paths.patch new file mode 100644 index 00000000000000..4bf5c8fc75c900 --- /dev/null +++ b/pkgs/development/python-modules/vosk-python/fix-paths.patch @@ -0,0 +1,39 @@ +diff --git a/python/setup.py b/python/setup.py +index dcfa5de..245f54e 100644 +--- a/python/setup.py ++++ b/python/setup.py +@@ -53,7 +53,6 @@ setuptools.setup( + long_description_content_type="text/markdown", + url="https://github.com/alphacep/vosk-api", + packages=setuptools.find_packages(), +- package_data = {'vosk': ['*.so', '*.dll', '*.dyld']}, + entry_points = { + 'console_scripts': ['vosk-transcriber=vosk.transcriber.cli:main'], + }, +diff --git a/python/vosk/__init__.py b/python/vosk/__init__.py +index 87cccad..d1c73b1 100644 +--- a/python/vosk/__init__.py ++++ b/python/vosk/__init__.py +@@ -19,7 +19,7 @@ MODEL_DIRS = [os.getenv("VOSK_MODEL_PATH"), Path("/usr/share/vosk"), + Path.home() / "AppData/Local/vosk", Path.home() / ".cache/vosk"] + + def open_dll(): +- dlldir = os.path.abspath(os.path.dirname(__file__)) ++ dlldir = "@vosk_api@/lib" + if sys.platform == "win32": + # We want to load dependencies too + os.environ["PATH"] = dlldir + os.pathsep + os.environ["PATH"] +diff --git a/python/vosk_builder.py b/python/vosk_builder.py +index 902910d..88bcac8 100644 +--- a/python/vosk_builder.py ++++ b/python/vosk_builder.py +@@ -3,8 +3,7 @@ + import os + from cffi import FFI + +-vosk_root=os.environ.get("VOSK_SOURCE", "..") +-cpp_command = "cpp " + vosk_root + "/src/vosk_api.h" ++cpp_command = "cpp @vosk_api@/include/vosk_api.h" + + ffibuilder = FFI() + ffibuilder.set_source("vosk.vosk_cffi", None) diff --git a/pkgs/tools/audio/kaldi/default.nix b/pkgs/tools/audio/kaldi/default.nix index 79da94f7243537..777a7535a10c26 100644 --- a/pkgs/tools/audio/kaldi/default.nix +++ b/pkgs/tools/audio/kaldi/default.nix @@ -3,7 +3,6 @@ , openblas , blas , lapack -, openfst , icu , cmake , pkg-config @@ -11,27 +10,23 @@ , git , python3 , Accelerate +, _experimental-update-script-combinators +, common-updater-scripts +, ripgrep +, unstableGitUpdater +, writeShellScript }: assert blas.implementation == "openblas" && lapack.implementation == "openblas"; -let - # rev from https://github.com/kaldi-asr/kaldi/blob/master/cmake/third_party/openfst.cmake - openfst = fetchFromGitHub { - owner = "kkm000"; - repo = "openfst"; - rev = "338225416178ac36b8002d70387f5556e44c8d05"; - sha256 = "sha256-MGEUuw7ex+WcujVdxpO2Bf5sB6Z0edcAeLGqW/Lo1Hs="; - }; -in -stdenv.mkDerivation { +stdenv.mkDerivation (finalAttrs: { pname = "kaldi"; - version = "unstable-2022-09-26"; + version = "unstable-2023-05-02"; src = fetchFromGitHub { owner = "kaldi-asr"; repo = "kaldi"; - rev = "f6f4ccaf213f0fe8b26e633a7dc0c802150626a0"; - sha256 = "sha256-ybW2J4lWf6YaQGZZvxEVDUMAg84DC17W+yX6ZsuBDac="; + rev = "71f38e62cad01c3078555bfe78d0f3a527422d75"; + sha256 = "sha256-2xm0F80cjovy/G9Ytq/iwa1eexZk0mromv6PPuNIT8U="; }; cmakeFlags = [ @@ -39,42 +34,11 @@ stdenv.mkDerivation { "-DBUILD_SHARED_LIBS=on" "-DBLAS_LIBRARIES=-lblas" "-DLAPACK_LIBRARIES=-llapack" + "-DFETCHCONTENT_SOURCE_DIR_OPENFST:PATH=${finalAttrs.passthru.sources.openfst}" ]; - enableParallelBuilding = true; - - preConfigure = '' - mkdir bin - cat > bin/git <<'EOF' - #!${stdenv.shell} - if [[ "$1" == "--version" ]]; then - # cmake checks this - ${git}/bin/git --version - elif [[ "$1" == "clone" ]]; then - # mock this call: - - # https://github.com/kaldi-asr/kaldi/blob/c9d8b9ad3fef89237ba5517617d977b7d70a7ed5/cmake/third_party/openfst.cmake#L5 - cp -r ${openfst} ''${@: -1} - chmod -R +w ''${@: -1} - elif [[ "$1" == "rev-list" ]]; then - # fix up this call: - # https://github.com/kaldi-asr/kaldi/blob/c9d8b9ad3fef89237ba5517617d977b7d70a7ed5/cmake/VersionHelper.cmake#L8 - echo 0 - elif [[ "$1" == "rev-parse" ]]; then - echo ${openfst.rev} - echo 0 - fi - true - EOF - chmod +x bin/git - export PATH=$(pwd)/bin:$PATH - ''; - - outputs = [ "out" "dev" ]; - buildInputs = [ openblas - openfst icu ] ++ lib.optionals stdenv.isDarwin [ Accelerate @@ -86,11 +50,46 @@ stdenv.mkDerivation { python3 ]; + preConfigure = '' + cmakeFlagsArray+=( + # Extract version without the need for git. + # https://github.com/kaldi-asr/kaldi/blob/71f38e62cad01c3078555bfe78d0f3a527422d75/cmake/VersionHelper.cmake + # Patch number is not actually used by default so we can just ignore it. + # https://github.com/kaldi-asr/kaldi/blob/71f38e62cad01c3078555bfe78d0f3a527422d75/CMakeLists.txt#L214 + "-DKALDI_VERSION=$(cat src/.version)" + ) + ''; + postInstall = '' mkdir -p $out/share/kaldi cp -r ../egs $out/share/kaldi ''; + passthru = { + sources = { + # rev from https://github.com/kaldi-asr/kaldi/blob/master/cmake/third_party/openfst.cmake + openfst = fetchFromGitHub { + owner = "kkm000"; + repo = "openfst"; + rev = "338225416178ac36b8002d70387f5556e44c8d05"; + hash = "sha256-MGEUuw7ex+WcujVdxpO2Bf5sB6Z0edcAeLGqW/Lo1Hs="; + }; + }; + + updateScript = + let + updateSource = unstableGitUpdater {}; + updateOpenfst = writeShellScript "update-openfst" '' + hash=$(${ripgrep}/bin/rg --multiline --pcre2 --only-matching 'FetchContent_Declare\(\s*openfst[^)]*GIT_TAG\s*([0-9a-f]{40})' --replace '$1' "${finalAttrs.src}/cmake/third_party/openfst.cmake") + ${common-updater-scripts}/bin/update-source-version kaldi.sources.openfst "$hash" --source-key=out "--version-key=rev" + ''; + in + _experimental-update-script-combinators.sequence [ + updateSource + updateOpenfst + ]; + }; + meta = with lib; { description = "Speech Recognition Toolkit"; homepage = "https://kaldi-asr.org"; @@ -98,4 +97,4 @@ stdenv.mkDerivation { maintainers = with maintainers; [ mic92 ]; platforms = platforms.unix; }; -} +}) diff --git a/pkgs/tools/audio/vosk-api/default.nix b/pkgs/tools/audio/vosk-api/default.nix new file mode 100644 index 00000000000000..ba900da2e639c9 --- /dev/null +++ b/pkgs/tools/audio/vosk-api/default.nix @@ -0,0 +1,72 @@ +{ stdenv +, fetchFromGitHub +, fetchpatch2 +, cmake +, kaldi +, openblas +, python3 +, lib +}: + +let + tweakedDependencies = { + kaldi = kaldi.overrideAttrs (attrs: { + patches = attrs.patches or [ ] ++ [ + # Vosk patches: https://github.com/kaldi-asr/kaldi/compare/master...alphacep:kaldi:vosk + # https://github.com/alphacep/vosk-api/issues/1082 + # Expose raw lattice + (fetchpatch2 { + url = "https://github.com/kaldi-asr/kaldi/commit/dd629c862e7ff45ed9fe79c38dcb7c793549dc03.patch"; + hash = "sha256-Wwn/FY7pi0sndt3esbFtU8k+MoHUluEVZ5jEq1/b7o8="; + }) + # Allow alignment of the partial lattice without warnings + (fetchpatch2 { + url = "https://github.com/kaldi-asr/kaldi/commit/98155d8ae0a7f6b2f5d5ed33c07927aaefe96622.patch"; + hash = "sha256-uWGKQeSNkvKdndc8jcbnOhxtzt2eiZKLluQPrtN5wf4="; + }) + ]; + }); + }; +in +let + # Shadow the original kaldi to prevent accidental use. + inherit (tweakedDependencies) kaldi; +in +stdenv.mkDerivation rec { + name = "vosk-api"; + version = "0.3.45"; + + src = fetchFromGitHub { + owner = "alphacep"; + repo = "vosk-api"; + rev = "v${version}"; + hash = "sha256-sa+rUJP0JvZo7YOFrWDEAuySlQJstOBnldz/LMiu/pk="; + }; + + nativeBuildInputs = [ + cmake + ]; + + buildInputs = [ + kaldi + openblas + ]; + + cmakeFlags = [ + "-DBUILD_SHARED_LIBS:BOOL=on" + ]; + + passthru = { + tests = { + python = python3.pkgs.vosk-python; + }; + }; + + meta = with lib; { + description = "Offline speech recognition API"; + homepage = "https://github.com/alphacep/vosk-api"; + license = licenses.asl20; + maintainers = with maintainers; [ ]; + platforms = platforms.unix; + }; +} diff --git a/pkgs/top-level/all-packages.nix b/pkgs/top-level/all-packages.nix index 73ff9f5e1089f1..c38d0c3e62ccc7 100644 --- a/pkgs/top-level/all-packages.nix +++ b/pkgs/top-level/all-packages.nix @@ -1778,6 +1778,8 @@ with pkgs; vopono = callPackage ../tools/networking/vopono { }; + vosk-api = callPackage ../tools/audio/vosk-api { }; + winbox = callPackage ../tools/admin/winbox { wine = wineWowPackages.staging; }; @@ -10363,6 +10365,8 @@ with pkgs; nerdfonts = callPackage ../data/fonts/nerdfonts { }; + nerd-dictation = callPackage ../applications/accessibility/nerd-dictation { }; + netatalk = callPackage ../tools/filesystems/netatalk { }; netavark = callPackage ../tools/networking/netavark { }; diff --git a/pkgs/top-level/python-packages.nix b/pkgs/top-level/python-packages.nix index 7fef6b0d2445c7..c725b2a4bd6f31 100644 --- a/pkgs/top-level/python-packages.nix +++ b/pkgs/top-level/python-packages.nix @@ -12802,6 +12802,8 @@ self: super: with self; { volvooncall = callPackage ../development/python-modules/volvooncall { }; + vosk-python = callPackage ../development/python-modules/vosk-python { }; + vowpalwabbit = callPackage ../development/python-modules/vowpalwabbit { }; vpk = callPackage ../development/python-modules/vpk { };