diff --git a/dlls/sapi/Makefile.in b/dlls/sapi/Makefile.in index 94e21bdd520..640c56223dd 100644 --- a/dlls/sapi/Makefile.in +++ b/dlls/sapi/Makefile.in @@ -5,7 +5,8 @@ EXTRADLLFLAGS = -mno-cygwin C_SRCS = \ main.c \ - token.c + token.c \ + tts.c IDL_SRCS = \ sapi_classes.idl \ diff --git a/dlls/sapi/main.c b/dlls/sapi/main.c index f86fee25cab..ce51c374831 100644 --- a/dlls/sapi/main.c +++ b/dlls/sapi/main.c @@ -106,6 +106,7 @@ static const struct IClassFactoryVtbl class_factory_vtbl = }; static struct class_factory data_key_cf = { { &class_factory_vtbl }, data_key_create }; +static struct class_factory speech_voice_cf = { { &class_factory_vtbl }, speech_voice_create }; static struct class_factory token_category_cf = { { &class_factory_vtbl }, token_category_create }; static struct class_factory token_enum_cf = { { &class_factory_vtbl }, token_enum_create }; @@ -124,6 +125,8 @@ HRESULT WINAPI DllGetClassObject( REFCLSID clsid, REFIID iid, void **obj ) cf = &token_category_cf.IClassFactory_iface; else if (IsEqualCLSID( clsid, &CLSID_SpObjectTokenEnum )) cf = &token_enum_cf.IClassFactory_iface; + else if (IsEqualCLSID( clsid, &CLSID_SpVoice )) + cf = &speech_voice_cf.IClassFactory_iface; if (!cf) return CLASS_E_CLASSNOTAVAILABLE; diff --git a/dlls/sapi/sapi_classes.idl b/dlls/sapi/sapi_classes.idl index 40886f59742..dbb08c9ae21 100644 --- a/dlls/sapi/sapi_classes.idl +++ b/dlls/sapi/sapi_classes.idl @@ -50,3 +50,16 @@ coclass SpObjectTokenEnum interface ISpObjectTokenEnumBuilder; [default] interface IEnumSpObjectTokens; } + +[ + uuid(96749377-3391-11d2-9ee3-00c04f797396), + helpstring("Speech Voice"), + progid("SAPI.SpVoice.1"), + vi_progid("SAPI.SpVoice"), + threading(both) +] +coclass SpVoice +{ + interface ISpVoice; + [default] interface ISpeechVoice; +} diff --git a/dlls/sapi/sapi_private.h b/dlls/sapi/sapi_private.h index 1f89672242f..8db4b47bbdc 100644 --- a/dlls/sapi/sapi_private.h +++ b/dlls/sapi/sapi_private.h @@ -21,6 +21,7 @@ #include "wine/heap.h" HRESULT data_key_create( IUnknown *outer, REFIID iid, void **obj ) DECLSPEC_HIDDEN; +HRESULT speech_voice_create( IUnknown *outer, REFIID iid, void **obj ) DECLSPEC_HIDDEN; HRESULT token_category_create( IUnknown *outer, REFIID iid, void **obj ) DECLSPEC_HIDDEN; HRESULT token_enum_create( IUnknown *outer, REFIID iid, void **obj ) DECLSPEC_HIDDEN; diff --git a/dlls/sapi/tests/Makefile.in b/dlls/sapi/tests/Makefile.in index bf924d7ed99..bb89e0d8e0a 100644 --- a/dlls/sapi/tests/Makefile.in +++ b/dlls/sapi/tests/Makefile.in @@ -2,4 +2,5 @@ TESTDLL = sapi.dll IMPORTS = ole32 user32 advapi32 C_SRCS = \ - token.c + token.c \ + tts.c diff --git a/dlls/sapi/tests/tts.c b/dlls/sapi/tests/tts.c new file mode 100644 index 00000000000..3b71e933a27 --- /dev/null +++ b/dlls/sapi/tests/tts.c @@ -0,0 +1,71 @@ +/* + * Speech API (SAPI) text-to-speech tests. + * + * Copyright 2019 Jactry Zeng 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 + */ + +#define COBJMACROS + +#include "sapiddk.h" +#include "sperror.h" + +#include "wine/test.h" + +#define EXPECT_REF(obj,ref) _expect_ref((IUnknown*)obj, ref, __LINE__) +static void _expect_ref(IUnknown *obj, ULONG ref, int line) +{ + ULONG rc; + IUnknown_AddRef(obj); + rc = IUnknown_Release(obj); + ok_(__FILE__,line)(rc == ref, "Unexpected refcount %d, expected %d.\n", rc, ref); +} + +static void test_interfaces(void) +{ + ISpeechVoice *speech_voice; + IDispatch *dispatch; + IUnknown *unk; + HRESULT hr; + + hr = CoCreateInstance(&CLSID_SpVoice, NULL, CLSCTX_INPROC_SERVER, + &IID_ISpeechVoice, (void **)&speech_voice); + ok(hr == S_OK, "Failed to create ISpeechVoice interface: %#x.\n", hr); + EXPECT_REF(speech_voice, 1); + + hr = CoCreateInstance(&CLSID_SpVoice, NULL, CLSCTX_INPROC_SERVER, + &IID_IDispatch, (void **)&dispatch); + ok(hr == S_OK, "Failed to create IDispatch interface: %#x.\n", hr); + EXPECT_REF(dispatch, 1); + EXPECT_REF(speech_voice, 1); + IDispatch_Release(dispatch); + + hr = CoCreateInstance(&CLSID_SpVoice, NULL, CLSCTX_INPROC_SERVER, + &IID_IUnknown, (void **)&unk); + ok(hr == S_OK, "Failed to create IUnknown interface: %#x.\n", hr); + EXPECT_REF(unk, 1); + EXPECT_REF(speech_voice, 1); + IUnknown_Release(unk); + + ISpeechVoice_Release(speech_voice); +} + +START_TEST(tts) +{ + CoInitialize(NULL); + test_interfaces(); + CoUninitialize(); +} diff --git a/dlls/sapi/tts.c b/dlls/sapi/tts.c new file mode 100644 index 00000000000..4000db11f57 --- /dev/null +++ b/dlls/sapi/tts.c @@ -0,0 +1,414 @@ +/* + * Speech API (SAPI) text-to-speech implementation. + * + * Copyright 2019 Jactry Zeng 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 + +#define COBJMACROS + +#include "windef.h" +#include "winbase.h" +#include "objbase.h" + +#include "sapiddk.h" + +#include "wine/debug.h" + +#include "sapi_private.h" + +WINE_DEFAULT_DEBUG_CHANNEL(sapi); + +struct speech_voice +{ + ISpeechVoice ISpeechVoice_iface; + LONG ref; +}; + +static inline struct speech_voice *impl_from_ISpeechVoice(ISpeechVoice *iface) +{ + return CONTAINING_RECORD(iface, struct speech_voice, ISpeechVoice_iface); +} + +/* ISpeechVoice interface */ +static HRESULT WINAPI speech_voice_QueryInterface(ISpeechVoice *iface, REFIID iid, void **obj) +{ + struct speech_voice *This = impl_from_ISpeechVoice(iface); + + TRACE("(%p, %s %p).\n", iface, debugstr_guid(iid), obj); + + if (IsEqualIID(iid, &IID_IUnknown) || + IsEqualIID(iid, &IID_IDispatch) || + IsEqualIID(iid, &IID_ISpeechVoice)) + *obj = &This->ISpeechVoice_iface; + else + { + *obj = NULL; + FIXME("interface %s not implemented.\n", debugstr_guid(iid)); + return E_NOINTERFACE; + } + + IUnknown_AddRef((IUnknown *)*obj); + return S_OK; +} + +static ULONG WINAPI speech_voice_AddRef(ISpeechVoice *iface) +{ + struct speech_voice *This = impl_from_ISpeechVoice(iface); + ULONG ref = InterlockedIncrement(&This->ref); + + TRACE("(%p): ref=%u.\n", iface, ref); + + return ref; +} + +static ULONG WINAPI speech_voice_Release(ISpeechVoice *iface) +{ + struct speech_voice *This = impl_from_ISpeechVoice(iface); + ULONG ref = InterlockedDecrement(&This->ref); + + TRACE("(%p): ref=%u.\n", iface, ref); + + if (!ref) + { + heap_free(This); + } + + return ref; +} + +static HRESULT WINAPI speech_voice_GetTypeInfoCount(ISpeechVoice *iface, UINT *info) +{ + FIXME("(%p, %p): stub.\n", iface, info); + + return E_NOTIMPL; +} + +static HRESULT WINAPI speech_voice_GetTypeInfo(ISpeechVoice *iface, UINT info, LCID lcid, + ITypeInfo **type_info) +{ + FIXME("(%p, %u, %u, %p): stub.\n", iface, info, lcid, type_info); + + return E_NOTIMPL; +} + +static HRESULT WINAPI speech_voice_GetIDsOfNames(ISpeechVoice *iface, REFIID riid, LPOLESTR *names, + UINT count, LCID lcid, DISPID *dispid) +{ + FIXME("(%p, %s, %p, %u, %u, %p): stub.\n", iface, debugstr_guid(riid), names, count, lcid, dispid); + + return E_NOTIMPL; +} + +static HRESULT WINAPI speech_voice_Invoke(ISpeechVoice *iface, DISPID dispid, REFIID riid, LCID lcid, + WORD flags, DISPPARAMS *params, VARIANT *result, + EXCEPINFO *excepinfo, UINT *argerr) +{ + FIXME("(%p, %d, %s, %#x, %#x, %p, %p, %p, %p): stub.\n", iface, dispid, debugstr_guid(riid), + lcid, flags, params, result, excepinfo, argerr); + + return E_NOTIMPL; +} + +static HRESULT WINAPI speech_voice_get_Status(ISpeechVoice *iface, ISpeechVoiceStatus **status) +{ + FIXME("(%p, %p): stub.\n", iface, status); + + return E_NOTIMPL; +} + +static HRESULT WINAPI speech_voice_get_Voice(ISpeechVoice *iface, ISpeechObjectToken **voice) +{ + FIXME("(%p, %p): stub.\n", iface, voice); + + return E_NOTIMPL; +} + +static HRESULT WINAPI speech_voice_putref_Voice(ISpeechVoice *iface, ISpeechObjectToken *voice) +{ + FIXME("(%p, %p): stub.\n", iface, voice); + + return E_NOTIMPL; +} + +static HRESULT WINAPI speech_voice_get_AudioOutput(ISpeechVoice *iface, ISpeechObjectToken **output) +{ + FIXME("(%p, %p): stub.\n", iface, output); + + return E_NOTIMPL; +} + +static HRESULT WINAPI speech_voice_putref_AudioOutput(ISpeechVoice *iface, ISpeechObjectToken *output) +{ + FIXME("(%p, %p): stub.\n", iface, output); + + return E_NOTIMPL; +} + +static HRESULT WINAPI speech_voice_get_AudioOutputStream(ISpeechVoice *iface, ISpeechBaseStream **output) +{ + FIXME("(%p, %p): stub.\n", iface, output); + + return E_NOTIMPL; +} + +static HRESULT WINAPI speech_voice_putref_AudioOutputStream(ISpeechVoice *iface, ISpeechBaseStream *output) +{ + FIXME("(%p, %p): stub.\n", iface, output); + + return E_NOTIMPL; +} + +static HRESULT WINAPI speech_voice_get_Rate(ISpeechVoice *iface, LONG *rate) +{ + FIXME("(%p, %p): stub.\n", iface, rate); + + return E_NOTIMPL; +} + +static HRESULT WINAPI speech_voice_put_Rate(ISpeechVoice *iface, LONG rate) +{ + FIXME("(%p, %d): stub.\n", iface, rate); + + return E_NOTIMPL; +} + +static HRESULT WINAPI speech_voice_get_Volume(ISpeechVoice *iface, LONG *volume) +{ + FIXME("(%p, %p): stub.\n", iface, volume); + + return E_NOTIMPL; +} + +static HRESULT WINAPI speech_voice_put_Volume(ISpeechVoice *iface, LONG volume) +{ + FIXME("(%p, %d): stub.\n", iface, volume); + + return E_NOTIMPL; +} + +static HRESULT WINAPI speech_voice_put_AllowAudioOutputFormatChangesOnNextSet(ISpeechVoice *iface, + VARIANT_BOOL allow) +{ + FIXME("(%p, %d): stub.\n", iface, allow); + + return E_NOTIMPL; +} + +static HRESULT WINAPI speech_voice_get_AllowAudioOutputFormatChangesOnNextSet(ISpeechVoice *iface, VARIANT_BOOL *allow) +{ + FIXME("(%p, %p): stub.\n", iface, allow); + + return E_NOTIMPL; +} + +static HRESULT WINAPI speech_voice_get_EventInterests(ISpeechVoice *iface, SpeechVoiceEvents *flags) +{ + FIXME("(%p, %p): stub.\n", iface, flags); + + return E_NOTIMPL; +} + +static HRESULT WINAPI speech_voice_put_EventInterests(ISpeechVoice *iface, SpeechVoiceEvents flags) +{ + FIXME("(%p, %#x): stub.\n", iface, flags); + + return E_NOTIMPL; +} + +static HRESULT WINAPI speech_voice_put_Priority(ISpeechVoice *iface, SpeechVoicePriority priority) +{ + FIXME("(%p, %d): stub.\n", iface, priority); + + return E_NOTIMPL; +} + +static HRESULT WINAPI speech_voice_get_Priority(ISpeechVoice *iface, SpeechVoicePriority *priority) +{ + FIXME("(%p, %p): stub.\n", iface, priority); + + return E_NOTIMPL; +} + +static HRESULT WINAPI speech_voice_put_AlertBoundary(ISpeechVoice *iface, SpeechVoiceEvents boundary) +{ + FIXME("(%p, %#x): stub.\n", iface, boundary); + + return E_NOTIMPL; +} + +static HRESULT WINAPI speech_voice_get_AlertBoundary(ISpeechVoice *iface, SpeechVoiceEvents *boundary) +{ + FIXME("(%p, %p): stub.\n", iface, boundary); + + return E_NOTIMPL; +} + +static HRESULT WINAPI speech_voice_put_SynchronousSpeakTimeout(ISpeechVoice *iface, LONG timeout) +{ + FIXME("(%p, %d): stub.\n", iface, timeout); + + return E_NOTIMPL; +} + +static HRESULT WINAPI speech_voice_get_SynchronousSpeakTimeout(ISpeechVoice *iface, LONG *timeout) +{ + FIXME("(%p, %p): stub.\n", iface, timeout); + + return E_NOTIMPL; +} + +static HRESULT WINAPI speech_voice_Speak(ISpeechVoice *iface, BSTR text, SpeechVoiceSpeakFlags flags, LONG *number) +{ + FIXME("(%p, %s, %#x, %p): stub.\n", iface, debugstr_w(text), flags, number); + + return E_NOTIMPL; +} + +static HRESULT WINAPI speech_voice_SpeakStream(ISpeechVoice *iface, ISpeechBaseStream *stream, + SpeechVoiceSpeakFlags flags, LONG *number) +{ + FIXME("(%p, %p, %#x, %p): stub.\n", iface, stream, flags, number); + + return E_NOTIMPL; +} + +static HRESULT WINAPI speech_voice_Pause(ISpeechVoice *iface) +{ + FIXME("(%p): stub.\n", iface); + + return E_NOTIMPL; +} + +static HRESULT WINAPI speech_voice_Resume(ISpeechVoice *iface) +{ + FIXME("(%p): stub.\n", iface); + + return E_NOTIMPL; +} + +static HRESULT WINAPI speech_voice_Skip(ISpeechVoice *iface, const BSTR type, LONG items, LONG *skipped) +{ + FIXME("(%p, %s, %d, %p): stub.\n", iface, debugstr_w(type), items, skipped); + + return E_NOTIMPL; +} + +static HRESULT WINAPI speech_voice_GetVoices(ISpeechVoice *iface, BSTR required, BSTR optional, + ISpeechObjectTokens **tokens) +{ + FIXME("(%p, %s, %s, %p): stub.\n", iface, debugstr_w(required), debugstr_w(optional), tokens); + + return E_NOTIMPL; +} + +static HRESULT WINAPI speech_voice_GetAudioOutputs(ISpeechVoice *iface, BSTR required, BSTR optional, + ISpeechObjectTokens **tokens) +{ + FIXME("(%p, %s, %s, %p): stub.\n", iface, debugstr_w(required), debugstr_w(optional), tokens); + + return E_NOTIMPL; +} + +static HRESULT WINAPI speech_voice_WaitUntilDone(ISpeechVoice *iface, LONG timeout, VARIANT_BOOL *done) +{ + FIXME("(%p, %d, %p): stub.\n", iface, timeout, done); + + return E_NOTIMPL; +} + +static HRESULT WINAPI speech_voice_SpeakCompleteEvent(ISpeechVoice *iface, LONG *handle) +{ + FIXME("(%p, %p): stub.\n", iface, handle); + + return E_NOTIMPL; +} + +static HRESULT WINAPI speech_voice_IsUISupported(ISpeechVoice *iface, const BSTR typeui, const VARIANT *data, + VARIANT_BOOL *supported) +{ + FIXME("(%p, %s, %p, %p): stub.\n", iface, debugstr_w(typeui), data, supported); + + return E_NOTIMPL; +} + +static HRESULT WINAPI speech_voice_DisplayUI(ISpeechVoice *iface, LONG hwnd, BSTR title, + const BSTR typeui, const VARIANT *data) +{ + FIXME("(%p, %d, %s, %s, %p): stub.\n", iface, hwnd, debugstr_w(title), debugstr_w(typeui), data); + + return E_NOTIMPL; +} + +static const ISpeechVoiceVtbl speech_voice_vtbl = +{ + speech_voice_QueryInterface, + speech_voice_AddRef, + speech_voice_Release, + speech_voice_GetTypeInfoCount, + speech_voice_GetTypeInfo, + speech_voice_GetIDsOfNames, + speech_voice_Invoke, + speech_voice_get_Status, + speech_voice_get_Voice, + speech_voice_putref_Voice, + speech_voice_get_AudioOutput, + speech_voice_putref_AudioOutput, + speech_voice_get_AudioOutputStream, + speech_voice_putref_AudioOutputStream, + speech_voice_get_Rate, + speech_voice_put_Rate, + speech_voice_get_Volume, + speech_voice_put_Volume, + speech_voice_put_AllowAudioOutputFormatChangesOnNextSet, + speech_voice_get_AllowAudioOutputFormatChangesOnNextSet, + speech_voice_get_EventInterests, + speech_voice_put_EventInterests, + speech_voice_put_Priority, + speech_voice_get_Priority, + speech_voice_put_AlertBoundary, + speech_voice_get_AlertBoundary, + speech_voice_put_SynchronousSpeakTimeout, + speech_voice_get_SynchronousSpeakTimeout, + speech_voice_Speak, + speech_voice_SpeakStream, + speech_voice_Pause, + speech_voice_Resume, + speech_voice_Skip, + speech_voice_GetVoices, + speech_voice_GetAudioOutputs, + speech_voice_WaitUntilDone, + speech_voice_SpeakCompleteEvent, + speech_voice_IsUISupported, + speech_voice_DisplayUI, +}; + +HRESULT speech_voice_create(IUnknown *outer, REFIID iid, void **obj) +{ + struct speech_voice *This = heap_alloc(sizeof(*This)); + HRESULT hr; + + if (!This) return E_OUTOFMEMORY; + This->ISpeechVoice_iface.lpVtbl = &speech_voice_vtbl; + This->ref = 1; + + hr = ISpeechVoice_QueryInterface(&This->ISpeechVoice_iface, iid, obj); + + ISpeechVoice_Release(&This->ISpeechVoice_iface); + return hr; +} diff --git a/include/sapi.idl b/include/sapi.idl index 365d594a204..e87478cac69 100644 --- a/include/sapi.idl +++ b/include/sapi.idl @@ -839,7 +839,7 @@ interface ISpVoice : ISpEventSource [out] SPVOICESTATUS *status, [out, string] WCHAR **bookmark); - HRESULT Skip([in,string] WCHAR* type, [in] long items, [out] ULONG *skipped); + HRESULT Skip([in,string] const WCHAR *type, [in] long items, [out] ULONG *skipped); HRESULT SetPriority([in] SPVPRIORITY priority); HRESULT GetPriority([out] SPVPRIORITY* priority); @@ -1047,4 +1047,13 @@ library SpeechLib /*[default] interface ISpeechRecognizer;*/ interface ISpRecognizer; }; + + [ + uuid(96749377-3391-11d2-9ee3-00c04f797396) + ] + coclass SpVoice + { + interface ISpVoice; + [default] interface ISpeechVoice; + }; }