Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
42 commits
Select commit Hold shift + click to select a range
e462272
chore: bump RustTtsWrapper.Bindings to 0.3.17
willwade Aug 16, 2026
4066481
fix(adapter): handle progressive on_audio chunks and legacy model IDs
willwade Aug 16, 2026
3a891db
feat(catalog): refresh to sherpa-onnx registry v2026-08-10 (1399 entr…
willwade Aug 16, 2026
431b7dc
feat(ui): legacy model-ID migration, fp16 filter, licence metadata, p…
willwade Aug 16, 2026
4148778
docs(tests): move examples off legacy kokoro-en-en-19 ID
willwade Aug 16, 2026
9e259e2
build: copy rust_tts_wrapper.dll from the NuGet cache in local builds
willwade Aug 16, 2026
e572f01
fix(build): make the local build work on space-containing paths
willwade Aug 16, 2026
75ed1ba
test(scripts): resolve promoted voice names and ASCII-normalize
willwade Aug 16, 2026
40dfd79
chore: bump RustTtsWrapper.Bindings to 0.3.18
willwade Aug 16, 2026
0244c1a
feat(ui): quality tiers and derived gender in the Sherpa models tab
willwade Aug 16, 2026
4441de1
feat(ui): thread cloud voice gender through SAPI promotion
willwade Aug 16, 2026
dd9b8b3
feat(tokens): Gender and Quality in promoted SAPI voice tokens
willwade Aug 16, 2026
25db2e0
chore(build): pick newest rust_tts_wrapper.dll from the NuGet cache
willwade Aug 16, 2026
bcc7b15
feat(ui): shared chunky accessible styles and 3-tab localization strings
willwade Aug 16, 2026
a32bade
feat(ui): shared TTS credential builder, audio preview and per-model …
willwade Aug 16, 2026
3fdefba
feat(ui): 3-tab accessible flow — Engines, Credentials, Voices + Adva…
willwade Aug 16, 2026
a30f122
fix(ui): use unitless BoxShadow syntax Avalonia can parse
willwade Aug 17, 2026
05ae448
fix(ui): clamp main window to screen work-area for small displays
willwade Aug 17, 2026
9319795
fix(ui): centre button text and shrink buttons to 36x80dp
willwade Aug 17, 2026
7633fa2
fix(ui): clear tab sub-text from the active-tab underline
willwade Aug 17, 2026
b0e90d3
fix(ui): stop engine/voice lists overlapping the action bars
willwade Aug 17, 2026
d508e17
feat(ui): auto-load voices whenever the Voices tab is shown
willwade Aug 17, 2026
4e46c5c
feat(ui): engines search matches languages, names and ISO codes
willwade Aug 17, 2026
710c72a
fix(ui): drop in-app header, let the window chrome carry the title
willwade Aug 17, 2026
b7d19f0
chore(deps): bump RustTtsWrapper.Bindings to 0.3.20
willwade Aug 18, 2026
966c011
feat(catalog): sync sherpa-onnx registry v2026-08-18 (1760 entries)
willwade Aug 18, 2026
572ed87
feat(catalog): migrate legacy model IDs through the 2026-08-18 renames
willwade Aug 18, 2026
08bfa3d
feat(ui): zipvoice-friendly catalog details and vocoder auto-fetch
willwade Aug 18, 2026
8018b28
fix(ui): disambiguate voices that share a display name
willwade Aug 18, 2026
e1d31a1
fix(ui): align onboarding walkthrough with the 3-tab flow
willwade Aug 18, 2026
2cca4fd
feat(aliases): real-language SAPI tokens with en-US and Arabic alias …
willwade Aug 18, 2026
687b42b
fix(installer): version the MSI so upgrades replace installed files
willwade Aug 18, 2026
33abc51
fix(installer): remove the old product before installing the new one
willwade Aug 18, 2026
82436b4
fix(download): stop scans racing model downloads into file-lock failures
willwade Aug 18, 2026
448406d
feat(ui): accessibility and UX critique pass
willwade Aug 18, 2026
931a035
fix(ui): freeze after install — promote/preview ran locked work on th…
willwade Aug 18, 2026
8148733
feat(installer): setup closes a running app instead of failing the up…
willwade Aug 18, 2026
f92f5a9
chore(payload): stop shipping the superseded SherpaOnnxConfig.exe
willwade Aug 18, 2026
37c352f
chore(payload): drop the Azure Speech SDK runtime chain
willwade Aug 18, 2026
0610326
refactor(adapter): strip the dead Azure Speech SDK code path
willwade Aug 18, 2026
6ff4f28
feat(i18n): translate the Phase 4 UI strings into all 36 locales
willwade Aug 19, 2026
0362762
feat(ui): 'Show installed voices only' filter on the Voices tab
willwade Aug 19, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 18 additions & 18 deletions .github/workflows/msbuild.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,9 @@ jobs:
- name: Build SherpaOnnxConfig (self-contained, x64)
run: dotnet publish SherpaOnnxConfig\SherpaOnnxConfig.csproj -c Release -r win-x64 --self-contained -p:PublishSingleFile=true -o ${{github.workspace}}\sherpa-config

- name: Copy merged_models.json (single-file publish drops it)
- name: Copy models.json (single-file publish drops it)
shell: pwsh
run: Copy-Item SherpaOnnxConfig\merged_models.json sherpa-config\ -Force
run: Copy-Item SherpaOnnxConfig\models.json sherpa-config\ -Force

- name: Verify Sherpa integration checks
shell: pwsh
Expand All @@ -36,7 +36,7 @@ jobs:
name: sherpa-onnx-config
path: |
sherpa-config/SherpaOnnxConfig.exe
sherpa-config/merged_models.json
sherpa-config/models.json

build-voice-garden-ui:
name: Build VoiceGarden.UI
Expand Down Expand Up @@ -84,19 +84,9 @@ jobs:
- name: Add MSBuild to PATH
uses: microsoft/setup-msbuild@v2

- name: Build AzureSpeechSDKShim (x64)
if: matrix.platform == 'x64'
working-directory: ${{github.workspace}}\AzureSpeechSDKShim
run: |
nuget restore AzureSpeechSDKShim.sln
msbuild /m /p:Configuration=${{env.BUILD_CONFIGURATION}} /p:Platform=x64 /p:OutDir=${{github.workspace}}\out\ AzureSpeechSDKShim.sln

- name: Build AzureSpeechSDKShim (x86)
if: matrix.platform == 'x86'
working-directory: ${{github.workspace}}\AzureSpeechSDKShim
run: |
nuget restore AzureSpeechSDKShim.sln
msbuild /m /p:Configuration=${{env.BUILD_CONFIGURATION}} /p:Platform=x86 /p:OutDir=${{github.workspace}}\out\ AzureSpeechSDKShim.sln
# AzureSpeechSDKShim is no longer built: the local Narrator voice feature
# it supported was dropped (TOS concerns) and the SDK runtime chain is
# excluded from the payload at compose time.

- name: Build Arm64XForwarder (ARM64 only)
if: matrix.platform == 'ARM64'
Expand All @@ -119,8 +109,7 @@ jobs:
name: sherpa-onnx-config
path: ${{github.workspace}}\out\

- name: Keep only merged_models.json in ARM64 utilities
if: matrix.platform == 'ARM64'
- name: Keep only models.json in utilities (drop the superseded SherpaOnnxConfig.exe)
shell: pwsh
run: Remove-Item out\SherpaOnnxConfig.exe -ErrorAction SilentlyContinue

Expand Down Expand Up @@ -228,6 +217,12 @@ jobs:
# VoiceGarden.UI.exe (the main configuration app)
Copy-Item artifacts\voice-garden-ui\VoiceGarden.UI.exe payload\ -Force

# The Azure Speech SDK runtime chain is not shipped (local Narrator
# voices dropped — TOS; the adapter never loads the SDK at runtime).
Get-ChildItem payload -Recurse -File |
Where-Object { $_.Name -like 'Microsoft.CognitiveServices.Speech.*.dll' -or $_.Name -in 'SpeechSDKShim.dll','SpeechSDKPatcher.exe' } |
Remove-Item -Force

# Rust TTS wrapper native DLL (from NuGet package)
dotnet restore VoiceGarden.UI\VoiceGarden.UI.csproj
$rustDll = Get-ChildItem "$env:USERPROFILE\.nuget\packages\rustttswrapper.bindings" -Recurse -Filter "rust_tts_wrapper.dll" | Select-Object -First 1
Expand Down Expand Up @@ -294,6 +289,11 @@ jobs:
# VoiceGarden.UI.exe goes to payload root
cp voice-garden-ui/VoiceGarden.UI.exe . 2>/dev/null || true

# The Azure Speech SDK runtime chain is not shipped (local Narrator
# voices dropped — TOS; the adapter never loads the SDK at runtime).
rm -f x64/Microsoft.CognitiveServices.Speech.*.dll x64/SpeechSDKShim.dll x64/SpeechSDKPatcher.exe
rm -f x86/Microsoft.CognitiveServices.Speech.*.dll x86/SpeechSDKShim.dll x86/SpeechSDKPatcher.exe

# MSI + setup.exe
cp setup-msi/VoiceGardenSAPIAdapter.msi . 2>/dev/null || true
cp setup-exe/setup.exe . 2>/dev/null || true
Expand Down
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ Fully offline neural TTS — no internet, no API keys, no cloud dependency.
### Downloading models
Models are stored in `%LOCALAPPDATA%\VoiceGardenSAPIAdapter\models\`. Download via:
- **UI:** SherpaOnnx tab → select models → Download Selected (built-in extraction, no 7-Zip needed)
- **CLI:** `VoiceGarden.UI.exe models download kokoro-en-en-19`
- **CLI:** `VoiceGarden.UI.exe models download kokoro-en-v0_19`

### Grid 3 compatibility
Grid 3 (`System.Speech`) only selects voices from HKLM registry tokens. Use **Install to SAPI** to promote voices to HKLM. Word boundary events work with Grid3 (tested, no crash).
Expand Down Expand Up @@ -148,7 +148,7 @@ Each cloud engine needs its API key set in the Engine Config tab. Search voices
```powershell
Add-Type -AssemblyName "System.Speech"
$s = New-Object System.Speech.Synthesis.SpeechSynthesizer
$s.SelectVoice("kokoro-en-en-19") # or any voice name
$s.SelectVoice("kokoro-en-v0_19") # or any voice name
$s.Speak("Hello, this is a test.")
```

Expand Down
9 changes: 8 additions & 1 deletion Setup/Package.wxs
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,17 @@
<MajorUpgrade
AllowSameVersionUpgrades="yes"
IgnoreRemoveFailure="yes"
Schedule="afterInstallFinalize"
Schedule="afterInstallValidate"
DowngradeErrorMessage="A newer version of [ProductName] is already installed." />
<MediaTemplate EmbedCab="yes" />

<!--
Running apps are closed by the SetupLauncher bootstrapper before the
MSI starts (ask-first, terminate on refusal) — their locked files
otherwise fail the upgrade. Kept out of util:CloseApplication because
the PanelSw WiX build cannot load official wixext packages.
-->

<StandardDirectory Id="ProgramFiles6432Folder">
<Directory Id="INSTALLFOLDER" Name="$(var.InstallFolderName)" />
</StandardDirectory>
Expand Down
6 changes: 6 additions & 0 deletions Setup/Setup.wixproj
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,10 @@
<ProjectUrl Condition="'$(ProjectUrl)'==''">https://github.com/gexgd0419/VoiceGardenSAPIAdapter</ProjectUrl>
<DefineConstants>ProductVersion=$(ProductVersion);PayloadDir=$(PayloadDir);ProductName=$(ProductName);Manufacturer=$(Manufacturer);InstallFolderName=$(InstallFolderName);InstallerShortcutName=$(InstallerShortcutName);ProjectUrl=$(ProjectUrl)</DefineConstants>
</PropertyGroup>

<ItemGroup>
<!-- Close-application prompting is handled by the SetupLauncher
bootstrapper (the PanelSw WiX build rejects official wixext
packages, so util:CloseApplication is not usable here). -->
</ItemGroup>
</Project>
75 changes: 75 additions & 0 deletions SetupLauncher/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,10 @@ static int Main(string[] args)
return 3;
}

// Uninstall removes the apps' files too — they must not run.
if (!EnsureAppsClosed(quiet))
return 1602; // ERROR_INSTALL_USEREXIT

msiexecArgs = $"/x {productCode}";
if (quiet)
msiexecArgs += " /qn";
Expand All @@ -49,6 +53,11 @@ static int Main(string[] args)
}
else
{
// Upgrades replace the apps' files — a running instance keeps
// them locked and the MSI fails or leaves a half-mixed install.
if (!EnsureAppsClosed(quiet))
return 1602; // ERROR_INSTALL_USEREXIT

msiexecArgs = $"/i \"{msiPath}\"";
if (quiet)
msiexecArgs += " /qn";
Expand Down Expand Up @@ -107,6 +116,72 @@ static int Main(string[] args)
static bool HasArg(string[] args, string needle) =>
args.Any(a => string.Equals(a, needle, StringComparison.OrdinalIgnoreCase));

/// <summary>Apps whose files the MSI replaces — they must be closed first.</summary>
static readonly string[] AppProcessNames = { "VoiceGarden.UI", "SherpaOnnxConfig" };

/// <summary>
/// Make sure none of the shipped apps are running. Asks the user first
/// (with the option to close them automatically); silent runs close
/// everything without asking. Returns false when the user declines.
/// </summary>
static bool EnsureAppsClosed(bool quiet)
{
if (!EnumerateAppProcesses().Any())
return true; // nothing was running

if (quiet)
{
CloseAppProcesses();
return true;
}

var choice = MessageBox.Show(
"VoiceGarden is currently running and must be closed before setup can continue.\n\n" +
"Click OK to close it now and continue, or Cancel to stop setup.",
Branding.SetupCaption,
MessageBoxButtons.OKCancel,
MessageBoxIcon.Information,
MessageBoxDefaultButton.Button1);
if (choice != DialogResult.OK)
return false;

CloseAppProcesses();

// If something still refuses to die, say so rather than failing
// the MSI with a cryptic file-in-use error.
if (EnumerateAppProcesses().Any())
{
Notify("Could not close VoiceGarden (another user may be running it, or it may be busy). " +
"Close it manually and run setup again.",
Branding.SetupCaption, quiet: false, error: true);
return false;
}
return true;
}

static IEnumerable<Process> EnumerateAppProcesses() =>
AppProcessNames.SelectMany(Process.GetProcessesByName).Distinct();

static void CloseAppProcesses()
{
foreach (var proc in EnumerateAppProcesses().ToList())
{
try
{
// Ask nicely first (WM_CLOSE); the single-instance app exits on it.
proc.CloseMainWindow();
if (!proc.WaitForExit(3000))
proc.Kill();
}
catch
{
try { proc.Kill(); } catch { }
}
}
// Give the OS a moment to release file handles.
Thread.Sleep(1500);
}

static int RunMsiexec(string arguments)
{
var psi = new ProcessStartInfo
Expand Down
Loading
Loading