Skip to content
Open
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
Binary file removed Knossos.NET.Desktop/Assets/utils/win/KSapi.exe
Binary file not shown.
Binary file removed Knossos.NET.Desktop/Assets/utils/win/KSapi_x86.exe
Binary file not shown.
90 changes: 70 additions & 20 deletions Knossos.NET.Desktop/Knossos.NET.Desktop.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
<TrimMode>copyused</TrimMode>
<PublishSingleFile>true</PublishSingleFile>
<SuppressTfmSupportBuildWarnings>true</SuppressTfmSupportBuildWarnings>
<Platforms>AnyCPU;x64</Platforms>
</PropertyGroup>

<PropertyGroup>
Expand All @@ -27,27 +28,76 @@
</PackageReference>
</ItemGroup>

<ItemGroup Condition="'$(Configuration)' != 'Release'">
<AvaloniaResource Include="Assets\**" />
</ItemGroup>
<!--https://learn.microsoft.com/en-us/dotnet/core/rid-catalog-->
<ItemGroup Condition="$(RuntimeIdentifier.StartsWith('win'))">
<AvaloniaResource Include="Assets\utils\win\**" />
</ItemGroup>
<ItemGroup Condition="$(RuntimeIdentifier.Equals('linux-riscv64'))">
<AvaloniaResource Include="Assets\utils\linux-riscv64\**" />
</ItemGroup>
<ItemGroup Condition="$(RuntimeIdentifier.Equals('linux-arm64'))">
<AvaloniaResource Include="Assets\utils\linux-arm64\**" />
</ItemGroup>
<ItemGroup Condition="$(RuntimeIdentifier.Equals('linux-x64'))">
<AvaloniaResource Include="Assets\utils\linux-x64\**" />
</ItemGroup>
<ItemGroup Condition="$(RuntimeIdentifier.StartsWith('osx'))">
<AvaloniaResource Include="Assets\utils\osx\**" />
</ItemGroup>
<ItemGroup Condition="'$(Configuration)' != 'Release'">
<AvaloniaResource Include="Assets\**" />
</ItemGroup>
<!--https://learn.microsoft.com/en-us/dotnet/core/rid-catalog-->
<ItemGroup Condition="$(RuntimeIdentifier.StartsWith('win'))">
<AvaloniaResource Include="Assets\utils\win\**" />
</ItemGroup>
<ItemGroup Condition="$(RuntimeIdentifier.Equals('linux-riscv64'))">
<AvaloniaResource Include="Assets\utils\linux-riscv64\**" />
</ItemGroup>
<ItemGroup Condition="$(RuntimeIdentifier.Equals('linux-arm64'))">
<AvaloniaResource Include="Assets\utils\linux-arm64\**" />
</ItemGroup>
<ItemGroup Condition="$(RuntimeIdentifier.Equals('linux-x64'))">
<AvaloniaResource Include="Assets\utils\linux-x64\**" />
</ItemGroup>
<ItemGroup Condition="$(RuntimeIdentifier.StartsWith('osx'))">
<AvaloniaResource Include="Assets\utils\osx\**" />
</ItemGroup>

<!--
NATIVE LIBRARY COPY RULES
==========================================================================
$(RuntimeIdentifier) is only set during "dotnet publish -r <RID>".
During "dotnet run" / F5 / plain "dotnet build" it is EMPTY.
So we use a property that normalises both cases:
- If RID is set, use it.
- If RID is empty, fall back to the OS + arch MSBuild detects at
eval time via RuntimeInformation property functions.
-->
<PropertyGroup>
<_IsWin Condition="$(RuntimeIdentifier.StartsWith('win')) Or
($(RuntimeIdentifier) == '' And $([MSBuild]::IsOSPlatform('Windows')))">true</_IsWin>
<_IsLinux Condition="$(RuntimeIdentifier.StartsWith('linux')) Or
($(RuntimeIdentifier) == '' And $([MSBuild]::IsOSPlatform('Linux')))">true</_IsLinux>
<_IsOsx Condition="$(RuntimeIdentifier.StartsWith('osx')) Or
($(RuntimeIdentifier) == '' And $([MSBuild]::IsOSPlatform('OSX')))">true</_IsOsx>

<!-- Arch: prefer explicit RID, otherwise ask the runtime -->
<_IsX64 Condition="$(RuntimeIdentifier.Contains('x64')) Or
('$(RuntimeIdentifier)' == '' And '$([System.Runtime.InteropServices.RuntimeInformation]::OSArchitecture)' == 'X64')">true</_IsX64>
<_IsX86 Condition="$(RuntimeIdentifier.Contains('x86')) Or
('$(RuntimeIdentifier)' == '' And '$([System.Runtime.InteropServices.RuntimeInformation]::OSArchitecture)' == 'X86')">true</_IsX86>
<_IsArm64 Condition="$(RuntimeIdentifier.Contains('arm64')) Or
('$(RuntimeIdentifier)' == '' And '$([System.Runtime.InteropServices.RuntimeInformation]::OSArchitecture)' == 'Arm64')">true</_IsArm64>
<_IsRiscV64 Condition="$(RuntimeIdentifier.Contains('riscv64'))">true</_IsRiscV64>
</PropertyGroup>

<!-- Windows -->
<ItemGroup Condition="'$(_IsWin)' == 'true' And '$(_IsX64)' == 'true'">
<None Update="precompiled\windows\x64\KSapi.dll">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
<Link>KSapi.dll</Link>
</None>
</ItemGroup>
<ItemGroup Condition="'$(_IsWin)' == 'true' And '$(_IsX86)' == 'true'">
<None Update="precompiled\windows\x86\KSapi.dll">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
<Link>KSapi.dll</Link>
</None>
</ItemGroup>
<ItemGroup Condition="'$(_IsWin)' == 'true' And '$(_IsArm64)' == 'true'">
<None Update="precompiled\windows\arm64\KSapi.dll">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
<Link>KSapi.dll</Link>
</None>
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\Knossos.NET\Knossos.NET.csproj" />
</ItemGroup>
</Project>

</Project>
Binary file not shown.
Binary file not shown.
Binary file not shown.
41 changes: 41 additions & 0 deletions Knossos.NET/Classes/KSapi.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
using System.Runtime.InteropServices;

namespace Knossos.NET.Classes
{
public static class KSapi
{
private const string Dll = "KSapi";
private static bool inited;
private static readonly object gate = new();

[DllImport(Dll, CallingConvention = CallingConvention.Cdecl)]
private static extern int KSapi_Init();
[DllImport(Dll, CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Cdecl)]
private static extern int KSapi_Speak(string text, int voiceIndex, int volume);
[DllImport(Dll, CallingConvention = CallingConvention.Cdecl)]
private static extern void KSapi_Stop();
[DllImport(Dll, CallingConvention = CallingConvention.Cdecl)]
private static extern void KSapi_Shutdown();

public static void Stop()
{
try
{
if (inited) KSapi_Stop();
}
catch { }
}

public static void Speak(string text, int voice, int vol)
{
try
{
lock (gate)
{
if (!inited) { KSapi_Init(); inited = true; }
}
KSapi_Speak(text, voice, vol);
}catch { }
}
}
}
59 changes: 10 additions & 49 deletions Knossos.NET/Classes/Knossos.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1743,58 +1743,19 @@ public async static void Tts(string text, int? voice_index = null, string? voice
{
if (KnUtils.IsWindows)
{
if (ttsObject != null)
{
var sp = (Process)ttsObject;
sp.Kill();
ttsObject = null;
}
if (text != string.Empty)
KSapi.Stop();

if (!string.IsNullOrEmpty(text))
{
if (!File.Exists(KnUtils.GetKnossosDataFolderPath() + Path.DirectorySeparatorChar + "KSapi.exe"))
{
if (KnUtils.CpuArch == "X86")
{
using (var fileStream = File.Create(KnUtils.GetKnossosDataFolderPath() + Path.DirectorySeparatorChar + "KSapi.exe"))
{
AssetLoader.Open(new Uri("avares://Knossos.NET.Desktop/Assets/utils/win/KSapi_x86.exe")).CopyTo(fileStream);
fileStream.Close();
}
}
else
{
using (var fileStream = File.Create(KnUtils.GetKnossosDataFolderPath() + Path.DirectorySeparatorChar + "KSapi.exe"))
{
AssetLoader.Open(new Uri("avares://Knossos.NET.Desktop/Assets/utils/win/KSapi.exe")).CopyTo(fileStream);
fileStream.Close();
}
}
}
await Task.Run(async () =>
var voice = voice_index ?? globalSettings.ttsVoice;
var vol = volume ?? globalSettings.ttsVolume;

await Task.Run(() =>
{
//Max cmdline lenght is 8192, lets limit the text to 7500
if(text.Length > 7500)
{
text = text.Substring(0, 7500);
}
await Task.Delay(300);
var voice = globalSettings.ttsVoice;
var vol = globalSettings.ttsVolume;
if (voice_index.HasValue)
voice = voice_index.Value;
if (volume.HasValue)
vol = volume.Value;
using var ttsProcess = new Process();
ttsProcess.StartInfo.FileName = KnUtils.GetKnossosDataFolderPath() + Path.DirectorySeparatorChar + "KSapi.exe";
ttsProcess.StartInfo.Arguments = "-text \"" + text + "\" -voice " + voice + " -vol " + vol;
ttsProcess.StartInfo.UseShellExecute = false;
ttsProcess.StartInfo.CreateNoWindow = true;
ttsObject = ttsProcess;
ttsProcess.Start();
ttsProcess.WaitForExit();
ttsObject = null;
KSapi.Speak(text, voice!.Value, Math.Clamp(vol, 0, 100));

if (callBack != null)
await Dispatcher.UIThread.InvokeAsync(() => callBack(), DispatcherPriority.Background);
Dispatcher.UIThread.InvokeAsync(() => callBack(), DispatcherPriority.Background);
});
}
}
Expand Down