actually use bindings
about time
This commit is contained in:
parent
1ebd3285f9
commit
13ce046d3f
7 changed files with 86 additions and 45 deletions
8
Justfile
8
Justfile
|
@ -2,10 +2,18 @@ build:
|
||||||
dotnet build -c Release
|
dotnet build -c Release
|
||||||
make -C speech2 -j$(nproc)
|
make -C speech2 -j$(nproc)
|
||||||
|
|
||||||
|
cp speech2/bin/x86/Release/speech2.dll SAPIServer/bin/Release/net40/windows-x86
|
||||||
|
cp /usr/i686-w64-mingw32/bin/libgcc_s_dw2-1.dll SAPIServer/bin/Release/net40/windows-x86/
|
||||||
|
cp /usr/i686-w64-mingw32/bin/libstdc++-6.dll SAPIServer/bin/Release/net40/windows-x86/
|
||||||
|
|
||||||
build-debug:
|
build-debug:
|
||||||
dotnet build -c Debug
|
dotnet build -c Debug
|
||||||
make -C speech2 CONFIG=Debug -j$(nproc)
|
make -C speech2 CONFIG=Debug -j$(nproc)
|
||||||
|
|
||||||
|
cp speech2/bin/x86/Debug/speech2.dll SAPIServer/bin/Debug/net40/windows-x86
|
||||||
|
cp /usr/i686-w64-mingw32/bin/libgcc_s_dw2-1.dll SAPIServer/bin/Debug/net40/windows-x86/
|
||||||
|
cp /usr/i686-w64-mingw32/bin/libstdc++-6.dll SAPIServer/bin/Debug/net40/windows-x86/
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
rm -rf SAPIServer/bin SAPIServer/obj
|
rm -rf SAPIServer/bin SAPIServer/obj
|
||||||
make -C speech2 clean
|
make -C speech2 clean
|
||||||
|
|
|
@ -7,22 +7,24 @@ using System.Text;
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
|
|
||||||
namespace SAPIServer {
|
namespace SAPIServer {
|
||||||
class Program {
|
|
||||||
static void Main(string[] args) {
|
class SpeechServer {
|
||||||
if(args.Length < 1 || !ushort.TryParse(args[0], out var port)) {
|
private Dictionary<string, SpeechAPI> apis = new();
|
||||||
Console.Error.WriteLine("Usage: SAPIServer.exe <port>");
|
private HTTPServer httpServer = new();
|
||||||
Environment.Exit(1);
|
|
||||||
return;
|
public SpeechServer() {
|
||||||
}
|
// Test out creating a speech2 api object
|
||||||
var http = new HTTPServer();
|
apis["sapi4"] = new SpeechAPI(EngineType.ET_SAPI4);
|
||||||
http.Get("/api/voices", ctx => {
|
|
||||||
|
httpServer.Get("/api/voices", ctx => {
|
||||||
using(var synth = new SpeechSynthesizer()) {
|
using(var synth = new SpeechSynthesizer()) {
|
||||||
ctx.Response.ContentType = "application/json";
|
ctx.Response.ContentType = "application/json";
|
||||||
return Encoding.UTF8.GetBytes(
|
return Encoding.UTF8.GetBytes(
|
||||||
JsonConvert.SerializeObject(new VoicesResponse { voices = synth.GetInstalledVoices().Select(v => v.VoiceInfo.Name).ToArray() }));
|
JsonConvert.SerializeObject(new VoicesResponse { voices = synth.GetInstalledVoices().Select(v => v.VoiceInfo.Name).ToArray() }));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
http.Post("/api/synthesize", ctx => {
|
|
||||||
|
httpServer.Post("/api/synthesize", ctx => {
|
||||||
SynthesizePayload body;
|
SynthesizePayload body;
|
||||||
try {
|
try {
|
||||||
string bodyraw;
|
string bodyraw;
|
||||||
|
@ -38,20 +40,38 @@ namespace SAPIServer {
|
||||||
ctx.Response.StatusCode = 400;
|
ctx.Response.StatusCode = 400;
|
||||||
return Encoding.UTF8.GetBytes("Bad payload");
|
return Encoding.UTF8.GetBytes("Bad payload");
|
||||||
}
|
}
|
||||||
using(var ms = new MemoryStream()) using(var synth = new SpeechSynthesizer()) {
|
using(var ms = new MemoryStream()) {
|
||||||
if(!synth.GetInstalledVoices().Any(v => v.VoiceInfo.Name == body.voice)) {
|
using(var synth = new SpeechSynthesizer()) {
|
||||||
ctx.Response.StatusCode = 400;
|
if(!synth.GetInstalledVoices().Any(v => v.VoiceInfo.Name == body.voice)) {
|
||||||
return Encoding.UTF8.GetBytes("Voice not found");
|
ctx.Response.StatusCode = 400;
|
||||||
|
return Encoding.UTF8.GetBytes("Voice not found");
|
||||||
|
}
|
||||||
|
synth.SelectVoice(body.voice);
|
||||||
|
synth.SetOutputToWaveStream(ms);
|
||||||
|
synth.Speak(body.text);
|
||||||
|
ctx.Response.ContentType = "audio/wav";
|
||||||
|
return ms.ToArray();
|
||||||
}
|
}
|
||||||
synth.SelectVoice(body.voice);
|
|
||||||
synth.SetOutputToWaveStream(ms);
|
|
||||||
synth.Speak(body.text);
|
|
||||||
ctx.Response.ContentType = "audio/wav";
|
|
||||||
return ms.ToArray();
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Start(ushort port) {
|
||||||
Console.WriteLine($"[{DateTime.Now:u}] Starting HTTP server on port {port}");
|
Console.WriteLine($"[{DateTime.Now:u}] Starting HTTP server on port {port}");
|
||||||
http.Listen(port);
|
httpServer.Listen(port);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class Program {
|
||||||
|
static void Main(string[] args) {
|
||||||
|
if(args.Length < 1 || !ushort.TryParse(args[0], out var port)) {
|
||||||
|
Console.Error.WriteLine("Usage: SAPIServer.exe <port>");
|
||||||
|
Environment.Exit(1);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var server = new SpeechServer();
|
||||||
|
server.Start(port);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,11 +6,18 @@
|
||||||
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
|
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
|
||||||
<OutputType>Exe</OutputType>
|
<OutputType>Exe</OutputType>
|
||||||
<TargetFrameworks>net40</TargetFrameworks>
|
<TargetFrameworks>net40</TargetFrameworks>
|
||||||
|
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<RuntimeIdentifier>windows-x86</RuntimeIdentifier>
|
||||||
|
<!-- N.B: This is only to gain support for some compiler-supported nicities, like new(). -->
|
||||||
|
<LangVersion>9.0</LangVersion>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
|
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
|
||||||
<PackageReference Include="Microsoft.Bcl.Async" Version="1.0.168" />
|
<!--<PackageReference Include="Microsoft.Bcl.Async" Version="1.0.168" />-->
|
||||||
<Reference Include="System" />
|
<Reference Include="System" />
|
||||||
<Reference Include="System.Core" />
|
<Reference Include="System.Core" />
|
||||||
<Reference Include="System.Speech" />
|
<Reference Include="System.Speech" />
|
||||||
|
|
|
@ -8,17 +8,17 @@ namespace SAPIServer {
|
||||||
// Sync with C++ code.
|
// Sync with C++ code.
|
||||||
public enum EngineType : int { ET_SAPI4, ET_SAPI5, ET_DECTALK }
|
public enum EngineType : int { ET_SAPI4, ET_SAPI5, ET_DECTALK }
|
||||||
|
|
||||||
// Speech2 DLL API.
|
// Speech2 DLL API. Sync with c++ code.
|
||||||
internal class SpeechDLL {
|
internal class SpeechDLL {
|
||||||
[DllImport("speech2")]
|
[DllImport("speech2.dll")]
|
||||||
public static extern IntPtr speech2_create_api(EngineType type);
|
public static extern IntPtr speech2_create_api(EngineType type);
|
||||||
|
|
||||||
|
[DllImport("speech2.dll")]
|
||||||
[DllImport("speech2")]
|
|
||||||
public static extern void speech2_destroy_api(IntPtr pAPI);
|
public static extern void speech2_destroy_api(IntPtr pAPI);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Native binding of speech2 library from C++ to C#.
|
// A speech API. This is generic for all speech2 supported speech APIs, so
|
||||||
|
// we can use the same code for everything. Cool, huh?
|
||||||
public class SpeechAPI : IDisposable {
|
public class SpeechAPI : IDisposable {
|
||||||
private IntPtr handle = IntPtr.Zero;
|
private IntPtr handle = IntPtr.Zero;
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
include build/arch.mk
|
include build/arch.mk
|
||||||
include build/configs.mk
|
include build/configs.mk
|
||||||
|
|
||||||
NAME = sapiserver
|
NAME = speech2
|
||||||
|
|
||||||
BINDIR = bin/$(ARCH)/$(CONFIG)
|
BINDIR = bin/$(ARCH)/$(CONFIG)
|
||||||
OBJDIR = obj/$(ARCH)/$(CONFIG)
|
OBJDIR = obj/$(ARCH)/$(CONFIG)
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
#include <windows.h>
|
#include <windows.h>
|
||||||
|
|
||||||
#include "speechapi.hpp"
|
#include "speechapi.hpp"
|
||||||
|
|
||||||
#define SP2_EXPORT __declspec(dllexport)
|
#define SP2_EXPORT __declspec(dllexport)
|
||||||
|
|
||||||
|
// Engine type. Sync with C#
|
||||||
enum class EngineType : int { ET_SAPI4, ET_SAPI5, ET_DECTALK };
|
enum class EngineType : int { ET_SAPI4, ET_SAPI5, ET_DECTALK };
|
||||||
|
|
||||||
extern "C" {
|
extern "C" {
|
||||||
|
@ -20,21 +20,6 @@ SP2_EXPORT void speech2_destroy_api(void* engine) {
|
||||||
delete static_cast<ISpeechAPI*>(engine);
|
delete static_cast<ISpeechAPI*>(engine);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
// API bindings TODO
|
||||||
|
|
||||||
BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved) {
|
|
||||||
switch(fdwReason) {
|
|
||||||
case DLL_PROCESS_ATTACH: break;
|
|
||||||
|
|
||||||
case DLL_THREAD_ATTACH: break;
|
|
||||||
|
|
||||||
case DLL_THREAD_DETACH: break;
|
|
||||||
|
|
||||||
case DLL_PROCESS_DETACH:
|
|
||||||
if(lpvReserved != nullptr) {
|
|
||||||
break; // do not do cleanup if process termination scenario
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
return TRUE;
|
|
||||||
}
|
}
|
21
speech2/src/dllmain.cpp
Normal file
21
speech2/src/dllmain.cpp
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
#include <windows.h>
|
||||||
|
|
||||||
|
BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved) {
|
||||||
|
// N.B: Should initalize COM if it's not initalized.
|
||||||
|
// Note that with .NET Framework, *all* managed threads (incl. ThreadPool threads)
|
||||||
|
// have COM initalized by default, so we don't need to do so there.
|
||||||
|
switch(fdwReason) {
|
||||||
|
case DLL_PROCESS_ATTACH: break;
|
||||||
|
|
||||||
|
case DLL_THREAD_ATTACH: break;
|
||||||
|
|
||||||
|
case DLL_THREAD_DETACH: break;
|
||||||
|
|
||||||
|
case DLL_PROCESS_DETACH:
|
||||||
|
if(lpvReserved != nullptr) {
|
||||||
|
break; // do not do cleanup if process termination scenario
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return TRUE;
|
||||||
|
}
|
Loading…
Reference in a new issue