185 lines
7.2 KiB
PowerShell
185 lines
7.2 KiB
PowerShell
<#
|
|
.SYNOPSIS
|
|
Generates PGO (profile-guided optimization) profile data for an instrumented FreeDV build.
|
|
|
|
.DESCRIPTION
|
|
This script feeds a known test WAV file through FreeDV's RADE TX pipeline while recording the
|
|
resulting analog audio via SoX from the "computer to radio" device. The recording is then fed
|
|
straight back into FreeDV's RX pipeline from disk (bypassing the sound card) so that both TX and
|
|
RX code paths are exercised. FreeDV is expected to have been built with PGO instrumentation
|
|
enabled, so each run will emit a *.profraw file that can later be merged and used to guide a
|
|
final optimized build.
|
|
|
|
This script must be run from the folder containing freedv.exe, and expects freedv-pgo.conf.tmpl
|
|
to be present in that same folder.
|
|
|
|
.INPUTS
|
|
None. You can't pipe objects to this script.
|
|
|
|
.OUTPUTS
|
|
FreeDV's console output is written to the host. The script exits with the exit code of the RX pass.
|
|
|
|
.EXAMPLE
|
|
PS> .\GeneratePGOProfiles.ps1 -RadioToComputerDevice "CABLE Output (VB-Audio Virtual Cable)" -ComputerToRadioDevice "Speakers (VB-Audio Virtual Cable)" -ComputerToSpeakerDevice "Line 1 (Virtual Audio Cable)" -MicrophoneToComputerDevice "Line 1 (Virtual Audio Cable)"
|
|
|
|
#>
|
|
|
|
param (
|
|
[Parameter(Mandatory = $true)]
|
|
[ValidateNotNullOrEmpty()]
|
|
[string]
|
|
# The sound device to receive RX audio from.
|
|
$RadioToComputerDevice,
|
|
|
|
[Parameter(Mandatory = $true)]
|
|
[ValidateNotNullOrEmpty()]
|
|
[string]
|
|
# The sound device to emit decoded audio to.
|
|
$ComputerToSpeakerDevice,
|
|
|
|
[Parameter(Mandatory = $true)]
|
|
[ValidateNotNullOrEmpty()]
|
|
[string]
|
|
# The sound device to receive analog audio from.
|
|
$MicrophoneToComputerDevice,
|
|
|
|
[Parameter(Mandatory = $true)]
|
|
[ValidateNotNullOrEmpty()]
|
|
[string]
|
|
# The sound device to emit TX audio to. This is also recorded from to obtain the RX input.
|
|
$ComputerToRadioDevice)
|
|
|
|
$current_loc = Get-Location
|
|
|
|
# Best-effort: wait for the configured audio endpoints to be enumerable before
|
|
# starting FreeDV, so a virtual cable that has briefly dropped out of the
|
|
# MMDevice list doesn't make the UT abort with a fatal "device cannot be found"
|
|
# message box. The CI workflow does a stricter, failing check up front; this is
|
|
# just a short guard against a mid-script drop between the TX and RX passes.
|
|
function Wait-ForAudioDevices {
|
|
param (
|
|
[string[]] $Names,
|
|
[int] $TimeoutSeconds = 90
|
|
)
|
|
|
|
if (-not (Get-Module -ListAvailable -Name AudioDeviceCmdlets)) { return }
|
|
Import-Module AudioDeviceCmdlets -ErrorAction SilentlyContinue
|
|
|
|
$wanted = @($Names | Where-Object { $_ } | Select-Object -Unique)
|
|
$deadline = (Get-Date).AddSeconds($TimeoutSeconds)
|
|
while ($true) {
|
|
try { $have = @((Get-AudioDevice -List).Name) } catch { $have = @() }
|
|
$missing = @($wanted | Where-Object { $have -notcontains $_ })
|
|
if ($missing.Count -eq 0) { return }
|
|
if ((Get-Date) -ge $deadline) {
|
|
Write-Host "WARNING: audio device(s) still missing after ${TimeoutSeconds}s: $($missing -join ', ')"
|
|
return
|
|
}
|
|
Start-Sleep -Seconds 3
|
|
}
|
|
}
|
|
|
|
$allDevices = @($RadioToComputerDevice, $ComputerToRadioDevice, $MicrophoneToComputerDevice, $ComputerToSpeakerDevice)
|
|
|
|
# Clone the RADE test corpus if not already present, then resample the TX test file to 48 kHz
|
|
# to reduce CPU usage during the run.
|
|
if (-not (Test-Path "$current_loc\rade_src")) {
|
|
& git clone -b main https://github.com/drowe67/radae.git "$current_loc\rade_src"
|
|
if ($LASTEXITCODE -ne 0) {
|
|
throw "Failed to clone RADE test corpus"
|
|
}
|
|
}
|
|
|
|
& sox.exe "$current_loc\rade_src\wav\all.wav" -r 48000 "$current_loc\tx_in.wav"
|
|
if ($LASTEXITCODE -ne 0) {
|
|
throw "Failed to resample TX test file"
|
|
}
|
|
|
|
# Generate new conf
|
|
$conf_tmpl = Get-Content "$current_loc\freedv-pgo.conf.tmpl"
|
|
$conf_tmpl = $conf_tmpl.Replace("@FREEDV_RADIO_TO_COMPUTER_DEVICE@", $RadioToComputerDevice)
|
|
$conf_tmpl = $conf_tmpl.Replace("@FREEDV_COMPUTER_TO_RADIO_DEVICE@", $ComputerToRadioDevice)
|
|
$conf_tmpl = $conf_tmpl.Replace("@FREEDV_MICROPHONE_TO_COMPUTER_DEVICE@", $MicrophoneToComputerDevice)
|
|
$conf_tmpl = $conf_tmpl.Replace("@FREEDV_COMPUTER_TO_SPEAKER_DEVICE@", $ComputerToSpeakerDevice)
|
|
$conf_file = "$current_loc\freedv-pgo.conf"
|
|
$conf_tmpl | Set-Content -Path $conf_file
|
|
$quoted_conf_filename = "`"" + $conf_file + "`""
|
|
|
|
# Start recording the TX output via SoX so that it can be fed back in as the RX input file.
|
|
$soxPsi = New-Object System.Diagnostics.ProcessStartInfo
|
|
$soxPsi.CreateNoWindow = $true
|
|
$soxPsi.UseShellExecute = $false
|
|
$soxPsi.RedirectStandardError = $false
|
|
$soxPsi.RedirectStandardOutput = $false
|
|
$soxPsi.FileName = "sox.exe"
|
|
$soxPsi.WorkingDirectory = $current_loc
|
|
$quoted_device = "`"" + $RadioToComputerDevice + "`""
|
|
$soxPsi.Arguments = @("-t waveaudio $quoted_device -c 1 -r 48000 -t wav `"$current_loc\test.wav`"")
|
|
|
|
$soxProcess = New-Object System.Diagnostics.Process
|
|
$soxProcess.StartInfo = $soxPsi
|
|
[void]$soxProcess.Start()
|
|
|
|
# Start FreeDV in test mode to record TX
|
|
Wait-ForAudioDevices -Names $allDevices
|
|
|
|
$psi = New-Object System.Diagnostics.ProcessStartInfo
|
|
$psi.CreateNoWindow = $true
|
|
$psi.UseShellExecute = $false
|
|
$psi.RedirectStandardError = $true
|
|
$psi.RedirectStandardOutput = $true
|
|
$psi.FileName = "$current_loc\freedv.exe"
|
|
$psi.WorkingDirectory = $current_loc
|
|
$psi.Arguments = @("/f $quoted_conf_filename /ut tx /utmode RADEV1 /txfile `"$current_loc\tx_in.wav`" /txfeaturefile `"$current_loc\txfeatures.f32`"")
|
|
|
|
$process = New-Object System.Diagnostics.Process
|
|
$process.StartInfo = $psi
|
|
[void]$process.Start()
|
|
$process.PriorityClass = [System.Diagnostics.ProcessPriorityClass]::AboveNormal
|
|
|
|
# Read output from the TX run
|
|
$err_output = $process.StandardError.ReadToEnd()
|
|
$output = $process.StandardOutput.ReadToEnd()
|
|
$process.WaitForExit()
|
|
|
|
Write-Host "$output"
|
|
Write-Host "$err_output"
|
|
|
|
# Stop recording, play back in RX mode
|
|
try {
|
|
$soxProcess.Kill()
|
|
} catch {
|
|
# Ignore failure as SoX may have already exited on its own
|
|
}
|
|
$soxProcess.WaitForExit()
|
|
|
|
# Killing SoX above can briefly disturb the shared audio engine; make sure the
|
|
# endpoints are back before the RX pass starts.
|
|
Wait-ForAudioDevices -Names $allDevices
|
|
|
|
$psi.Arguments = @("/f $quoted_conf_filename /ut rx /utmode RADEV1 /rxfile `"$current_loc\test.wav`" /rxfeaturefile `"$current_loc\rxfeatures.f32`"")
|
|
|
|
$conf_tmpl = Get-Content "$current_loc\freedv-pgo.conf.tmpl"
|
|
$conf_tmpl = $conf_tmpl.Replace("@FREEDV_RADIO_TO_COMPUTER_DEVICE@", $RadioToComputerDevice)
|
|
$conf_tmpl = $conf_tmpl.Replace("@FREEDV_COMPUTER_TO_RADIO_DEVICE@", $ComputerToRadioDevice)
|
|
$conf_tmpl = $conf_tmpl.Replace("@FREEDV_MICROPHONE_TO_COMPUTER_DEVICE@", $MicrophoneToComputerDevice)
|
|
$conf_tmpl = $conf_tmpl.Replace("@FREEDV_COMPUTER_TO_SPEAKER_DEVICE@", $ComputerToSpeakerDevice)
|
|
$conf_file = "$current_loc\freedv-pgo.conf"
|
|
$conf_tmpl | Set-Content -Path $conf_file
|
|
|
|
$process = New-Object System.Diagnostics.Process
|
|
$process.StartInfo = $psi
|
|
[void]$process.Start()
|
|
$process.PriorityClass = [System.Diagnostics.ProcessPriorityClass]::AboveNormal
|
|
|
|
# Read output from the RX run
|
|
$err_output = $process.StandardError.ReadToEnd()
|
|
$output = $process.StandardOutput.ReadToEnd()
|
|
$process.WaitForExit()
|
|
$freedv_exit_code = $process.ExitCode
|
|
|
|
Write-Host "$output"
|
|
Write-Host "$err_output"
|
|
|
|
exit $freedv_exit_code
|