Add managed Windows remote audio stack

This commit is contained in:
2026-09-20 17:26:36 +01:00
commit baa48c10b6
9 changed files with 650 additions and 0 deletions
+134
View File
@@ -0,0 +1,134 @@
# Dependency setup is kept separate from the process supervisor. Nothing is
# downloaded or installed when all configured executables already exist.
$script:depsProjectRoot = $PSScriptRoot
$script:depsManifest = Get-Content -LiteralPath (Join-Path $PSScriptRoot 'dependencies.json') -Raw | ConvertFrom-Json
$script:depsDownloadDir = Join-Path $PSScriptRoot '.runtime\downloads'
$script:depsInstallDir = Join-Path $PSScriptRoot '.deps'
function Test-JackPaths($config) {
return (Test-Path -LiteralPath $config.jackd -PathType Leaf) -and
(Test-Path -LiteralPath (Join-Path $config.jackTools 'jack_lsp.exe') -PathType Leaf) -and
(Test-Path -LiteralPath (Join-Path $config.jackTools 'jack_connect.exe') -PathType Leaf)
}
function Test-JackRouter {
return (Test-Path -LiteralPath 'HKLM:\SOFTWARE\ASIO\JackRouter') -and
(Test-Path -LiteralPath 'HKLM:\SOFTWARE\WOW6432Node\ASIO\JackRouter')
}
function Use-DefaultJackPaths($config) {
$candidate = 'C:\Program Files\JACK2'
if (-not (Test-Path -LiteralPath (Join-Path $candidate 'jackd.exe'))) {
$location = (Get-ItemProperty -Path 'HKLM:\SOFTWARE\JACK' -Name Location -ErrorAction SilentlyContinue).Location
if ($location) {
$candidate = if ($location -like '*.exe') { Split-Path -Path $location -Parent } else { $location }
}
}
$config.jackd = Join-Path $candidate 'jackd.exe'
$config.jackTools = Join-Path $candidate 'tools'
}
function Get-PinnedDownload($asset) {
New-Item -ItemType Directory -Force -Path $script:depsDownloadDir | Out-Null
$destination = Join-Path $script:depsDownloadDir $asset.file
if (Test-Path -LiteralPath $destination) {
$existingHash = (Get-FileHash -LiteralPath $destination -Algorithm SHA256).Hash
if ($existingHash -ieq $asset.sha256) { return $destination }
Write-Warning "Cached $($asset.file) failed SHA-256 verification; downloading it again."
Remove-Item -LiteralPath $destination
}
$partial = "$destination.partial"
Remove-Item -LiteralPath $partial -ErrorAction SilentlyContinue
Write-Host "Downloading $($asset.file) from $($asset.url)"
try {
Invoke-WebRequest -Uri $asset.url -OutFile $partial -UseBasicParsing
$actual = (Get-FileHash -LiteralPath $partial -Algorithm SHA256).Hash
if ($actual -ine $asset.sha256) { throw "SHA-256 mismatch for $($asset.file): got $actual" }
Move-Item -LiteralPath $partial -Destination $destination
return $destination
} finally {
Remove-Item -LiteralPath $partial -ErrorAction SilentlyContinue
}
}
function Assert-DependencyPath([string]$path) {
$base = [System.IO.Path]::GetFullPath($script:depsInstallDir).TrimEnd('\') + '\'
$target = [System.IO.Path]::GetFullPath($path)
if (-not $target.StartsWith($base, [StringComparison]::OrdinalIgnoreCase)) {
throw "Refusing to modify directory outside .deps: $target"
}
return $target
}
function Remove-DependencyStaging([string]$path) {
$target = Assert-DependencyPath $path
if (Test-Path -LiteralPath $target) { Remove-Item -LiteralPath $target -Recurse -Force }
}
function Get-ManagedJackTrip {
$folder = Join-Path $script:depsInstallDir "JackTrip-v$($script:depsManifest.jacktrip.version)-Windows-x64"
if (-not (Test-Path -LiteralPath $folder)) { return $null }
$exe = Get-ChildItem -LiteralPath $folder -Recurse -File -Filter 'jacktrip.exe' | Select-Object -First 1
if ($exe) { return $exe.FullName }
return $null
}
function Install-ManagedJackTrip {
$asset = $script:depsManifest.jacktrip
$archive = Get-PinnedDownload $asset
New-Item -ItemType Directory -Force -Path $script:depsInstallDir | Out-Null
$folder = Assert-DependencyPath (Join-Path $script:depsInstallDir "JackTrip-v$($asset.version)-Windows-x64")
$staging = Assert-DependencyPath (Join-Path $script:depsInstallDir ('.staging-' + [guid]::NewGuid().ToString('n')))
try {
Expand-Archive -LiteralPath $archive -DestinationPath $staging
$exe = Get-ChildItem -LiteralPath $staging -Recurse -File -Filter 'jacktrip.exe' | Select-Object -First 1
if (-not $exe) { throw 'Verified JackTrip archive did not contain jacktrip.exe' }
if (Test-Path -LiteralPath $folder) { Remove-DependencyStaging $folder }
Move-Item -LiteralPath $staging -Destination $folder
} finally {
Remove-DependencyStaging $staging
}
return Get-ManagedJackTrip
}
function Resolve-AudioDependencies($config, [bool]$allowDownload, [bool]$interactive) {
if (-not (Test-Path -LiteralPath $config.bridge -PathType Leaf)) {
$defaultBridge = 'C:\Program Files (x86)\VB\ASIOBridge\VBCABLE_AsioBridge.exe'
if (Test-Path -LiteralPath $defaultBridge -PathType Leaf) { $config.bridge = $defaultBridge }
}
if (-not (Test-Path -LiteralPath $config.bridge -PathType Leaf)) {
Write-Warning 'Hi-Fi Cable / ASIO Bridge is missing. Download and install it as administrator, then reboot Windows.'
if ($interactive) {
$answer = Read-Host 'Open the official VB-Audio download page now? [y/N]'
if ($answer -match '^(y|yes)$') { Start-Process $script:depsManifest.hifiPage }
}
throw "Hi-Fi Cable installation required: $($script:depsManifest.hifiPage)"
}
if (-not (Test-JackPaths $config)) { Use-DefaultJackPaths $config }
if (-not (Test-JackPaths $config) -or -not (Test-JackRouter)) {
if (-not $allowDownload) { throw 'JACK or JACK Router is missing. Run .\audio.ps1 setup in an interactive PowerShell window.' }
$installer = Get-PinnedDownload $script:depsManifest.jack
if ($interactive) {
$answer = Read-Host "JACK or JACK Router is missing. Run its installer ($installer) now? [y/N]"
if ($answer -match '^(y|yes)$') {
Start-Process -FilePath $installer -Verb RunAs -Wait
Use-DefaultJackPaths $config
}
}
if (-not (Test-JackPaths $config) -or -not (Test-JackRouter)) {
throw "Install JACK with Full installation (with JACK-Router) from $installer, then run setup again."
}
}
if (-not (Test-Path -LiteralPath $config.jacktrip -PathType Leaf)) {
$managed = Get-ManagedJackTrip
if (-not $managed) {
if (-not $allowDownload) { throw 'JackTrip is missing. Run .\audio.ps1 setup first.' }
$managed = Install-ManagedJackTrip
}
$config.jacktrip = $managed
}
if (-not (Test-Path -LiteralPath $config.jacktrip -PathType Leaf)) { throw 'JackTrip is still missing after setup.' }
return $config
}