From: Shaun Ren sren@codeweavers.com
--- dlls/msttsengine/tts.c | 49 ++++++++++++++++++++- dlls/msttsengine/ttseng_private.h | 13 ++++++ dlls/msttsengine/unixlib.c | 72 +++++++++++++++++++++++++++++++ 3 files changed, 133 insertions(+), 1 deletion(-)
diff --git a/dlls/msttsengine/tts.c b/dlls/msttsengine/tts.c index fac8fb05743..9b65416a981 100644 --- a/dlls/msttsengine/tts.c +++ b/dlls/msttsengine/tts.c @@ -42,6 +42,8 @@ struct ttsengine LONG ref;
ISpObjectToken *token; + INT64 speaker_id; + tts_voice_t voice; };
static INIT_ONCE init_once = INIT_ONCE_STATIC_INIT; @@ -68,6 +70,20 @@ void free_tts(void) if (tts) WINE_UNIX_CALL(unix_tts_destroy, &tts); }
+static tts_voice_t tts_voice_load(tts_t tts, const char *model_path, INT64 speaker_id) +{ + struct tts_voice_load_params params = + { + .tts = tts, + .model_path = model_path, + .speaker_id = speaker_id, + .voice = 0, + }; + + WINE_UNIX_CALL(unix_tts_voice_load, ¶ms); + return params.voice; +} + static HRESULT WINAPI ttsengine_QueryInterface(ISpTTSEngine *iface, REFIID iid, void **obj) { struct ttsengine *This = impl_from_ISpTTSEngine(iface); @@ -111,6 +127,8 @@ static ULONG WINAPI ttsengine_Release(ISpTTSEngine *iface) if (!ref) { if (This->token) ISpObjectToken_Release(This->token); + if (This->voice) WINE_UNIX_CALL(unix_tts_voice_destroy, &This->voice); + free(This); }
@@ -174,14 +192,41 @@ static ULONG WINAPI objwithtoken_Release(ISpObjectWithToken *iface) static HRESULT WINAPI objwithtoken_SetObjectToken(ISpObjectWithToken *iface, ISpObjectToken *token) { struct ttsengine *This = impl_from_ISpObjectWithToken(iface); + WCHAR *value; + char *model_path; + int ret; + HRESULT hr;
- FIXME("(%p, %p): semi-stub.\n", iface, token); + TRACE("(%p, %p).\n", iface, token);
if (!token) return E_INVALIDARG; if (This->token) return SPERR_ALREADY_INITIALIZED;
+ if (FAILED(hr = ISpObjectToken_GetStringValue(token, L"ModelPath", &value))) + return hr; + model_path = wine_get_unix_file_name(value); + CoTaskMemFree(value); + if (!model_path) + return E_INVALIDARG; + + hr = ISpObjectToken_GetStringValue(token, L"SpeakerID", &value); + if (FAILED(hr) && hr != SPERR_NOT_FOUND) + return hr; + else if (SUCCEEDED(hr)) + { + ret = swscanf(value, L"%I64d", &This->speaker_id); + CoTaskMemFree(value); + if (ret != 1) + return E_INVALIDARG; + } + + This->voice = tts_voice_load(tts, model_path, This->speaker_id); + HeapFree(GetProcessHeap(), 0, model_path); + if (!This->voice) + return E_FAIL; + ISpObjectToken_AddRef(token); This->token = token; return S_OK; @@ -231,6 +276,8 @@ HRESULT ttsengine_create(REFIID iid, void **obj) This->ref = 1;
This->token = NULL; + This->speaker_id = 0; + This->voice = 0;
hr = ISpTTSEngine_QueryInterface(&This->ISpTTSEngine_iface, iid, obj); ISpTTSEngine_Release(&This->ISpTTSEngine_iface); diff --git a/dlls/msttsengine/ttseng_private.h b/dlls/msttsengine/ttseng_private.h index 04c0807dc25..62bb89f431a 100644 --- a/dlls/msttsengine/ttseng_private.h +++ b/dlls/msttsengine/ttseng_private.h @@ -32,10 +32,23 @@ void free_tts(void); HRESULT ttsengine_create(REFIID iid, void **obj);
typedef UINT64 tts_t; +typedef UINT64 tts_voice_t; + +struct tts_voice_load_params +{ + tts_t tts; + const char *model_path; + INT64 speaker_id; + tts_voice_t voice; +}; + enum unix_funcs { unix_tts_create, unix_tts_destroy, + + unix_tts_voice_load, + unix_tts_voice_destroy, };
#endif /* __WINE_TTSENG_PRIVATE_H */ diff --git a/dlls/msttsengine/unixlib.c b/dlls/msttsengine/unixlib.c index 864f97fa9a2..e45b870d066 100644 --- a/dlls/msttsengine/unixlib.c +++ b/dlls/msttsengine/unixlib.c @@ -34,11 +34,22 @@
#include "ttseng_private.h"
+struct tts_voice +{ + Piper *piper; + PiperVoice *voice; +}; + static inline Piper *get_piper(tts_t tts) { return (Piper *)(ULONG_PTR)tts; }
+static inline struct tts_voice *get_voice(tts_voice_t voice) +{ + return (struct tts_voice *)(ULONG_PTR)voice; +} + static NTSTATUS tts_create(void *args) { Piper *piper = piperInitialize(NULL); @@ -55,18 +66,79 @@ static NTSTATUS tts_destroy(void *args) return STATUS_SUCCESS; }
+static NTSTATUS tts_voice_load(void *args) +{ + struct tts_voice_load_params *params = args; + struct tts_voice *voice; + + if (!(voice = calloc(1, sizeof(*voice)))) + return STATUS_NO_MEMORY; + + voice->piper = get_piper(params->tts); + if (!(voice->voice = piperLoadVoice(voice->piper, params->model_path, NULL, params->speaker_id))) + { + free(voice); + return STATUS_UNSUCCESSFUL; + } + + params->voice = (tts_voice_t)(ULONG_PTR)voice; + + return STATUS_SUCCESS; +} + +static NTSTATUS tts_voice_destroy(void *args) +{ + struct tts_voice *voice = get_voice(*(tts_voice_t *)args); + + piperFreeVoice(voice->voice); + + free(voice); + + return STATUS_SUCCESS; +} + const unixlib_entry_t __wine_unix_call_funcs[] = { tts_create, tts_destroy, + + tts_voice_load, + tts_voice_destroy, };
#ifdef _WIN64
+typedef ULONG PTR32; + +static NTSTATUS wow64_tts_voice_load(void *args) +{ + struct + { + tts_t tts; + PTR32 model_path; + INT64 speaker_id; + tts_voice_t voice; + } *params32 = args; + struct tts_voice_load_params params = + { + .tts = params32->tts, + .model_path = ULongToPtr(params32->model_path), + .speaker_id = params32->speaker_id, + }; + NTSTATUS ret; + + ret = tts_voice_load(¶ms); + params32->voice = params.voice; + return ret; +} + const unixlib_entry_t __wine_unix_call_wow64_funcs[] = { tts_create, tts_destroy, + + wow64_tts_voice_load, + tts_voice_destroy, };
#endif /* _WIN64 */