Last active
July 30, 2026 08:51
-
-
Save shyamzzp/98c0e820a3e82c27b64a1c53aa027b41 to your computer and use it in GitHub Desktop.
Install local live Zoom/system-audio transcription on macOS, Linux, or Windows
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| # Installs local live Windows system-audio transcription with whisper.cpp. | |
| [CmdletBinding()] | |
| param( | |
| [switch]$VerifyOnly | |
| ) | |
| $ErrorActionPreference = 'Stop' | |
| $RepoUrl = 'https://github.com/ggml-org/whisper.cpp.git' | |
| $WhisperDirectory = if ($env:WHISPER_DIRECTORY) { $env:WHISPER_DIRECTORY } else { Join-Path $HOME 'whisper.cpp' } | |
| $ModelPath = Join-Path $WhisperDirectory 'models\ggml-base.en.bin' | |
| $VcpkgDirectory = if ($env:VCPKG_ROOT) { $env:VCPKG_ROOT } else { Join-Path $HOME 'vcpkg' } | |
| function Stop-Install([string]$Message) { | |
| throw "Error: $Message" | |
| } | |
| function Write-Section([string]$Message) { | |
| Write-Host "`n==> $Message" -ForegroundColor Cyan | |
| } | |
| function Assert-Windows { | |
| if ([System.Environment]::OSVersion.Platform -ne [System.PlatformID]::Win32NT) { | |
| Stop-Install 'This installer requires Windows.' | |
| } | |
| } | |
| function Add-CommonToolPaths { | |
| $paths = @('C:\Program Files\CMake\bin', 'C:\Program Files\Git\cmd') | |
| foreach ($path in $paths) { | |
| if ((Test-Path $path) -and -not (($env:Path -split ';') -contains $path)) { | |
| $env:Path = "$env:Path;$path" | |
| } | |
| } | |
| } | |
| function Install-WinGetPackage([string]$Id, [string[]]$Arguments = @()) { | |
| & winget install --id $Id --exact --accept-package-agreements --accept-source-agreements @Arguments | |
| if ($LASTEXITCODE -ne 0) { Stop-Install "WinGet failed to install $Id." } | |
| } | |
| function Install-Dependencies { | |
| Get-Command winget -ErrorAction Stop | Out-Null | |
| Write-Section 'Installing Git, CMake, and Visual Studio C++ Build Tools' | |
| Install-WinGetPackage 'Git.Git' | |
| Install-WinGetPackage 'Kitware.CMake' | |
| Install-WinGetPackage 'Microsoft.VisualStudio.2022.BuildTools' @('--override', '--wait --quiet --add Microsoft.VisualStudio.Workload.VCTools --includeRecommended') | |
| Add-CommonToolPaths | |
| Get-Command git -ErrorAction Stop | Out-Null | |
| Get-Command cmake -ErrorAction Stop | Out-Null | |
| } | |
| function Install-VcpkgSdl2 { | |
| if (-not (Test-Path (Join-Path $VcpkgDirectory '.git'))) { | |
| Write-Section 'Cloning vcpkg' | |
| & git clone https://github.com/microsoft/vcpkg.git $VcpkgDirectory | |
| if ($LASTEXITCODE -ne 0) { Stop-Install 'Could not clone vcpkg.' } | |
| } | |
| $vcpkg = Join-Path $VcpkgDirectory 'vcpkg.exe' | |
| if (-not (Test-Path $vcpkg)) { | |
| Write-Section 'Bootstrapping vcpkg' | |
| & (Join-Path $VcpkgDirectory 'bootstrap-vcpkg.bat') -disableMetrics | |
| if ($LASTEXITCODE -ne 0) { Stop-Install 'Could not bootstrap vcpkg.' } | |
| } | |
| Write-Section 'Installing SDL2' | |
| & $vcpkg install sdl2:x64-windows | |
| if ($LASTEXITCODE -ne 0) { Stop-Install 'Could not install SDL2 through vcpkg.' } | |
| } | |
| function Prepare-WhisperCheckout { | |
| if (-not (Test-Path $WhisperDirectory)) { | |
| Write-Section 'Cloning whisper.cpp' | |
| & git clone $RepoUrl $WhisperDirectory | |
| if ($LASTEXITCODE -ne 0) { Stop-Install 'Could not clone whisper.cpp.' } | |
| return | |
| } | |
| if (-not (Test-Path (Join-Path $WhisperDirectory '.git'))) { | |
| Stop-Install "$WhisperDirectory exists but is not a Git checkout; move it aside and run again." | |
| } | |
| $origin = (& git -C $WhisperDirectory remote get-url origin).Trim() | |
| if ($origin -notin @($RepoUrl, 'https://github.com/ggml-org/whisper.cpp')) { | |
| Stop-Install "$WhisperDirectory is not the official whisper.cpp checkout; refusing to modify it." | |
| } | |
| if ((& git -C $WhisperDirectory status --porcelain)) { | |
| Stop-Install "$WhisperDirectory has uncommitted changes; commit or stash them before running this installer." | |
| } | |
| Write-Section 'Updating whisper.cpp' | |
| & git -C $WhisperDirectory pull --ff-only | |
| if ($LASTEXITCODE -ne 0) { Stop-Install 'Could not update whisper.cpp.' } | |
| } | |
| function Build-Whisper { | |
| $toolchain = Join-Path $VcpkgDirectory 'scripts\buildsystems\vcpkg.cmake' | |
| Write-Section 'Building whisper-stream' | |
| & cmake -S $WhisperDirectory -B (Join-Path $WhisperDirectory 'build') -A x64 -DWHISPER_SDL2=ON "-DCMAKE_TOOLCHAIN_FILE=$toolchain" | |
| if ($LASTEXITCODE -ne 0) { Stop-Install 'CMake configuration failed.' } | |
| & cmake --build (Join-Path $WhisperDirectory 'build') --config Release | |
| if ($LASTEXITCODE -ne 0) { Stop-Install 'whisper.cpp build failed.' } | |
| } | |
| function Get-StreamBinary { | |
| $binary = Get-ChildItem -Path (Join-Path $WhisperDirectory 'build') -Filter 'whisper-stream.exe' -File -Recurse | Select-Object -First 1 | |
| if (-not $binary) { Stop-Install 'Installation verification failed: whisper-stream.exe was not built.' } | |
| return $binary.FullName | |
| } | |
| function Get-Model { | |
| if (-not (Test-Path $ModelPath)) { | |
| Write-Section 'Downloading the base English Whisper model' | |
| Invoke-WebRequest -Uri 'https://huggingface.co/ggerganov/whisper.cpp/resolve/main/ggml-base.en.bin' -OutFile $ModelPath | |
| } | |
| if (-not (Test-Path $ModelPath)) { Stop-Install 'Installation verification failed: model was not downloaded.' } | |
| } | |
| function Show-Usage([string]$StreamBinary) { | |
| $TranscriptCommand = "& '$StreamBinary' -m '$ModelPath' --step 0 --length 30000 -vth 0.6 --capture <ID> 2>&1 | Tee-Object -FilePath `"$WhisperDirectory\transcript-`$(Get-Date -Format yyyy-MM-dd_HH-mm-ss).txt`"" | |
| @" | |
| Installation complete for Windows. | |
| 1. Download VB-CABLE from https://vb-audio.com/Cable/. | |
| 2. Extract it, run VBCABLE_Setup_x64.exe as Administrator, then restart Windows. | |
| 3. In Zoom > Settings > Audio > Speaker, select CABLE Input (VB-Audio Virtual Cable). | |
| 4. In Windows Sound > Recording > CABLE Output > Properties > Listen, enable | |
| Listen to this device and select your headphones, so you still hear the call. | |
| 5. Start the command below. It lists capture devices; choose the CABLE Output | |
| device ID with --capture <ID>. Keep Zoom's microphone set to your real mic. | |
| Start transcription: | |
| $TranscriptCommand | |
| The transcript is saved beside whisper.cpp as transcript-YYYY-MM-DD_HH-mm-ss.txt. | |
| Stop transcription with Ctrl-C. Ensure call participants consent where required. | |
| "@ | Write-Host | |
| } | |
| Assert-Windows | |
| if ($VerifyOnly) { | |
| $stream = Get-StreamBinary | |
| if (-not (Test-Path $ModelPath)) { Stop-Install 'Installation verification failed: model was not downloaded.' } | |
| Write-Host 'Installation verification passed.' | |
| exit 0 | |
| } | |
| Install-Dependencies | |
| Install-VcpkgSdl2 | |
| Prepare-WhisperCheckout | |
| Build-Whisper | |
| $stream = Get-StreamBinary | |
| Get-Model | |
| Show-Usage $stream |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/usr/bin/env bash | |
| # Installs local live transcription on macOS or Linux. | |
| set -euo pipefail | |
| readonly WHISPER_REPOSITORY='https://github.com/ggml-org/whisper.cpp.git' | |
| readonly WHISPER_DIRECTORY="${WHISPER_DIRECTORY:-${HOME}/whisper.cpp}" | |
| readonly MODEL_PATH="${WHISPER_DIRECTORY}/models/ggml-base.en.bin" | |
| readonly STREAM_BINARY="${WHISPER_DIRECTORY}/build/bin/whisper-stream" | |
| usage() { | |
| cat <<'EOF' | |
| Install local live transcription for macOS system audio, Linux desktop audio, and Windows (using install.ps1). | |
| Usage: | |
| curl -fsSL "<raw-gist-url>/install.sh" | bash | |
| bash install-live-zoom-transcription.sh --verify-only | |
| macOS uses BlackHole and Audio MIDI Setup. Linux uses a PipeWire/PulseAudio sink | |
| monitor. Windows users should run install.ps1 from PowerShell. | |
| EOF | |
| } | |
| die() { printf 'Error: %s\n' "$*" >&2; exit 1; } | |
| info() { printf '\n==> %s\n' "$*"; } | |
| configure_homebrew_path() { | |
| if [[ -x /opt/homebrew/bin/brew ]]; then | |
| eval "$(/opt/homebrew/bin/brew shellenv)" | |
| elif [[ -x /usr/local/bin/brew ]]; then | |
| eval "$(/usr/local/bin/brew shellenv)" | |
| fi | |
| } | |
| install_macos_dependencies() { | |
| if ! xcode-select -p >/dev/null 2>&1; then | |
| info 'Requesting Apple Command Line Tools' | |
| xcode-select --install || true | |
| printf '\nComplete the Apple Command Line Tools dialog, then run this installer again.\n' | |
| exit 0 | |
| fi | |
| configure_homebrew_path | |
| if ! command -v brew >/dev/null 2>&1; then | |
| info 'Installing Homebrew' | |
| NONINTERACTIVE=1 /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)" | |
| configure_homebrew_path | |
| fi | |
| command -v brew >/dev/null 2>&1 || die 'Homebrew was not found after installation.' | |
| info 'Installing BlackHole and build dependencies' | |
| brew install blackhole-2ch cmake sdl2 git | |
| } | |
| install_linux_dependencies() { | |
| command -v sudo >/dev/null 2>&1 || die 'Linux installation requires sudo to install packages.' | |
| info 'Installing Linux build and audio-routing dependencies' | |
| if command -v apt-get >/dev/null 2>&1; then | |
| sudo apt-get update | |
| sudo apt-get install -y build-essential cmake libsdl2-dev git curl pavucontrol | |
| elif command -v dnf >/dev/null 2>&1; then | |
| sudo dnf install -y gcc-c++ make cmake SDL2-devel git curl pavucontrol | |
| elif command -v pacman >/dev/null 2>&1; then | |
| sudo pacman -Sy --needed --noconfirm base-devel cmake sdl2 git curl pavucontrol | |
| else | |
| die 'Supported Linux package managers: apt-get, dnf, or pacman.' | |
| fi | |
| command -v pactl >/dev/null 2>&1 || die 'PipeWire/PulseAudio (pactl) is required to capture desktop audio.' | |
| } | |
| prepare_whisper_checkout() { | |
| if [[ ! -e "$WHISPER_DIRECTORY" ]]; then | |
| info 'Cloning whisper.cpp' | |
| git clone "$WHISPER_REPOSITORY" "$WHISPER_DIRECTORY" | |
| return | |
| fi | |
| [[ -d "$WHISPER_DIRECTORY/.git" ]] || die "$WHISPER_DIRECTORY exists but is not a git checkout; move it aside and run again." | |
| local origin | |
| origin=$(git -C "$WHISPER_DIRECTORY" remote get-url origin) | |
| [[ "$origin" == 'https://github.com/ggml-org/whisper.cpp' || "$origin" == "$WHISPER_REPOSITORY" ]] || die "$WHISPER_DIRECTORY is not the official whisper.cpp checkout; refusing to modify it." | |
| [[ -z "$(git -C "$WHISPER_DIRECTORY" status --porcelain)" ]] || die "$WHISPER_DIRECTORY has uncommitted changes; commit or stash them before running this installer." | |
| info 'Updating whisper.cpp' | |
| git -C "$WHISPER_DIRECTORY" pull --ff-only | |
| } | |
| build_and_verify() { | |
| info 'Building whisper-stream' | |
| cmake -S "$WHISPER_DIRECTORY" -B "$WHISPER_DIRECTORY/build" -DWHISPER_SDL2=ON | |
| cmake --build "$WHISPER_DIRECTORY/build" --config Release | |
| if [[ ! -f "$MODEL_PATH" ]]; then | |
| info 'Downloading the base English Whisper model' | |
| bash "$WHISPER_DIRECTORY/models/download-ggml-model.sh" base.en | |
| fi | |
| [[ -x "$STREAM_BINARY" && -f "$MODEL_PATH" ]] || die 'Installation verification failed.' | |
| } | |
| print_macos_steps() { | |
| cat <<'EOF' | |
| Installation complete for macOS. | |
| 1. Open Audio MIDI Setup > + > Create Multi-Output Device. | |
| 2. Tick both your headphones and BlackHole 2ch. | |
| 3. In Zoom > Settings > Audio > Speaker, select that Multi-Output Device. | |
| 4. In System Settings > Sound > Input, select BlackHole 2ch. | |
| 5. Allow Terminal microphone access when macOS asks. | |
| Start transcription: | |
| TRANSCRIPT_FILE="$HOME/whisper.cpp/transcript-$(date +%Y-%m-%d_%H-%M-%S).txt" | |
| "$HOME/whisper.cpp/build/bin/whisper-stream" -m "$HOME/whisper.cpp/models/ggml-base.en.bin" --step 0 --length 30000 -vth 0.6 2>&1 | tee "$TRANSCRIPT_FILE" | |
| The transcript is saved beside whisper.cpp as transcript-YYYY-MM-DD_HH-MM-SS.txt. | |
| EOF | |
| } | |
| print_linux_steps() { | |
| cat <<'EOF' | |
| Installation complete for Linux. | |
| 1. Start the transcription command below, then open pavucontrol. | |
| 2. In pavucontrol > Recording, change whisper-stream's source to the monitor of | |
| your active output device (for example, "Built-in Audio Stereo Monitor"). | |
| 3. Keep Zoom's microphone set to your real microphone; only whisper-stream uses | |
| the monitor source. Audio remains audible through your normal output. | |
| Start transcription: | |
| TRANSCRIPT_FILE="$HOME/whisper.cpp/transcript-$(date +%Y-%m-%d_%H-%M-%S).txt" | |
| "$HOME/whisper.cpp/build/bin/whisper-stream" -m "$HOME/whisper.cpp/models/ggml-base.en.bin" --step 0 --length 30000 -vth 0.6 2>&1 | tee "$TRANSCRIPT_FILE" | |
| The transcript is saved beside whisper.cpp as transcript-YYYY-MM-DD_HH-MM-SS.txt. | |
| EOF | |
| } | |
| main() { | |
| case "${1:-}" in | |
| -h|--help) usage; return 0 ;; | |
| --verify-only) | |
| [[ -x "$STREAM_BINARY" && -f "$MODEL_PATH" ]] || die 'Installation verification failed.' | |
| printf 'Installation verification passed.\n' | |
| return 0 | |
| ;; | |
| '') ;; | |
| *) usage >&2; die "Unknown option: $1" ;; | |
| esac | |
| case "$(uname -s)" in | |
| Darwin) | |
| install_macos_dependencies | |
| prepare_whisper_checkout | |
| build_and_verify | |
| print_macos_steps | |
| ;; | |
| Linux) | |
| install_linux_dependencies | |
| prepare_whisper_checkout | |
| build_and_verify | |
| print_linux_steps | |
| ;; | |
| *) die "Unsupported operating system: $(uname -s)" ;; | |
| esac | |
| } | |
| main "$@" |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment