From: Shaun Ren sren@codeweavers.com
--- configure.ac | 14 ++ dlls/winetts/Makefile.in | 11 ++ dlls/winetts/main.c | 123 +++++++++++++++++ dlls/winetts/tts.c | 220 +++++++++++++++++++++++++++++++ dlls/winetts/winetts.rc | 22 ++++ dlls/winetts/winetts.rgs | 29 ++++ dlls/winetts/winetts.spec | 4 + dlls/winetts/winetts_classes.idl | 28 ++++ dlls/winetts/winetts_private.h | 35 +++++ 9 files changed, 486 insertions(+) create mode 100644 dlls/winetts/Makefile.in create mode 100644 dlls/winetts/main.c create mode 100644 dlls/winetts/tts.c create mode 100644 dlls/winetts/winetts.rc create mode 100644 dlls/winetts/winetts.rgs create mode 100644 dlls/winetts/winetts.spec create mode 100644 dlls/winetts/winetts_classes.idl create mode 100644 dlls/winetts/winetts_private.h
diff --git a/configure.ac b/configure.ac index 7229e25dbed..9e5d5ae994e 100644 --- a/configure.ac +++ b/configure.ac @@ -51,6 +51,7 @@ AC_ARG_WITH(oss, AS_HELP_STRING([--without-oss],[do not use the OSS sound AC_ARG_WITH(pcap, AS_HELP_STRING([--without-pcap],[do not use the Packet Capture library]), [if test "x$withval" = "xno"; then ac_cv_header_pcap_pcap_h=no; fi]) AC_ARG_WITH(pcsclite, AS_HELP_STRING([--without-pcsclite],[do not use PCSC lite])) +AC_ARG_WITH(piper, AS_HELP_STRING([--without-piper],[do not use the Piper TTS library])) AC_ARG_WITH(pthread, AS_HELP_STRING([--without-pthread],[do not use the pthread library]), [if test "x$withval" = "xno"; then ac_cv_header_pthread_h=no; fi]) AC_ARG_WITH(pulse, AS_HELP_STRING([--without-pulse],[do not use PulseAudio sound support])) @@ -1808,6 +1809,18 @@ fi WINE_NOTICE_WITH(vulkan,[test "x$ac_cv_lib_soname_vulkan" = "x" -a "x$ac_cv_lib_soname_MoltenVK" = "x"], [libvulkan and libMoltenVK ${notice_platform}development files not found, Vulkan won't be supported.])
+dnl **** Check for libpiper **** +if test "x$with_piper" != "xno" +then + WINE_PACKAGE_FLAGS(PIPER,[piper],[-lpiper],,, + [AC_CHECK_HEADER(piper/piper_c.h, + [AC_CHECK_LIB(piper,piperInitialize,[:],[PIPER_LIBS=""],[$PIPER_LIBS])], + [PIPER_LIBS=""])]) +fi +WINE_NOTICE_WITH(piper,[test "$ac_cv_lib_piper_piperInitialize" != "yes"], + [libpiper ${notice_platform}development files not found, winetts won't be supported.], + [enable_winetts]) + dnl **** Check for gcc specific options ****
if test "x${GCC}" = "xyes" @@ -3186,6 +3199,7 @@ WINE_CONFIG_MAKEFILE(dlls/wineoss.drv) WINE_CONFIG_MAKEFILE(dlls/wineps.drv) WINE_CONFIG_MAKEFILE(dlls/wineps16.drv16,enable_win16) WINE_CONFIG_MAKEFILE(dlls/winepulse.drv) +WINE_CONFIG_MAKEFILE(dlls/winetts) WINE_CONFIG_MAKEFILE(dlls/wineusb.sys) WINE_CONFIG_MAKEFILE(dlls/winevulkan) WINE_CONFIG_MAKEFILE(dlls/winewayland.drv) diff --git a/dlls/winetts/Makefile.in b/dlls/winetts/Makefile.in new file mode 100644 index 00000000000..22fd7bfc005 --- /dev/null +++ b/dlls/winetts/Makefile.in @@ -0,0 +1,11 @@ +MODULE = winetts.dll +IMPORTS = ole32 + +C_SRCS = \ + main.c \ + tts.c + +IDL_SRCS = \ + winetts_classes.idl + +RC_SRCS = winetts.rc diff --git a/dlls/winetts/main.c b/dlls/winetts/main.c new file mode 100644 index 00000000000..5f8b33df4d6 --- /dev/null +++ b/dlls/winetts/main.c @@ -0,0 +1,123 @@ +/* Wine TTS main file. + * + * Copyright 2023 Shaun Ren for CodeWeavers + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#include <stdarg.h> + +#define COBJMACROS + +#include "windef.h" +#include "winbase.h" +#include "initguid.h" +#include "objbase.h" +#include "sapiddk.h" + +#include "wine/debug.h" + +#include "winetts_private.h" + +WINE_DEFAULT_DEBUG_CHANNEL(winetts); + +BOOL WINAPI DllMain(HINSTANCE instance, DWORD reason, void *reserved) +{ + if (reason == DLL_PROCESS_ATTACH) + { + DisableThreadLibraryCalls(instance); + __wine_init_unix_call(); + } + return TRUE; +} + +struct class_factory +{ + IClassFactory IClassFactory_iface; + HRESULT (*create_instance)(REFIID iid, void **out); +}; + +static inline struct class_factory *impl_from_IClassFactory(IClassFactory *iface) +{ + return CONTAINING_RECORD(iface, struct class_factory, IClassFactory_iface); +} + +static HRESULT WINAPI class_factory_QueryInterface(IClassFactory *iface, REFIID iid, void **out) +{ + TRACE("(%p, %s, %p).\n", iface, debugstr_guid(iid), out); + + if (IsEqualGUID(iid, &IID_IUnknown) || IsEqualGUID(iid, &IID_IClassFactory)) + { + *out = iface; + IClassFactory_AddRef(iface); + return S_OK; + } + + WARN("%s not implemented.\n", debugstr_guid(iid)); + *out = NULL; + return E_NOINTERFACE; +} + +static ULONG WINAPI class_factory_AddRef(IClassFactory *iface) +{ + return 2; +} + +static ULONG WINAPI class_factory_Release(IClassFactory *iface) +{ + return 1; +} + +static HRESULT WINAPI class_factory_CreateInstance(IClassFactory *iface, IUnknown *outer, REFIID iid, void **obj) +{ + struct class_factory *This = impl_from_IClassFactory(iface); + + TRACE("(%p, %p, %s, %p).\n", iface, outer, debugstr_guid(iid), obj); + + *obj = NULL; + if (outer) return CLASS_E_NOAGGREGATION; + return This->create_instance(iid, obj); +} + +static HRESULT WINAPI class_factory_LockServer(IClassFactory *iface, BOOL lock) +{ + FIXME("(%d): stub.\n", lock); + return S_OK; +} + +static const IClassFactoryVtbl class_factory_vtbl = +{ + class_factory_QueryInterface, + class_factory_AddRef, + class_factory_Release, + class_factory_CreateInstance, + class_factory_LockServer, +}; + +static struct class_factory ttsengine_cf = {{&class_factory_vtbl}, ttsengine_create}; + +HRESULT WINAPI DllGetClassObject(REFCLSID clsid, REFIID iid, void **obj) +{ + IClassFactory *cf; + + TRACE("(%s, %s, %p).\n", debugstr_guid(clsid), debugstr_guid(iid), obj); + + if (IsEqualCLSID(clsid, &CLSID_WineTTSEngine)) + cf = &ttsengine_cf.IClassFactory_iface; + else + return CLASS_E_CLASSNOTAVAILABLE; + + return IClassFactory_QueryInterface(cf, iid, obj); +} diff --git a/dlls/winetts/tts.c b/dlls/winetts/tts.c new file mode 100644 index 00000000000..02578f4f92e --- /dev/null +++ b/dlls/winetts/tts.c @@ -0,0 +1,220 @@ +/* + * Wine sapi TTS engine implementation. + * + * Copyright 2023 Shaun Ren for CodeWeavers + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#include <stdarg.h> + +#define COBJMACROS + +#include "windef.h" +#include "winbase.h" +#include "objbase.h" + +#include "sapiddk.h" +#include "sperror.h" + +#include "wine/debug.h" +#include "wine/heap.h" + +WINE_DEFAULT_DEBUG_CHANNEL(winetts); + +struct ttsengine +{ + ISpTTSEngine ISpTTSEngine_iface; + ISpObjectWithToken ISpObjectWithToken_iface; + LONG ref; + + ISpObjectToken *token; +}; + +static inline struct ttsengine *impl_from_ISpTTSEngine(ISpTTSEngine *iface) +{ + return CONTAINING_RECORD(iface, struct ttsengine, ISpTTSEngine_iface); +} + +static inline struct ttsengine *impl_from_ISpObjectWithToken(ISpObjectWithToken *iface) +{ + return CONTAINING_RECORD(iface, struct ttsengine, ISpObjectWithToken_iface); +} + +static HRESULT WINAPI ttsengine_QueryInterface(ISpTTSEngine *iface, REFIID iid, void **obj) +{ + struct ttsengine *This = impl_from_ISpTTSEngine(iface); + + TRACE("(%p)->(%s %p)\n", This, debugstr_guid(iid), obj); + + if (IsEqualIID(iid, &IID_IUnknown) || + IsEqualIID(iid, &IID_ISpTTSEngine)) + { + *obj = &This->ISpTTSEngine_iface; + } + else if (IsEqualIID(iid, &IID_ISpObjectWithToken)) + *obj = &This->ISpObjectWithToken_iface; + else + { + *obj = NULL; + return E_NOINTERFACE; + } + + IUnknown_AddRef((IUnknown *)*obj); + return S_OK; +} + +static ULONG WINAPI ttsengine_AddRef(ISpTTSEngine *iface) +{ + struct ttsengine *This = impl_from_ISpTTSEngine(iface); + ULONG ref = InterlockedIncrement(&This->ref); + + TRACE("(%p) ref=%lu\n", This, ref); + + return ref; +} + +static ULONG WINAPI ttsengine_Release(ISpTTSEngine *iface) +{ + struct ttsengine *This = impl_from_ISpTTSEngine(iface); + ULONG ref = InterlockedDecrement(&This->ref); + + TRACE("(%p) ref=%lu\n", This, ref); + + if (!ref) + { + ISpObjectToken_Release(This->token); + heap_free(This); + } + + return ref; +} + +static HRESULT WINAPI ttsengine_Speak(ISpTTSEngine *iface, DWORD flags, REFGUID fmtid, + const WAVEFORMATEX *wfx, const SPVTEXTFRAG *frag_list, + ISpTTSEngineSite *site) +{ + FIXME("(%p, %#lx, %s, %p, %p, %p): stub.\n", iface, flags, debugstr_guid(fmtid), wfx, frag_list, site); + + return E_NOTIMPL; +} + +static HRESULT WINAPI ttsengine_GetOutputFormat(ISpTTSEngine *iface, const GUID *fmtid, + const WAVEFORMATEX *wfx, GUID *out_fmtid, + WAVEFORMATEX **out_wfx) +{ + FIXME("(%p, %s, %p, %p, %p): stub.\n", iface, debugstr_guid(fmtid), wfx, out_fmtid, out_wfx); + + return E_NOTIMPL; +} + +static ISpTTSEngineVtbl ttsengine_vtbl = +{ + ttsengine_QueryInterface, + ttsengine_AddRef, + ttsengine_Release, + ttsengine_Speak, + ttsengine_GetOutputFormat, +}; + +static HRESULT WINAPI objwithtoken_QueryInterface(ISpObjectWithToken *iface, REFIID iid, void **obj) +{ + struct ttsengine *This = impl_from_ISpObjectWithToken(iface); + + TRACE("(%p, %s, %p).\n", iface, debugstr_guid(iid), obj); + + return ISpTTSEngine_QueryInterface(&This->ISpTTSEngine_iface, iid, obj); +} + +static ULONG WINAPI objwithtoken_AddRef(ISpObjectWithToken *iface) +{ + struct ttsengine *This = impl_from_ISpObjectWithToken(iface); + + TRACE("(%p).\n", iface); + + return ISpTTSEngine_AddRef(&This->ISpTTSEngine_iface); +} + +static ULONG WINAPI objwithtoken_Release(ISpObjectWithToken *iface) +{ + struct ttsengine *This = impl_from_ISpObjectWithToken(iface); + + TRACE("(%p).\n", iface); + + return ISpTTSEngine_Release(&This->ISpTTSEngine_iface); +} + +static HRESULT WINAPI objwithtoken_SetObjectToken(ISpObjectWithToken *iface, ISpObjectToken *token) +{ + struct ttsengine *This = impl_from_ISpObjectWithToken(iface); + + FIXME("(%p, %p): semi-stub.\n", iface, token); + + if (!token) + return E_INVALIDARG; + if (This->token) + return SPERR_ALREADY_INITIALIZED; + + ISpObjectToken_AddRef(token); + This->token = token; + return S_OK; +} + +static HRESULT WINAPI objwithtoken_GetObjectToken(ISpObjectWithToken *iface, ISpObjectToken **token) +{ + struct ttsengine *This = impl_from_ISpObjectWithToken(iface); + + TRACE("(%p, %p).\n", iface, token); + + if (!token) + return E_POINTER; + + *token = This->token; + if (*token) + { + ISpObjectToken_AddRef(*token); + return S_OK; + } + else + return S_FALSE; +} + +static const ISpObjectWithTokenVtbl objwithtoken_vtbl = +{ + objwithtoken_QueryInterface, + objwithtoken_AddRef, + objwithtoken_Release, + objwithtoken_SetObjectToken, + objwithtoken_GetObjectToken +}; + +HRESULT ttsengine_create(REFIID iid, void **obj) +{ + struct ttsengine *This; + HRESULT hr; + + if (!(This = heap_alloc(sizeof(*This)))) + return E_OUTOFMEMORY; + + This->ISpTTSEngine_iface.lpVtbl = &ttsengine_vtbl; + This->ISpObjectWithToken_iface.lpVtbl = &objwithtoken_vtbl; + This->ref = 1; + + This->token = NULL; + + hr = ISpTTSEngine_QueryInterface(&This->ISpTTSEngine_iface, iid, obj); + ISpTTSEngine_Release(&This->ISpTTSEngine_iface); + return hr; +} diff --git a/dlls/winetts/winetts.rc b/dlls/winetts/winetts.rc new file mode 100644 index 00000000000..b2dcef5de77 --- /dev/null +++ b/dlls/winetts/winetts.rc @@ -0,0 +1,22 @@ +/* + * Wine TTS resource file. + * + * Copyright 2023 Shaun Ren for CodeWeavers + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +/* @makedep: winetts.rgs */ +1 WINE_REGISTRY winetts.rgs diff --git a/dlls/winetts/winetts.rgs b/dlls/winetts/winetts.rgs new file mode 100644 index 00000000000..545c59fcf72 --- /dev/null +++ b/dlls/winetts/winetts.rgs @@ -0,0 +1,29 @@ +HKLM +{ + NoRemove Software + { + NoRemove Microsoft + { + NoRemove Speech + { + NoRemove Voices + { + NoRemove Tokens + { + val DefaultDefaultTokenId = s 'HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Speech\Voices\Tokens\WineTTS_en-US' + WineTTS_en-US = s 'Wine Voice - English (United States)' + { + val CLSID = s '{3FBA361F-0DEC-477C-B02E-57D07623B1BF}' + val 409 = s 'Wine Voice - English (United States)' + Attributes + { + val Language = s '409' + val Vendor = s 'Wine' + } + } + } + } + } + } + } +} diff --git a/dlls/winetts/winetts.spec b/dlls/winetts/winetts.spec new file mode 100644 index 00000000000..b16365d0c9f --- /dev/null +++ b/dlls/winetts/winetts.spec @@ -0,0 +1,4 @@ +@ stdcall -private DllCanUnloadNow() +@ stdcall -private DllGetClassObject(ptr ptr ptr) +@ stdcall -private DllRegisterServer() +@ stdcall -private DllUnregisterServer() diff --git a/dlls/winetts/winetts_classes.idl b/dlls/winetts/winetts_classes.idl new file mode 100644 index 00000000000..8491c10d55c --- /dev/null +++ b/dlls/winetts/winetts_classes.idl @@ -0,0 +1,28 @@ +/* + * Wine TTS classes. + * + * Copyright 2023 Shaun Ren for CodeWeavers + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#pragma makedep register + +[ + helpstring("Wine TTS Engine"), + threading(both), + uuid(3fba361f-0dec-477c-b02e-57d07623b1bf) +] +coclass WineTTSEngine {} diff --git a/dlls/winetts/winetts_private.h b/dlls/winetts/winetts_private.h new file mode 100644 index 00000000000..eee62ccf8b5 --- /dev/null +++ b/dlls/winetts/winetts_private.h @@ -0,0 +1,35 @@ +/* + * Wine TTS private header file. + * + * Copyright 2023 Shaun Ren for CodeWeavers + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#ifndef __WINE_WINETTS_PRIVATE_H +#define __WINE_WINETTS_PRIVATE_H + +#include <stdbool.h> +#include <stdint.h> +#include "windef.h" +#include "winternl.h" + +#include "wine/unixlib.h" + +DEFINE_GUID(CLSID_WineTTSEngine, 0x3fba361f,0x0dec,0x477c,0xb0,0x2e,0x57,0xd0,0x76,0x23,0xb1,0xbf); + +HRESULT ttsengine_create(REFIID iid, void **obj); + +#endif /* __WINE_WINETTS_PRIVATE_H */