Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
21 changes: 20 additions & 1 deletion src/ui/Features/Video/TextToSpeech/Engines/AudioCppRuntime.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
using Nikse.SubtitleEdit.Logic.Config;
using Nikse.SubtitleEdit.Logic.Config;
using System;
using System.IO;

Expand Down Expand Up @@ -30,6 +30,25 @@ public static string GetSetEngineFolder()
public static string GetServerExecutable() =>
Path.Combine(GetSetEngineFolder(), OperatingSystem.IsWindows() ? "audiocpp_server.exe" : "audiocpp_server");

/// <summary>
/// Identity of the server binary on disk (size + last write), or empty when it is missing.
/// The engines record it when they launch a server and restart when it changes: an engine
/// update extracts a new binary while the old process keeps running, and reusing that
/// process means the new build (and any family it adds) is never actually used.
/// </summary>
public static string GetServerExecutableStamp()
{
try
{
var fi = new FileInfo(GetServerExecutable());
return fi.Exists ? $"{fi.Length}:{fi.LastWriteTimeUtc.Ticks}" : string.Empty;
}
catch
{
return string.Empty;
}
}

/// <summary>
/// ggml backend the installed archive was built for, stored at install time. The setting
/// keeps its historical IndexTts25-prefixed name — it predates the runtime being shared —
Expand Down
13 changes: 10 additions & 3 deletions src/ui/Features/Video/TextToSpeech/Engines/FishTtsAudioCpp.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
using Nikse.SubtitleEdit.Features.Video.TextToSpeech.ModelLicense;
using Nikse.SubtitleEdit.Features.Video.TextToSpeech.ModelLicense;
using Nikse.SubtitleEdit.Features.Video.TextToSpeech.Voices;
using Nikse.SubtitleEdit.Logic.Config;
using Nikse.SubtitleEdit.Logic.Download;
Expand Down Expand Up @@ -138,6 +138,7 @@ public static string GetModelFileName(string? modelKey) =>
// Only the model and the backend are baked into the running server — voice is per request.
private static string? _serverModelKey;
private static string? _serverBackend;
private static string? _serverExeStamp;
private static bool _processExitHooked;
private static readonly StringBuilder _serverLog = new();

Expand Down Expand Up @@ -512,10 +513,12 @@ public async Task<TtsResult> Speak(
private static async Task EnsureServerRunningAsync(string modelKey, CancellationToken ct)
{
var backend = AudioCppRuntime.GetBackend();
var exeStamp = AudioCppRuntime.GetServerExecutableStamp();

if (_serverProcess is { HasExited: false } && _serverPort != 0
&& string.Equals(_serverModelKey, modelKey, StringComparison.OrdinalIgnoreCase)
&& string.Equals(_serverBackend, backend, StringComparison.OrdinalIgnoreCase))
&& string.Equals(_serverBackend, backend, StringComparison.OrdinalIgnoreCase)
&& string.Equals(_serverExeStamp, exeStamp, StringComparison.Ordinal))
{
return;
}
Expand All @@ -525,7 +528,8 @@ private static async Task EnsureServerRunningAsync(string modelKey, Cancellation
{
if (_serverProcess is { HasExited: false } && _serverPort != 0
&& string.Equals(_serverModelKey, modelKey, StringComparison.OrdinalIgnoreCase)
&& string.Equals(_serverBackend, backend, StringComparison.OrdinalIgnoreCase))
&& string.Equals(_serverBackend, backend, StringComparison.OrdinalIgnoreCase)
&& string.Equals(_serverExeStamp, exeStamp, StringComparison.Ordinal))
{
return;
}
Expand Down Expand Up @@ -596,6 +600,7 @@ private static async Task EnsureServerRunningAsync(string modelKey, Cancellation
_serverPort = port;
_serverModelKey = modelKey;
_serverBackend = backend;
_serverExeStamp = AudioCppRuntime.GetServerExecutableStamp();
HookProcessExitOnce();

// The config uses lazy_load, so /health answers within a second or two — the 5.9 GB
Expand All @@ -616,6 +621,7 @@ private static async Task EnsureServerRunningAsync(string modelKey, Cancellation
_serverLaunchCommand = null;
_serverModelKey = null;
_serverBackend = null;
_serverExeStamp = null;
throw new InvalidOperationException(
$"audiocpp_server exited during startup (code {exitCode}). "
+ AudioCppRuntime.DescribeStartupExit(exitCode, backend)
Expand Down Expand Up @@ -784,6 +790,7 @@ private static void StopServerInternal()
_serverLaunchCommand = null;
_serverModelKey = null;
_serverBackend = null;
_serverExeStamp = null;
if (p == null)
{
return;
Expand Down
13 changes: 10 additions & 3 deletions src/ui/Features/Video/TextToSpeech/Engines/HiggsTtsAudioCpp.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
using Nikse.SubtitleEdit.Features.Video.TextToSpeech.ModelLicense;
using Nikse.SubtitleEdit.Features.Video.TextToSpeech.ModelLicense;
using Nikse.SubtitleEdit.Features.Video.TextToSpeech.Voices;
using Nikse.SubtitleEdit.Logic.Config;
using Nikse.SubtitleEdit.Logic.Download;
Expand Down Expand Up @@ -140,6 +140,7 @@ public static string GetModelFileName(string? modelKey) =>
// Only the model and the backend are baked into the running server — voice is per request.
private static string? _serverModelKey;
private static string? _serverBackend;
private static string? _serverExeStamp;
private static bool _processExitHooked;
private static readonly StringBuilder _serverLog = new();

Expand Down Expand Up @@ -500,10 +501,12 @@ public async Task<TtsResult> Speak(
private static async Task EnsureServerRunningAsync(string modelKey, CancellationToken ct)
{
var backend = AudioCppRuntime.GetBackend();
var exeStamp = AudioCppRuntime.GetServerExecutableStamp();

if (_serverProcess is { HasExited: false } && _serverPort != 0
&& string.Equals(_serverModelKey, modelKey, StringComparison.OrdinalIgnoreCase)
&& string.Equals(_serverBackend, backend, StringComparison.OrdinalIgnoreCase))
&& string.Equals(_serverBackend, backend, StringComparison.OrdinalIgnoreCase)
&& string.Equals(_serverExeStamp, exeStamp, StringComparison.Ordinal))
{
return;
}
Expand All @@ -513,7 +516,8 @@ private static async Task EnsureServerRunningAsync(string modelKey, Cancellation
{
if (_serverProcess is { HasExited: false } && _serverPort != 0
&& string.Equals(_serverModelKey, modelKey, StringComparison.OrdinalIgnoreCase)
&& string.Equals(_serverBackend, backend, StringComparison.OrdinalIgnoreCase))
&& string.Equals(_serverBackend, backend, StringComparison.OrdinalIgnoreCase)
&& string.Equals(_serverExeStamp, exeStamp, StringComparison.Ordinal))
{
return;
}
Expand Down Expand Up @@ -584,6 +588,7 @@ private static async Task EnsureServerRunningAsync(string modelKey, Cancellation
_serverPort = port;
_serverModelKey = modelKey;
_serverBackend = backend;
_serverExeStamp = AudioCppRuntime.GetServerExecutableStamp();
HookProcessExitOnce();

// The config uses lazy_load, so /health answers within a second or two — the 4.7 GB
Expand All @@ -604,6 +609,7 @@ private static async Task EnsureServerRunningAsync(string modelKey, Cancellation
_serverLaunchCommand = null;
_serverModelKey = null;
_serverBackend = null;
_serverExeStamp = null;
throw new InvalidOperationException(
$"audiocpp_server exited during startup (code {exitCode}). "
+ AudioCppRuntime.DescribeStartupExit(exitCode, backend)
Expand Down Expand Up @@ -772,6 +778,7 @@ private static void StopServerInternal()
_serverLaunchCommand = null;
_serverModelKey = null;
_serverBackend = null;
_serverExeStamp = null;
if (p == null)
{
return;
Expand Down
13 changes: 10 additions & 3 deletions src/ui/Features/Video/TextToSpeech/Engines/IndexTts25AudioCpp.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
using Nikse.SubtitleEdit.Features.Video.TextToSpeech.Voices;
using Nikse.SubtitleEdit.Features.Video.TextToSpeech.Voices;
using Nikse.SubtitleEdit.Logic.Config;
using Nikse.SubtitleEdit.Logic.Download;
using Nikse.SubtitleEdit.Logic.Media;
Expand Down Expand Up @@ -116,6 +116,7 @@ public static string GetModelFileName(string? modelKey) =>
// Only the model and the backend are baked into the running server — voice is per request.
private static string? _serverModelKey;
private static string? _serverBackend;
private static string? _serverExeStamp;
private static bool _processExitHooked;
private static readonly StringBuilder _serverLog = new();

Expand Down Expand Up @@ -522,10 +523,12 @@ public async Task<TtsResult> Speak(
private static async Task EnsureServerRunningAsync(string modelKey, CancellationToken ct)
{
var backend = GetBackend();
var exeStamp = AudioCppRuntime.GetServerExecutableStamp();

if (_serverProcess is { HasExited: false } && _serverPort != 0
&& string.Equals(_serverModelKey, modelKey, StringComparison.OrdinalIgnoreCase)
&& string.Equals(_serverBackend, backend, StringComparison.OrdinalIgnoreCase))
&& string.Equals(_serverBackend, backend, StringComparison.OrdinalIgnoreCase)
&& string.Equals(_serverExeStamp, exeStamp, StringComparison.Ordinal))
{
return;
}
Expand All @@ -535,7 +538,8 @@ private static async Task EnsureServerRunningAsync(string modelKey, Cancellation
{
if (_serverProcess is { HasExited: false } && _serverPort != 0
&& string.Equals(_serverModelKey, modelKey, StringComparison.OrdinalIgnoreCase)
&& string.Equals(_serverBackend, backend, StringComparison.OrdinalIgnoreCase))
&& string.Equals(_serverBackend, backend, StringComparison.OrdinalIgnoreCase)
&& string.Equals(_serverExeStamp, exeStamp, StringComparison.Ordinal))
{
return;
}
Expand Down Expand Up @@ -608,6 +612,7 @@ private static async Task EnsureServerRunningAsync(string modelKey, Cancellation
_serverPort = port;
_serverModelKey = modelKey;
_serverBackend = backend;
_serverExeStamp = AudioCppRuntime.GetServerExecutableStamp();
HookProcessExitOnce();

// The config uses lazy_load, so /health answers within a second or two — the 3.3 GB
Expand All @@ -628,6 +633,7 @@ private static async Task EnsureServerRunningAsync(string modelKey, Cancellation
_serverLaunchCommand = null;
_serverModelKey = null;
_serverBackend = null;
_serverExeStamp = null;
throw new InvalidOperationException(
$"audiocpp_server exited during startup (code {exitCode}). "
+ DescribeStartupExit(exitCode, backend)
Expand Down Expand Up @@ -798,6 +804,7 @@ private static void StopServerInternal()
_serverLaunchCommand = null;
_serverModelKey = null;
_serverBackend = null;
_serverExeStamp = null;
if (p == null)
{
return;
Expand Down
10 changes: 9 additions & 1 deletion src/ui/Features/Video/TextToSpeech/TtsVoiceInstaller.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
using Avalonia.Controls;
using Avalonia.Controls;
using Nikse.SubtitleEdit.Core.Common;
using Nikse.SubtitleEdit.Features.Shared;
using Nikse.SubtitleEdit.Features.Video.SpeechToText;
Expand Down Expand Up @@ -312,6 +312,14 @@ await MessageBox.Show(
return false;
}

// The three audio.cpp engines share this binary. A server started from the old build
// must go before the archive is extracted: on Windows the running exe cannot be
// overwritten at all, and elsewhere the stale process would keep answering requests
// (and reject any model family the new build added) until Subtitle Edit restarts.
IndexTts25AudioCpp.StopServer();
HiggsTtsAudioCpp.StopServer();
FishTtsAudioCpp.StopServer();

var dlResult = await windowService.ShowDialogAsync<DownloadTtsWindow, DownloadTtsViewModel>(
window, vm => vm.StartDownloadIndexTts25AudioCppEngine(backend));

Expand Down