diff --git a/dlls/sapi/Makefile.in b/dlls/sapi/Makefile.in index 640c56223dd..464a174caa0 100644 --- a/dlls/sapi/Makefile.in +++ b/dlls/sapi/Makefile.in @@ -4,6 +4,7 @@ IMPORTS = uuid ole32 user32 advapi32 EXTRADLLFLAGS = -mno-cygwin C_SRCS = \ + automation.c \ main.c \ token.c \ tts.c diff --git a/dlls/sapi/automation.c b/dlls/sapi/automation.c new file mode 100644 index 00000000000..429f2ed3a6f --- /dev/null +++ b/dlls/sapi/automation.c @@ -0,0 +1,210 @@ +/* + * Speech API (SAPI) automation 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 file_stream +{ + ISpeechFileStream ISpeechFileStream_iface; + LONG ref; +}; + +static inline struct file_stream *impl_from_ISpeechFileStream(ISpeechFileStream *iface) +{ + return CONTAINING_RECORD(iface, struct file_stream, ISpeechFileStream_iface); +} + +/* ISpeechFileStream interface */ +static HRESULT WINAPI file_stream_QueryInterface(ISpeechFileStream *iface, REFIID iid, void **obj) +{ + struct file_stream *This = impl_from_ISpeechFileStream(iface); + + TRACE("(%p, %s, %p).\n", iface, debugstr_guid(iid), obj); + + if (IsEqualIID(iid, &IID_IUnknown) || + IsEqualIID(iid, &IID_IDispatch) || + IsEqualIID(iid, &IID_ISpeechBaseStream) || + IsEqualIID(iid, &IID_ISpeechFileStream)) + *obj = &This->ISpeechFileStream_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 file_stream_AddRef(ISpeechFileStream *iface) +{ + struct file_stream *This = impl_from_ISpeechFileStream(iface); + ULONG ref = InterlockedIncrement(&This->ref); + + TRACE("(%p): ref=%u.\n", iface, ref); + + return ref; +} + +static ULONG WINAPI file_stream_Release(ISpeechFileStream *iface) +{ + struct file_stream *This = impl_from_ISpeechFileStream(iface); + ULONG ref = InterlockedDecrement(&This->ref); + + TRACE("(%p): ref=%u.\n", iface, ref); + + if (!ref) + { + heap_free(This); + } + + return ref; +} + +static HRESULT WINAPI file_stream_GetTypeInfoCount(ISpeechFileStream *iface, UINT *info) +{ + FIXME("(%p, %p): stub.\n", iface, info); + + return E_NOTIMPL; +} + +static HRESULT WINAPI file_stream_GetTypeInfo(ISpeechFileStream *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 file_stream_GetIDsOfNames(ISpeechFileStream *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 file_stream_Invoke(ISpeechFileStream *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 file_stream_get_Format(ISpeechFileStream *iface, ISpeechAudioFormat **format) +{ + FIXME("(%p, %p): stub.\n", iface, format); + + return E_NOTIMPL; +} + +static HRESULT WINAPI file_stream_putref_Format(ISpeechFileStream *iface, ISpeechAudioFormat *format) +{ + FIXME("(%p, %p): stub.\n", iface, format); + + return E_NOTIMPL; +} + +static HRESULT WINAPI file_stream_Read(ISpeechFileStream *iface, VARIANT *buffer, LONG written, LONG *read) +{ + FIXME("(%p, %p, %d, %p): stub.\n", iface, buffer, written, read); + + return E_NOTIMPL; +} + +static HRESULT WINAPI file_stream_Write(ISpeechFileStream *iface, VARIANT buffer, LONG *written) +{ + FIXME("(%p, %s, %p): stub.\n", iface, debugstr_variant(&buffer), written); + + return E_NOTIMPL; +} + +static HRESULT WINAPI file_stream_Seek(ISpeechFileStream *iface, VARIANT position, SpeechStreamSeekPositionType origin, + VARIANT *pos) +{ + FIXME("(%p, %s, %d, %p): stub.\n", iface, debugstr_variant(&position), origin, pos); + + return E_NOTIMPL; +} + +static HRESULT WINAPI file_stream_Open(ISpeechFileStream *iface, BSTR filename, SpeechStreamFileMode mode, VARIANT_BOOL events) +{ + FIXME("(%p, %s, %d, %d): stub.\n", iface, debugstr_w(filename), mode, events); + + return E_NOTIMPL; +} + +static HRESULT WINAPI file_stream_Close(ISpeechFileStream *iface) +{ + FIXME("(%p): stub.\n", iface); + + return E_NOTIMPL; +} + +const static ISpeechFileStreamVtbl file_stream_vtbl = +{ + file_stream_QueryInterface, + file_stream_AddRef, + file_stream_Release, + file_stream_GetTypeInfoCount, + file_stream_GetTypeInfo, + file_stream_GetIDsOfNames, + file_stream_Invoke, + file_stream_get_Format, + file_stream_putref_Format, + file_stream_Read, + file_stream_Write, + file_stream_Seek, + file_stream_Open, + file_stream_Close +}; + +HRESULT file_stream_create(IUnknown *outer, REFIID iid, void **obj) +{ + struct file_stream *This = heap_alloc(sizeof(*This)); + HRESULT hr; + + if (!This) return E_OUTOFMEMORY; + This->ISpeechFileStream_iface.lpVtbl = &file_stream_vtbl; + This->ref = 1; + + hr = ISpeechFileStream_QueryInterface(&This->ISpeechFileStream_iface, iid, obj); + + ISpeechFileStream_Release(&This->ISpeechFileStream_iface); + return hr; +} diff --git a/dlls/sapi/main.c b/dlls/sapi/main.c index ce51c374831..e8d4ae01583 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 file_stream_cf = { { &class_factory_vtbl }, file_stream_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 }; @@ -121,6 +122,8 @@ HRESULT WINAPI DllGetClassObject( REFCLSID clsid, REFIID iid, void **obj ) if (IsEqualCLSID( clsid, &CLSID_SpDataKey )) cf = &data_key_cf.IClassFactory_iface; + else if (IsEqualCLSID( clsid, &CLSID_SpFileStream )) + cf = &file_stream_cf.IClassFactory_iface; else if (IsEqualCLSID( clsid, &CLSID_SpObjectTokenCategory )) cf = &token_category_cf.IClassFactory_iface; else if (IsEqualCLSID( clsid, &CLSID_SpObjectTokenEnum )) diff --git a/dlls/sapi/sapi_classes.idl b/dlls/sapi/sapi_classes.idl index dbb08c9ae21..ed86d166fcd 100644 --- a/dlls/sapi/sapi_classes.idl +++ b/dlls/sapi/sapi_classes.idl @@ -63,3 +63,16 @@ coclass SpVoice interface ISpVoice; [default] interface ISpeechVoice; } + +[ + uuid(947812b3-2ae1-4644-ba86-9e90ded7ec91), + helpstring("SpFileStream"), + progid("SAPI.SpFileStream.1"), + vi_progid("SAPI.SpFileStream"), + threading(both) +] +coclass SpFileStream +{ + interface ISpStream; + [default] interface ISpeechFileStream; +} diff --git a/dlls/sapi/sapi_private.h b/dlls/sapi/sapi_private.h index 8db4b47bbdc..e9fed0ea443 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 file_stream_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 bb89e0d8e0a..d5d97550970 100644 --- a/dlls/sapi/tests/Makefile.in +++ b/dlls/sapi/tests/Makefile.in @@ -2,5 +2,6 @@ TESTDLL = sapi.dll IMPORTS = ole32 user32 advapi32 C_SRCS = \ + automation.c \ token.c \ tts.c diff --git a/dlls/sapi/tests/automation.c b/dlls/sapi/tests/automation.c new file mode 100644 index 00000000000..57b6c7a3759 --- /dev/null +++ b/dlls/sapi/tests/automation.c @@ -0,0 +1,63 @@ +/* + * Speech API (SAPI) automation 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" + +static void test_interfaces(void) +{ + ISpeechFileStream *filestream; + ISpeechBaseStream *basestream; + IDispatch *dispatch; + IUnknown *unk; + HRESULT hr; + + hr = CoCreateInstance(&CLSID_SpFileStream, NULL, CLSCTX_INPROC_SERVER, + &IID_ISpeechFileStream, (void **)&filestream); + ok(hr == S_OK, "Failed to create ISpeechFileStream interface: %#x.\n", hr); + + hr = CoCreateInstance(&CLSID_SpFileStream, NULL, CLSCTX_INPROC_SERVER, + &IID_IUnknown, (void **)&unk); + ok(hr == S_OK, "Failed to create IUnknown interface: %#x.\n", hr); + IUnknown_Release(unk); + + hr = CoCreateInstance(&CLSID_SpFileStream, NULL, CLSCTX_INPROC_SERVER, + &IID_IDispatch, (void **)&dispatch); + ok(hr == S_OK, "Failed to create IDispatch interface: %#x.\n", hr); + IDispatch_Release(dispatch); + + hr = CoCreateInstance(&CLSID_SpFileStream, NULL, CLSCTX_INPROC_SERVER, + &IID_ISpeechBaseStream, (void **)&basestream); + ok(hr == S_OK, "Failed to create ISpeechBaseStream interface: %#x.\n", hr); + ISpeechBaseStream_Release(basestream); + + ISpeechFileStream_Release(filestream); +} + +START_TEST(automation) +{ + CoInitialize(NULL); + test_interfaces(); + CoUninitialize(); +} diff --git a/include/sapi.idl b/include/sapi.idl index e87478cac69..9e6ba5be5ea 100644 --- a/include/sapi.idl +++ b/include/sapi.idl @@ -483,6 +483,15 @@ typedef [restricted, hidden] struct SPRECORESULTTIMES ULONGLONG ullStart; } SPRECORESULTTIMES; +typedef [hidden] enum SPFILEMODE +{ + SPFM_OPEN_READONLY, + SPFM_OPEN_READWRITE, + SPFM_CREATE, + SPFM_CREATE_ALWAYS, + SPFM_NUM_MODES +} SPFILEMODE; + cpp_quote("#if defined(__GNUC__)") cpp_quote("#define SPCAT_AUDIOOUT (const WCHAR []){ 'H','K','E','Y','_','L','O','C','A','L','_','M','A','C','H','I','N','E','\\\\','S','O','F','T','W','A','R','E','\\\\','M','i','c','r','o','s','o','f','t','\\\\','S','p','e','e','c','h','\\\\','A','u','d','i','o','O','u','t','p','u','t',0 }") @@ -1016,6 +1025,22 @@ interface ISpRecognizer : ISpProperties HRESULT EmulateRecognition([in] ISpPhrase *phrase); }; +[ + object, + uuid(12e3cca9-7518-44c5-a5e7-ba5a79cb929e), + pointer_default(unique), + local, + restricted +] +interface ISpStream : ISpStreamFormat +{ + HRESULT SetBaseStream([in] IStream *stream, [in] REFGUID format, [in] const WAVEFORMATEX *wave); + HRESULT GetBaseStream([out] IStream **stream); + HRESULT BindToFile([in] LPCWSTR filename, [in] SPFILEMODE mode, [in] const GUID *format, + const WAVEFORMATEX *wave, [in] ULONGLONG interest); + HRESULT Close(); +} + [ helpstring("Speech Object Library"), uuid(c866ca3a-32f7-11d2-9602-00c04f8ee628), @@ -1056,4 +1081,13 @@ library SpeechLib interface ISpVoice; [default] interface ISpeechVoice; }; + + [ + uuid(947812b3-2ae1-4644-ba86-9e90ded7ec91), + ] + coclass SpFileStream + { + interface ISpStream; + [default] interface ISpeechFileStream; + }; } diff --git a/include/sapiaut.idl b/include/sapiaut.idl index 4ae4ebe2315..ecd6908163b 100644 --- a/include/sapiaut.idl +++ b/include/sapiaut.idl @@ -2005,3 +2005,32 @@ interface ISpeechRecognizer : IDispatch [out,retval] ISpeechObjectTokens **tokens); } + +typedef enum SpeechStreamFileMode +{ + SSFMOpenForRead = SPFM_OPEN_READONLY, + SSFMOpenReadWrite = SPFM_OPEN_READWRITE, + SSFMCreate = SPFM_CREATE, + SSFMCreateForWrite = SPFM_CREATE_ALWAYS, +} SpeechStreamFileMode; + +typedef [hidden] enum DISPID_SpeechFileStream +{ + DISPID_SFSOpen = 100, + DISPID_SFSClose +} DISPID_SpeechFileStream; + +[ + object, + uuid(af67f125-ab39-4e93-b4a2-cc2e66e182a7), + dual, + pointer_default(unique) +] +interface ISpeechFileStream : ISpeechBaseStream +{ + [id(DISPID_SFSOpen)] + HRESULT Open([in] BSTR filename, [in, defaultvalue(SSFMOpenForRead)] SpeechStreamFileMode mode, + [in, defaultvalue(0)] VARIANT_BOOL events); + [id(DISPID_SFSClose)] + HRESULT Close(void); +};