voiceinfo list work
(crashes for some reason)
This commit is contained in:
parent
3b127a0b08
commit
126566c3cf
9 changed files with 124 additions and 15 deletions
4
Justfile
4
Justfile
|
@ -8,9 +8,9 @@ build:
|
|||
|
||||
build-debug:
|
||||
dotnet build -c Debug
|
||||
make -C speech2 CONFIG=Debug -j$(nproc)
|
||||
make -C speech2 CONFIG=Release -j$(nproc)
|
||||
|
||||
cp speech2/bin/x86/Debug/speech2.dll SAPIServer/bin/Debug/net40/windows-x86
|
||||
cp speech2/bin/x86/Release/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/
|
||||
|
||||
|
|
|
@ -15,13 +15,21 @@ namespace SAPIServer {
|
|||
public SpeechServer() {
|
||||
// Test out creating a speech2 api object
|
||||
apis["sapi4"] = new SpeechAPI(EngineType.ET_SAPI4);
|
||||
#if true
|
||||
foreach(var voice in apis["sapi4"].GetVoices()) {
|
||||
Console.WriteLine($"ggg {voice.name}");
|
||||
}
|
||||
#endif
|
||||
|
||||
httpServer.Get("/api/voices", ctx => {
|
||||
/*
|
||||
using(var synth = new SpeechSynthesizer()) {
|
||||
ctx.Response.ContentType = "application/json";
|
||||
return Encoding.UTF8.GetBytes(
|
||||
JsonConvert.SerializeObject(new VoicesResponse { voices = synth.GetInstalledVoices().Select(v => v.VoiceInfo.Name).ToArray() }));
|
||||
}
|
||||
}*/
|
||||
|
||||
return new byte[]{0};
|
||||
});
|
||||
|
||||
httpServer.Post("/api/synthesize", ctx => {
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFrameworks>net40</TargetFrameworks>
|
||||
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
|
|
|
@ -1,13 +1,39 @@
|
|||
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Security;
|
||||
|
||||
namespace SAPIServer {
|
||||
|
||||
// Sync with C++ code.
|
||||
public enum EngineType : int { ET_SAPI4, ET_SAPI5, ET_DECTALK }
|
||||
|
||||
public class VoiceDef {
|
||||
public Guid guid;
|
||||
public string name;
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential, Pack = 1)]
|
||||
internal struct VoiceDefInternal {
|
||||
Guid guid;
|
||||
|
||||
[MarshalAs(UnmanagedType.LPStr)]
|
||||
string name;
|
||||
|
||||
public VoiceDef Voicify() {
|
||||
Console.WriteLine($"FUCK ${name}");
|
||||
//var str = Marshal.PtrToStringAnsi(name);
|
||||
VoiceDef def = new();
|
||||
|
||||
def.guid = guid;
|
||||
def.name = name;
|
||||
return def;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Speech2 DLL API. Sync with c++ code.
|
||||
internal class SpeechDLL {
|
||||
[DllImport("speech2.dll")]
|
||||
|
@ -15,6 +41,12 @@ namespace SAPIServer {
|
|||
|
||||
[DllImport("speech2.dll")]
|
||||
public static extern void speech2_destroy_api(IntPtr pAPI);
|
||||
|
||||
[DllImport("speech2.dll")]
|
||||
public static extern int speech2_api_get_voiceinfo_count(IntPtr pAPI);
|
||||
|
||||
[DllImport("speech2.dll")]
|
||||
public static extern IntPtr speech2_api_get_voiceinfo_index(IntPtr pAPI, int index);
|
||||
}
|
||||
|
||||
// A speech API. This is generic for all speech2 supported speech APIs, so
|
||||
|
@ -28,6 +60,18 @@ namespace SAPIServer {
|
|||
throw new InvalidOperationException("Failed to create speech API");
|
||||
}
|
||||
|
||||
public List<VoiceDef> GetVoices() {
|
||||
var count = SpeechDLL.speech2_api_get_voiceinfo_count(handle);
|
||||
Console.WriteLine($"count {count}");
|
||||
var list = new List<VoiceDef>();
|
||||
for(var i = 0; i < count; ++i) {
|
||||
var ptr = SpeechDLL.speech2_api_get_voiceinfo_index(handle, i);
|
||||
var obj = (VoiceDefInternal)Marshal.PtrToStructure(ptr, typeof(VoiceDefInternal));
|
||||
list.Add(obj.Voicify());
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
void IDisposable.Dispose() {
|
||||
if(handle != IntPtr.Zero)
|
||||
SpeechDLL.speech2_destroy_api(handle);
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
# Base compiler flags. Only change if you *explicitly* know what you're doing.
|
||||
BASE_CCFLAGS = -MMD -fvisibility=hidden -std=gnu17 -fpermissive -fno-ident -msse -Iinclude -Isrc -D_UCRT -D_WIN32_WINNT=0x0501
|
||||
BASE_CXXFLAGS = -MMD -fvisibility=hidden -std=c++20 -fpermissive -fno-ident -msse -Iinclude -Isrc -Ithird_party -D_UCRT -D_WIN32_WINNT=0x0501
|
||||
BASE_CCFLAGS = -MMD -fvisibility=hidden -std=gnu17 -fpermissive -fno-pic -fno-pie -fno-ident -msse -Iinclude -Isrc -D_UCRT -D_WIN32_WINNT=0x0501
|
||||
BASE_CXXFLAGS = -MMD -fvisibility=hidden -std=c++20 -fpermissive -fno-pic -fno-pie -fno-ident -msse -Iinclude -Isrc -Ithird_party -D_UCRT -D_WIN32_WINNT=0x0501
|
||||
BASE_LDFLAGS = -Wl,--subsystem=windows -fvisibility=hidden -shared -lkernel32 -lshell32 -luser32 -luuid -lole32
|
||||
|
||||
Release_Valid = yes
|
||||
|
|
|
@ -9,10 +9,20 @@ enum class EngineType : int { ET_SAPI4, ET_SAPI5, ET_DECTALK };
|
|||
extern "C" {
|
||||
|
||||
SP2_EXPORT void* speech2_create_api(EngineType type) {
|
||||
ISpeechAPI* api = nullptr;
|
||||
switch(type) {
|
||||
case EngineType::ET_SAPI4: return static_cast<void*>(ISpeechAPI::CreateSapi4());
|
||||
case EngineType::ET_SAPI4:
|
||||
api = ISpeechAPI::CreateSapi4();
|
||||
break;
|
||||
default: return nullptr;
|
||||
}
|
||||
|
||||
if(auto hr = api->Initialize(); FAILED(hr)) {
|
||||
delete api;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return static_cast<void*>(api);
|
||||
}
|
||||
|
||||
SP2_EXPORT void speech2_destroy_api(void* engine) {
|
||||
|
@ -22,4 +32,20 @@ SP2_EXPORT void speech2_destroy_api(void* engine) {
|
|||
|
||||
// API bindings TODO
|
||||
|
||||
SP2_EXPORT int speech2_api_get_voiceinfo_count(void* engine) {
|
||||
if(engine) {
|
||||
auto* api = static_cast<ISpeechAPI*>(engine);
|
||||
return api->GetVoices().size();
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
SP2_EXPORT const ISpeechAPI::VoiceInfo* speech2_api_get_voiceinfo_index(void* engine, int index) {
|
||||
if(engine) {
|
||||
auto* api = static_cast<ISpeechAPI*>(engine);
|
||||
return &api->GetVoices()[index];
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,11 +1,14 @@
|
|||
#include <windows.h>
|
||||
#include <winscard.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_PROCESS_ATTACH:
|
||||
CoInitialize(nullptr);
|
||||
break;
|
||||
|
||||
case DLL_THREAD_ATTACH: break;
|
||||
|
||||
|
@ -15,6 +18,7 @@ BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved) {
|
|||
if(lpvReserved != nullptr) {
|
||||
break; // do not do cleanup if process termination scenario
|
||||
}
|
||||
CoUninitialize();
|
||||
break;
|
||||
}
|
||||
return TRUE;
|
||||
|
|
|
@ -19,23 +19,40 @@ struct SpeechAPI_SAPI4 : public ISpeechAPI {
|
|||
HRESULT Initialize() override {
|
||||
HRESULT hRes;
|
||||
|
||||
printf("speech2: SpeechAPI_Sapi4::Initalize() begin\n");
|
||||
|
||||
hRes = pEnum.CreateInstance(CLSID_TTSEnumerator, CLSCTX_INPROC);
|
||||
if(FAILED(hRes))
|
||||
return hRes;
|
||||
|
||||
|
||||
pEnum->Reset();
|
||||
|
||||
printf("speech2: SpeechAPI_Sapi4::Initalize() created enum\n");
|
||||
|
||||
// Fill out voices
|
||||
EnumVoices();
|
||||
|
||||
|
||||
printf("speech2: SpeechAPI_Sapi4::Initalize() filled out voices! Yay\n");
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
std::vector<VoiceInfo> GetVoices() override {
|
||||
TTSMODEINFO found {};
|
||||
std::vector<VoiceInfo> ret;
|
||||
void EnumVoices() {
|
||||
static TTSMODEINFO found {};
|
||||
|
||||
while(!pEnum->Next(1, &found, nullptr)) {
|
||||
ret.push_back({ .guid = found.gModeID, .voiceName = found.szModeName });
|
||||
//auto ptr = strdup(found.szModeName);
|
||||
printf("EnumVoices() voice %p\n", &found.szModeName);
|
||||
//voices.push_back(VoiceInfo { .guid = found.gModeID, .voiceName = ptr });
|
||||
}
|
||||
|
||||
pEnum->Reset();
|
||||
return ret;
|
||||
}
|
||||
|
||||
const std::vector<VoiceInfo>& GetVoices() override {
|
||||
return voices;
|
||||
}
|
||||
|
||||
HRESULT SelectVoiceImpl(const GUID& guid) {
|
||||
|
@ -77,6 +94,8 @@ struct SpeechAPI_SAPI4 : public ISpeechAPI {
|
|||
|
||||
// The above comment is also why this isn't a ComPtr.
|
||||
AudioOutBuffer* pAudioOut { nullptr };
|
||||
|
||||
std::vector<VoiceInfo> voices{};
|
||||
};
|
||||
|
||||
ISpeechAPI* ISpeechAPI::CreateSapi4() {
|
||||
|
|
|
@ -7,7 +7,15 @@ struct ISpeechAPI {
|
|||
|
||||
struct VoiceInfo {
|
||||
GUID guid{}; // Optional. May not be filled out if th e
|
||||
std::string voiceName;
|
||||
char* voiceName;
|
||||
|
||||
//VoiceInfo(const VoiceInfo&) = delete;
|
||||
//VoiceInfo(VoiceInfo&&) = delete;
|
||||
|
||||
~VoiceInfo() {
|
||||
if(voiceName)
|
||||
free(voiceName);
|
||||
}
|
||||
};
|
||||
|
||||
virtual ~ISpeechAPI() = default;
|
||||
|
@ -19,7 +27,7 @@ struct ISpeechAPI {
|
|||
/// Performs the bare level of initalization required to use the speech API
|
||||
virtual HRESULT Initialize() = 0;
|
||||
|
||||
virtual std::vector<VoiceInfo> GetVoices() = 0;
|
||||
virtual const std::vector<VoiceInfo>& GetVoices() = 0;
|
||||
|
||||
/// Selects a voice.
|
||||
virtual HRESULT SelectVoice(std::string_view voiceName) = 0;
|
||||
|
|
Loading…
Reference in a new issue