cmd: Fix a couple of issues with redirections.
[wine.git] / dlls / sapi / tts.c
blob80f0298b51c76f7f6c56191d82859c085e0fc8f0
1 /*
2 * Speech API (SAPI) text-to-speech implementation.
4 * Copyright 2019 Jactry Zeng for CodeWeavers
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
21 #include <stdarg.h>
23 #define COBJMACROS
25 #include "windef.h"
26 #include "winbase.h"
27 #include "objbase.h"
29 #include "sapiddk.h"
30 #include "sperror.h"
32 #include "wine/debug.h"
34 #include "sapi_private.h"
36 WINE_DEFAULT_DEBUG_CHANNEL(sapi);
38 struct speech_voice
40 ISpeechVoice ISpeechVoice_iface;
41 ISpVoice ISpVoice_iface;
42 IConnectionPointContainer IConnectionPointContainer_iface;
43 LONG ref;
45 ISpStreamFormat *output;
46 ISpObjectToken *engine_token;
47 ISpTTSEngine *engine;
48 LONG cur_stream_num;
49 DWORD actions;
50 USHORT volume;
51 LONG rate;
52 struct async_queue queue;
53 CRITICAL_SECTION cs;
56 static inline struct speech_voice *impl_from_ISpeechVoice(ISpeechVoice *iface)
58 return CONTAINING_RECORD(iface, struct speech_voice, ISpeechVoice_iface);
61 static inline struct speech_voice *impl_from_ISpVoice(ISpVoice *iface)
63 return CONTAINING_RECORD(iface, struct speech_voice, ISpVoice_iface);
66 static inline struct speech_voice *impl_from_IConnectionPointContainer(IConnectionPointContainer *iface)
68 return CONTAINING_RECORD(iface, struct speech_voice, IConnectionPointContainer_iface);
71 struct tts_engine_site
73 ISpTTSEngineSite ISpTTSEngineSite_iface;
74 LONG ref;
76 struct speech_voice *voice;
77 ULONG stream_num;
80 static inline struct tts_engine_site *impl_from_ISpTTSEngineSite(ISpTTSEngineSite *iface)
82 return CONTAINING_RECORD(iface, struct tts_engine_site, ISpTTSEngineSite_iface);
85 static HRESULT create_token_category(const WCHAR *cat_id, ISpObjectTokenCategory **cat)
87 HRESULT hr;
88 if (FAILED(hr = CoCreateInstance(&CLSID_SpObjectTokenCategory, NULL, CLSCTX_INPROC_SERVER,
89 &IID_ISpObjectTokenCategory, (void **)cat)))
90 return hr;
91 return ISpObjectTokenCategory_SetId(*cat, cat_id, FALSE);
94 static HRESULT create_default_token(const WCHAR *cat_id, ISpObjectToken **token)
96 ISpObjectTokenCategory *cat;
97 WCHAR *default_token_id = NULL;
98 HRESULT hr;
100 TRACE("(%s, %p).\n", debugstr_w(cat_id), token);
102 if (FAILED(hr = create_token_category(cat_id, &cat)))
103 return hr;
105 hr = ISpObjectTokenCategory_GetDefaultTokenId(cat, &default_token_id);
106 ISpObjectTokenCategory_Release(cat);
107 if (FAILED(hr))
108 return hr;
110 if (FAILED(hr = CoCreateInstance(&CLSID_SpObjectToken, NULL, CLSCTX_INPROC_SERVER,
111 &IID_ISpObjectToken, (void **)token)))
112 goto done;
114 if (FAILED(hr = ISpObjectToken_SetId(*token, NULL, default_token_id, FALSE)))
116 ISpObjectToken_Release(*token);
117 *token = NULL;
120 done:
121 CoTaskMemFree(default_token_id);
122 return hr;
125 /* ISpeechVoice interface */
126 static HRESULT WINAPI speech_voice_QueryInterface(ISpeechVoice *iface, REFIID iid, void **obj)
128 struct speech_voice *This = impl_from_ISpeechVoice(iface);
130 TRACE("(%p, %s %p).\n", iface, debugstr_guid(iid), obj);
132 if (IsEqualIID(iid, &IID_IUnknown) ||
133 IsEqualIID(iid, &IID_IDispatch) ||
134 IsEqualIID(iid, &IID_ISpeechVoice))
135 *obj = &This->ISpeechVoice_iface;
136 else if (IsEqualIID(iid, &IID_ISpVoice))
137 *obj = &This->ISpVoice_iface;
138 else if (IsEqualIID(iid, &IID_IConnectionPointContainer))
139 *obj = &This->IConnectionPointContainer_iface;
140 else
142 *obj = NULL;
143 FIXME("interface %s not implemented.\n", debugstr_guid(iid));
144 return E_NOINTERFACE;
147 IUnknown_AddRef((IUnknown *)*obj);
148 return S_OK;
151 static ULONG WINAPI speech_voice_AddRef(ISpeechVoice *iface)
153 struct speech_voice *This = impl_from_ISpeechVoice(iface);
154 ULONG ref = InterlockedIncrement(&This->ref);
156 TRACE("(%p): ref=%lu.\n", iface, ref);
158 return ref;
161 static ULONG WINAPI speech_voice_Release(ISpeechVoice *iface)
163 struct speech_voice *This = impl_from_ISpeechVoice(iface);
164 ULONG ref = InterlockedDecrement(&This->ref);
166 TRACE("(%p): ref=%lu.\n", iface, ref);
168 if (!ref)
170 async_cancel_queue(&This->queue);
171 if (This->output) ISpStreamFormat_Release(This->output);
172 if (This->engine_token) ISpObjectToken_Release(This->engine_token);
173 if (This->engine) ISpTTSEngine_Release(This->engine);
174 DeleteCriticalSection(&This->cs);
176 free(This);
179 return ref;
182 static HRESULT WINAPI speech_voice_GetTypeInfoCount(ISpeechVoice *iface, UINT *count)
184 TRACE("(%p, %p).\n", iface, count);
185 *count = 1;
186 return S_OK;
189 static HRESULT WINAPI speech_voice_GetTypeInfo(ISpeechVoice *iface, UINT index, LCID lcid,
190 ITypeInfo **type_info)
192 TRACE("(%p, %u, %#lx, %p).\n", iface, index, lcid, type_info);
193 if (index != 0) return DISP_E_BADINDEX;
194 return get_typeinfo(ISpeechVoice_tid, type_info);
197 static HRESULT WINAPI speech_voice_GetIDsOfNames(ISpeechVoice *iface, REFIID riid, LPOLESTR *names,
198 UINT count, LCID lcid, DISPID *dispids)
200 ITypeInfo *typeinfo;
201 HRESULT hr;
203 TRACE("(%p, %s, %p, %u, %#lx, %p).\n", iface, debugstr_guid(riid), names, count, lcid, dispids);
205 if (FAILED(hr = get_typeinfo(ISpeechVoice_tid, &typeinfo)))
206 return hr;
207 hr = ITypeInfo_GetIDsOfNames(typeinfo, names, count, dispids);
208 ITypeInfo_Release(typeinfo);
209 return hr;
212 static HRESULT WINAPI speech_voice_Invoke(ISpeechVoice *iface, DISPID dispid, REFIID riid, LCID lcid,
213 WORD flags, DISPPARAMS *params, VARIANT *result,
214 EXCEPINFO *excepinfo, UINT *argerr)
216 ITypeInfo *typeinfo;
217 HRESULT hr;
219 TRACE("(%p, %ld, %s, %#lx, %#x, %p, %p, %p, %p).\n", iface, dispid, debugstr_guid(riid),
220 lcid, flags, params, result, excepinfo, argerr);
222 if (FAILED(hr = get_typeinfo(ISpeechVoice_tid, &typeinfo)))
223 return hr;
224 hr = ITypeInfo_Invoke(typeinfo, iface, dispid, flags, params, result, excepinfo, argerr);
225 ITypeInfo_Release(typeinfo);
226 return hr;
229 static HRESULT WINAPI speech_voice_get_Status(ISpeechVoice *iface, ISpeechVoiceStatus **status)
231 FIXME("(%p, %p): stub.\n", iface, status);
233 return E_NOTIMPL;
236 static HRESULT WINAPI speech_voice_get_Voice(ISpeechVoice *iface, ISpeechObjectToken **voice)
238 struct speech_voice *This = impl_from_ISpeechVoice(iface);
239 ISpObjectToken *token;
240 HRESULT hr;
242 TRACE("(%p, %p).\n", iface, voice);
244 if (!voice) return E_POINTER;
245 if (FAILED(hr = ISpVoice_GetVoice(&This->ISpVoice_iface, &token)))
246 return hr;
247 hr = ISpObjectToken_QueryInterface(token, &IID_ISpeechObjectToken, (void **)voice);
248 ISpObjectToken_Release(token);
249 return hr;
252 static HRESULT WINAPI speech_voice_putref_Voice(ISpeechVoice *iface, ISpeechObjectToken *voice)
254 struct speech_voice *This = impl_from_ISpeechVoice(iface);
255 ISpObjectToken *token;
256 HRESULT hr;
258 TRACE("(%p, %p).\n", iface, voice);
260 if (!voice) return E_INVALIDARG;
261 if (FAILED(hr = ISpeechObjectToken_QueryInterface(voice, &IID_ISpObjectToken, (void **)&token)))
262 return hr;
263 hr = ISpVoice_SetVoice(&This->ISpVoice_iface, token);
264 ISpObjectToken_Release(token);
265 return hr;
268 static HRESULT WINAPI speech_voice_get_AudioOutput(ISpeechVoice *iface, ISpeechObjectToken **output)
270 FIXME("(%p, %p): stub.\n", iface, output);
272 return E_NOTIMPL;
275 static HRESULT WINAPI speech_voice_putref_AudioOutput(ISpeechVoice *iface, ISpeechObjectToken *output)
277 FIXME("(%p, %p): stub.\n", iface, output);
279 return E_NOTIMPL;
282 static HRESULT WINAPI speech_voice_get_AudioOutputStream(ISpeechVoice *iface, ISpeechBaseStream **output)
284 FIXME("(%p, %p): stub.\n", iface, output);
286 return E_NOTIMPL;
289 static HRESULT WINAPI speech_voice_putref_AudioOutputStream(ISpeechVoice *iface, ISpeechBaseStream *output)
291 FIXME("(%p, %p): stub.\n", iface, output);
293 return E_NOTIMPL;
296 static HRESULT WINAPI speech_voice_get_Rate(ISpeechVoice *iface, LONG *rate)
298 FIXME("(%p, %p): stub.\n", iface, rate);
300 return E_NOTIMPL;
303 static HRESULT WINAPI speech_voice_put_Rate(ISpeechVoice *iface, LONG rate)
305 FIXME("(%p, %ld): stub.\n", iface, rate);
307 return E_NOTIMPL;
310 static HRESULT WINAPI speech_voice_get_Volume(ISpeechVoice *iface, LONG *volume)
312 struct speech_voice *This = impl_from_ISpeechVoice(iface);
313 USHORT res = 0;
314 HRESULT hr;
316 TRACE("(%p, %p).\n", iface, volume);
318 if (!volume) return E_POINTER;
319 hr = ISpVoice_GetVolume(&This->ISpVoice_iface, &res);
320 *volume = res;
321 return hr;
324 static HRESULT WINAPI speech_voice_put_Volume(ISpeechVoice *iface, LONG volume)
326 struct speech_voice *This = impl_from_ISpeechVoice(iface);
328 TRACE("(%p, %ld).\n", iface, volume);
330 return ISpVoice_SetVolume(&This->ISpVoice_iface, (USHORT)volume);
333 static HRESULT WINAPI speech_voice_put_AllowAudioOutputFormatChangesOnNextSet(ISpeechVoice *iface,
334 VARIANT_BOOL allow)
336 FIXME("(%p, %d): stub.\n", iface, allow);
338 return E_NOTIMPL;
341 static HRESULT WINAPI speech_voice_get_AllowAudioOutputFormatChangesOnNextSet(ISpeechVoice *iface, VARIANT_BOOL *allow)
343 FIXME("(%p, %p): stub.\n", iface, allow);
345 return E_NOTIMPL;
348 static HRESULT WINAPI speech_voice_get_EventInterests(ISpeechVoice *iface, SpeechVoiceEvents *flags)
350 FIXME("(%p, %p): stub.\n", iface, flags);
352 return E_NOTIMPL;
355 static HRESULT WINAPI speech_voice_put_EventInterests(ISpeechVoice *iface, SpeechVoiceEvents flags)
357 FIXME("(%p, %#x): stub.\n", iface, flags);
359 return E_NOTIMPL;
362 static HRESULT WINAPI speech_voice_put_Priority(ISpeechVoice *iface, SpeechVoicePriority priority)
364 FIXME("(%p, %d): stub.\n", iface, priority);
366 return E_NOTIMPL;
369 static HRESULT WINAPI speech_voice_get_Priority(ISpeechVoice *iface, SpeechVoicePriority *priority)
371 FIXME("(%p, %p): stub.\n", iface, priority);
373 return E_NOTIMPL;
376 static HRESULT WINAPI speech_voice_put_AlertBoundary(ISpeechVoice *iface, SpeechVoiceEvents boundary)
378 FIXME("(%p, %#x): stub.\n", iface, boundary);
380 return E_NOTIMPL;
383 static HRESULT WINAPI speech_voice_get_AlertBoundary(ISpeechVoice *iface, SpeechVoiceEvents *boundary)
385 FIXME("(%p, %p): stub.\n", iface, boundary);
387 return E_NOTIMPL;
390 static HRESULT WINAPI speech_voice_put_SynchronousSpeakTimeout(ISpeechVoice *iface, LONG timeout)
392 FIXME("(%p, %ld): stub.\n", iface, timeout);
394 return E_NOTIMPL;
397 static HRESULT WINAPI speech_voice_get_SynchronousSpeakTimeout(ISpeechVoice *iface, LONG *timeout)
399 FIXME("(%p, %p): stub.\n", iface, timeout);
401 return E_NOTIMPL;
404 static HRESULT WINAPI speech_voice_Speak(ISpeechVoice *iface, BSTR text, SpeechVoiceSpeakFlags flags, LONG *number)
406 struct speech_voice *This = impl_from_ISpeechVoice(iface);
408 TRACE("(%p, %s, %#x, %p).\n", iface, debugstr_w(text), flags, number);
410 return ISpVoice_Speak(&This->ISpVoice_iface, text, flags, (ULONG *)number);
413 static HRESULT WINAPI speech_voice_SpeakStream(ISpeechVoice *iface, ISpeechBaseStream *stream,
414 SpeechVoiceSpeakFlags flags, LONG *number)
416 FIXME("(%p, %p, %#x, %p): stub.\n", iface, stream, flags, number);
418 return E_NOTIMPL;
421 static HRESULT WINAPI speech_voice_Pause(ISpeechVoice *iface)
423 FIXME("(%p): stub.\n", iface);
425 return E_NOTIMPL;
428 static HRESULT WINAPI speech_voice_Resume(ISpeechVoice *iface)
430 FIXME("(%p): stub.\n", iface);
432 return E_NOTIMPL;
435 static HRESULT WINAPI speech_voice_Skip(ISpeechVoice *iface, const BSTR type, LONG items, LONG *skipped)
437 FIXME("(%p, %s, %ld, %p): stub.\n", iface, debugstr_w(type), items, skipped);
439 return E_NOTIMPL;
442 static HRESULT WINAPI speech_voice_GetVoices(ISpeechVoice *iface, BSTR required, BSTR optional,
443 ISpeechObjectTokens **tokens)
446 ISpObjectTokenCategory *cat;
447 IEnumSpObjectTokens *token_enum;
448 HRESULT hr;
450 TRACE("(%p, %s, %s, %p).\n", iface, debugstr_w(required), debugstr_w(optional), tokens);
452 if (!tokens) return E_POINTER;
454 if (FAILED(hr = create_token_category(SPCAT_VOICES, &cat)))
455 return hr;
457 if (SUCCEEDED(hr = ISpObjectTokenCategory_EnumTokens(cat, required, optional, &token_enum)))
459 hr = IEnumSpObjectTokens_QueryInterface(token_enum, &IID_ISpeechObjectTokens, (void **)tokens);
460 IEnumSpObjectTokens_Release(token_enum);
463 ISpObjectTokenCategory_Release(cat);
464 return hr;
467 static HRESULT WINAPI speech_voice_GetAudioOutputs(ISpeechVoice *iface, BSTR required, BSTR optional,
468 ISpeechObjectTokens **tokens)
470 FIXME("(%p, %s, %s, %p): stub.\n", iface, debugstr_w(required), debugstr_w(optional), tokens);
472 return E_NOTIMPL;
475 static HRESULT WINAPI speech_voice_WaitUntilDone(ISpeechVoice *iface, LONG timeout, VARIANT_BOOL *done)
477 FIXME("(%p, %ld, %p): stub.\n", iface, timeout, done);
479 return E_NOTIMPL;
482 static HRESULT WINAPI speech_voice_SpeakCompleteEvent(ISpeechVoice *iface, LONG *handle)
484 FIXME("(%p, %p): stub.\n", iface, handle);
486 return E_NOTIMPL;
489 static HRESULT WINAPI speech_voice_IsUISupported(ISpeechVoice *iface, const BSTR typeui, const VARIANT *data,
490 VARIANT_BOOL *supported)
492 FIXME("(%p, %s, %p, %p): stub.\n", iface, debugstr_w(typeui), data, supported);
494 return E_NOTIMPL;
497 static HRESULT WINAPI speech_voice_DisplayUI(ISpeechVoice *iface, LONG hwnd, BSTR title,
498 const BSTR typeui, const VARIANT *data)
500 FIXME("(%p, %ld, %s, %s, %p): stub.\n", iface, hwnd, debugstr_w(title), debugstr_w(typeui), data);
502 return E_NOTIMPL;
505 static const ISpeechVoiceVtbl speech_voice_vtbl =
507 speech_voice_QueryInterface,
508 speech_voice_AddRef,
509 speech_voice_Release,
510 speech_voice_GetTypeInfoCount,
511 speech_voice_GetTypeInfo,
512 speech_voice_GetIDsOfNames,
513 speech_voice_Invoke,
514 speech_voice_get_Status,
515 speech_voice_get_Voice,
516 speech_voice_putref_Voice,
517 speech_voice_get_AudioOutput,
518 speech_voice_putref_AudioOutput,
519 speech_voice_get_AudioOutputStream,
520 speech_voice_putref_AudioOutputStream,
521 speech_voice_get_Rate,
522 speech_voice_put_Rate,
523 speech_voice_get_Volume,
524 speech_voice_put_Volume,
525 speech_voice_put_AllowAudioOutputFormatChangesOnNextSet,
526 speech_voice_get_AllowAudioOutputFormatChangesOnNextSet,
527 speech_voice_get_EventInterests,
528 speech_voice_put_EventInterests,
529 speech_voice_put_Priority,
530 speech_voice_get_Priority,
531 speech_voice_put_AlertBoundary,
532 speech_voice_get_AlertBoundary,
533 speech_voice_put_SynchronousSpeakTimeout,
534 speech_voice_get_SynchronousSpeakTimeout,
535 speech_voice_Speak,
536 speech_voice_SpeakStream,
537 speech_voice_Pause,
538 speech_voice_Resume,
539 speech_voice_Skip,
540 speech_voice_GetVoices,
541 speech_voice_GetAudioOutputs,
542 speech_voice_WaitUntilDone,
543 speech_voice_SpeakCompleteEvent,
544 speech_voice_IsUISupported,
545 speech_voice_DisplayUI,
548 /* ISpVoice interface */
549 static HRESULT WINAPI spvoice_QueryInterface(ISpVoice *iface, REFIID iid, void **obj)
551 struct speech_voice *This = impl_from_ISpVoice(iface);
553 TRACE("(%p, %s %p).\n", iface, debugstr_guid(iid), obj);
555 return ISpeechVoice_QueryInterface(&This->ISpeechVoice_iface, iid, obj);
558 static ULONG WINAPI spvoice_AddRef(ISpVoice *iface)
560 struct speech_voice *This = impl_from_ISpVoice(iface);
562 TRACE("(%p).\n", iface);
564 return ISpeechVoice_AddRef(&This->ISpeechVoice_iface);
567 static ULONG WINAPI spvoice_Release(ISpVoice *iface)
569 struct speech_voice *This = impl_from_ISpVoice(iface);
571 TRACE("(%p).\n", iface);
573 return ISpeechVoice_Release(&This->ISpeechVoice_iface);
576 static HRESULT WINAPI spvoice_SetNotifySink(ISpVoice *iface, ISpNotifySink *sink)
578 FIXME("(%p, %p): stub.\n", iface, sink);
580 return E_NOTIMPL;
583 static HRESULT WINAPI spvoice_SetNotifyWindowMessage(ISpVoice *iface, HWND hwnd, UINT msg,
584 WPARAM wparam, LPARAM lparam)
586 FIXME("(%p, %p, %u, %Ix, %Ix): stub.\n", iface, hwnd, msg, wparam, lparam);
588 return E_NOTIMPL;
591 static HRESULT WINAPI spvoice_SetNotifyCallbackFunction(ISpVoice *iface, SPNOTIFYCALLBACK *callback,
592 WPARAM wparam, LPARAM lparam)
594 FIXME("(%p, %p, %Ix, %Ix): stub.\n", iface, callback, wparam, lparam);
596 return E_NOTIMPL;
599 static HRESULT WINAPI spvoice_SetNotifyCallbackInterface(ISpVoice *iface, ISpNotifyCallback *callback,
600 WPARAM wparam, LPARAM lparam)
602 FIXME("(%p, %p, %Ix, %Ix): stub.\n", iface, callback, wparam, lparam);
604 return E_NOTIMPL;
607 static HRESULT WINAPI spvoice_SetNotifyWin32Event(ISpVoice *iface)
609 FIXME("(%p): stub.\n", iface);
611 return E_NOTIMPL;
614 static HRESULT WINAPI spvoice_WaitForNotifyEvent(ISpVoice *iface, DWORD milliseconds)
616 FIXME("(%p, %ld): stub.\n", iface, milliseconds);
618 return E_NOTIMPL;
621 static HANDLE WINAPI spvoice_GetNotifyEventHandle(ISpVoice *iface)
623 FIXME("(%p): stub.\n", iface);
625 return NULL;
628 static HRESULT WINAPI spvoice_SetInterest(ISpVoice *iface, ULONGLONG event, ULONGLONG queued)
630 FIXME("(%p, %s, %s): stub.\n", iface, wine_dbgstr_longlong(event), wine_dbgstr_longlong(queued));
632 return E_NOTIMPL;
635 static HRESULT WINAPI spvoice_GetEvents(ISpVoice *iface, ULONG count, SPEVENT *array, ULONG *fetched)
637 FIXME("(%p, %lu, %p, %p): stub.\n", iface, count, array, fetched);
639 return E_NOTIMPL;
642 static HRESULT WINAPI spvoice_GetInfo(ISpVoice *iface, SPEVENTSOURCEINFO *info)
644 FIXME("(%p, %p): stub.\n", iface, info);
646 return E_NOTIMPL;
649 static HRESULT WINAPI spvoice_SetOutput(ISpVoice *iface, IUnknown *unk, BOOL allow_format_changes)
651 struct speech_voice *This = impl_from_ISpVoice(iface);
652 ISpStreamFormat *stream = NULL;
653 ISpObjectToken *token = NULL;
654 HRESULT hr;
656 TRACE("(%p, %p, %d).\n", iface, unk, allow_format_changes);
658 if (!allow_format_changes)
659 FIXME("ignoring allow_format_changes = FALSE.\n");
661 if (FAILED(hr = async_start_queue(&This->queue)))
662 return hr;
664 if (!unk)
666 /* TODO: Create the default SpAudioOut token here once SpMMAudioEnum is implemented. */
667 if (FAILED(hr = CoCreateInstance(&CLSID_SpMMAudioOut, NULL, CLSCTX_INPROC_SERVER,
668 &IID_ISpStreamFormat, (void **)&stream)))
669 return hr;
671 else
673 if (FAILED(IUnknown_QueryInterface(unk, &IID_ISpStreamFormat, (void **)&stream)))
675 if (FAILED(IUnknown_QueryInterface(unk, &IID_ISpObjectToken, (void **)&token)))
676 return E_INVALIDARG;
680 if (!stream)
682 hr = ISpObjectToken_CreateInstance(token, NULL, CLSCTX_ALL, &IID_ISpStreamFormat, (void **)&stream);
683 ISpObjectToken_Release(token);
684 if (FAILED(hr))
685 return hr;
688 EnterCriticalSection(&This->cs);
690 if (This->output)
691 ISpStreamFormat_Release(This->output);
692 This->output = stream;
694 LeaveCriticalSection(&This->cs);
696 return S_OK;
699 static HRESULT WINAPI spvoice_GetOutputObjectToken(ISpVoice *iface, ISpObjectToken **token)
701 FIXME("(%p, %p): stub.\n", iface, token);
703 return E_NOTIMPL;
706 static HRESULT WINAPI spvoice_GetOutputStream(ISpVoice *iface, ISpStreamFormat **stream)
708 FIXME("(%p, %p): stub.\n", iface, stream);
710 return E_NOTIMPL;
713 static HRESULT WINAPI spvoice_Pause(ISpVoice *iface)
715 FIXME("(%p): stub.\n", iface);
717 return E_NOTIMPL;
720 static HRESULT WINAPI spvoice_Resume(ISpVoice *iface)
722 FIXME("(%p): stub.\n", iface);
724 return E_NOTIMPL;
727 static HRESULT WINAPI spvoice_SetVoice(ISpVoice *iface, ISpObjectToken *token)
729 struct speech_voice *This = impl_from_ISpVoice(iface);
730 WCHAR *id = NULL, *old_id = NULL;
731 HRESULT hr;
733 TRACE("(%p, %p).\n", iface, token);
735 if (!token)
737 if (FAILED(hr = create_default_token(SPCAT_VOICES, &token)))
738 return hr;
740 else
741 ISpObjectToken_AddRef(token);
743 EnterCriticalSection(&This->cs);
745 if (This->engine_token &&
746 SUCCEEDED(ISpObjectToken_GetId(token, &id)) &&
747 SUCCEEDED(ISpObjectToken_GetId(This->engine_token, &old_id)) &&
748 !wcscmp(id, old_id))
750 ISpObjectToken_Release(token);
751 goto done;
754 if (This->engine_token)
755 ISpObjectToken_Release(This->engine_token);
756 This->engine_token = token;
758 if (This->engine)
760 ISpTTSEngine_Release(This->engine);
761 This->engine = NULL;
764 done:
765 LeaveCriticalSection(&This->cs);
766 CoTaskMemFree(id);
767 CoTaskMemFree(old_id);
768 return S_OK;
771 static HRESULT WINAPI spvoice_GetVoice(ISpVoice *iface, ISpObjectToken **token)
773 struct speech_voice *This = impl_from_ISpVoice(iface);
775 TRACE("(%p, %p).\n", iface, token);
777 if (!token)
778 return E_POINTER;
780 EnterCriticalSection(&This->cs);
782 if (!This->engine_token)
784 LeaveCriticalSection(&This->cs);
785 return create_default_token(SPCAT_VOICES, token);
788 ISpObjectToken_AddRef(This->engine_token);
789 *token = This->engine_token;
791 LeaveCriticalSection(&This->cs);
793 return S_OK;
796 struct async_result
798 HANDLE done;
799 HRESULT hr;
802 struct speak_task
804 struct async_task task;
805 struct async_result *result;
807 struct speech_voice *voice;
808 ISpTTSEngine *engine;
809 SPVTEXTFRAG *frag_list;
810 ISpTTSEngineSite *site;
811 DWORD flags;
814 static HRESULT set_output_format(ISpStreamFormat *output, ISpTTSEngine *engine, GUID *fmtid, WAVEFORMATEX **wfx)
816 GUID output_fmtid;
817 WAVEFORMATEX *output_wfx = NULL;
818 ISpAudio *audio = NULL;
819 HRESULT hr;
821 if (FAILED(hr = ISpStreamFormat_GetFormat(output, &output_fmtid, &output_wfx)))
822 return hr;
823 if (FAILED(hr = ISpTTSEngine_GetOutputFormat(engine, &output_fmtid, output_wfx, fmtid, wfx)))
824 goto done;
825 if (!IsEqualGUID(fmtid, &SPDFID_WaveFormatEx))
827 hr = E_INVALIDARG;
828 goto done;
831 if (memcmp(output_wfx, *wfx, sizeof(WAVEFORMATEX)) ||
832 memcmp(output_wfx + 1, *wfx + 1, output_wfx->cbSize))
834 if (FAILED(hr = ISpStreamFormat_QueryInterface(output, &IID_ISpAudio, (void **)&audio)) ||
835 FAILED(hr = ISpAudio_SetFormat(audio, &SPDFID_WaveFormatEx, *wfx)))
836 goto done;
839 done:
840 CoTaskMemFree(output_wfx);
841 if (audio) ISpAudio_Release(audio);
842 return hr;
845 static void speak_proc(struct async_task *task)
847 struct speak_task *speak_task = (struct speak_task *)task;
848 struct speech_voice *This = speak_task->voice;
849 GUID fmtid;
850 WAVEFORMATEX *wfx = NULL;
851 ISpAudio *audio = NULL;
852 HRESULT hr;
854 TRACE("(%p).\n", task);
856 EnterCriticalSection(&This->cs);
858 if (This->actions & SPVES_ABORT)
860 LeaveCriticalSection(&This->cs);
861 hr = S_OK;
862 goto done;
865 if (FAILED(hr = set_output_format(This->output, This->engine, &fmtid, &wfx)))
867 LeaveCriticalSection(&This->cs);
868 ERR("failed setting output format: %#lx.\n", hr);
869 goto done;
872 if (SUCCEEDED(ISpStreamFormat_QueryInterface(This->output, &IID_ISpAudio, (void **)&audio)))
873 ISpAudio_SetState(audio, SPAS_RUN, 0);
875 This->actions = SPVES_RATE | SPVES_VOLUME;
877 LeaveCriticalSection(&This->cs);
879 hr = ISpTTSEngine_Speak(speak_task->engine, speak_task->flags, &fmtid, wfx, speak_task->frag_list, speak_task->site);
880 if (SUCCEEDED(hr))
882 ISpStreamFormat_Commit(This->output, STGC_DEFAULT);
883 if (audio)
884 WaitForSingleObject(ISpAudio_EventHandle(audio), INFINITE);
886 else
887 WARN("ISpTTSEngine_Speak failed: %#lx.\n", hr);
889 done:
890 if (audio)
892 ISpAudio_SetState(audio, SPAS_CLOSED, 0);
893 ISpAudio_Release(audio);
895 CoTaskMemFree(wfx);
896 ISpTTSEngine_Release(speak_task->engine);
897 free(speak_task->frag_list);
898 ISpTTSEngineSite_Release(speak_task->site);
900 if (speak_task->result)
902 speak_task->result->hr = hr;
903 SetEvent(speak_task->result->done);
907 static HRESULT ttsenginesite_create(struct speech_voice *voice, ULONG stream_num, ISpTTSEngineSite **site);
909 static HRESULT WINAPI spvoice_Speak(ISpVoice *iface, const WCHAR *contents, DWORD flags, ULONG *stream_num_out)
911 struct speech_voice *This = impl_from_ISpVoice(iface);
912 ISpTTSEngineSite *site = NULL;
913 ISpTTSEngine *engine = NULL;
914 SPVTEXTFRAG *frag;
915 struct speak_task *speak_task = NULL;
916 struct async_result *result = NULL;
917 size_t contents_len, contents_size;
918 ULONG stream_num;
919 HRESULT hr;
921 TRACE("(%p, %p, %#lx, %p).\n", iface, contents, flags, stream_num_out);
923 flags &= ~SPF_IS_NOT_XML;
924 if (flags & ~(SPF_ASYNC | SPF_PURGEBEFORESPEAK | SPF_NLP_SPEAK_PUNC))
926 FIXME("flags %#lx not implemented.\n", flags & ~(SPF_ASYNC | SPF_PURGEBEFORESPEAK | SPF_NLP_SPEAK_PUNC));
927 return E_NOTIMPL;
930 if (flags & SPF_PURGEBEFORESPEAK)
932 ISpAudio *audio;
934 EnterCriticalSection(&This->cs);
936 This->actions = SPVES_ABORT;
937 if (This->output && SUCCEEDED(ISpStreamFormat_QueryInterface(This->output, &IID_ISpAudio, (void **)&audio)))
939 ISpAudio_SetState(audio, SPAS_CLOSED, 0);
940 ISpAudio_Release(audio);
943 LeaveCriticalSection(&This->cs);
945 async_empty_queue(&This->queue);
947 EnterCriticalSection(&This->cs);
948 This->actions = SPVES_CONTINUE;
949 LeaveCriticalSection(&This->cs);
951 if (!contents || !*contents)
952 return S_OK;
954 else if (!contents)
955 return E_POINTER;
957 contents_len = wcslen(contents);
958 contents_size = sizeof(WCHAR) * (contents_len + 1);
960 if (!This->output)
962 /* Create a new output stream with the default output. */
963 if (FAILED(hr = ISpVoice_SetOutput(iface, NULL, TRUE)))
964 return hr;
967 EnterCriticalSection(&This->cs);
969 if (!This->engine_token)
971 /* Set the engine token to default. */
972 if (FAILED(hr = ISpVoice_SetVoice(iface, NULL)))
974 LeaveCriticalSection(&This->cs);
975 return hr;
978 if (!This->engine &&
979 FAILED(hr = ISpObjectToken_CreateInstance(This->engine_token, NULL, CLSCTX_ALL, &IID_ISpTTSEngine, (void **)&This->engine)))
981 LeaveCriticalSection(&This->cs);
982 ERR("Failed to create engine: %#lx.\n", hr);
983 return hr;
985 engine = This->engine;
986 ISpTTSEngine_AddRef(engine);
988 LeaveCriticalSection(&This->cs);
990 if (!(frag = malloc(sizeof(*frag) + contents_size)))
991 return E_OUTOFMEMORY;
992 memset(frag, 0, sizeof(*frag));
993 memcpy(frag + 1, contents, contents_size);
994 frag->State.eAction = SPVA_Speak;
995 frag->State.Volume = 100;
996 frag->pTextStart = (WCHAR *)(frag + 1);
997 frag->ulTextLen = contents_len;
998 frag->ulTextSrcOffset = 0;
1000 stream_num = InterlockedIncrement(&This->cur_stream_num);
1001 if (FAILED(hr = ttsenginesite_create(This, stream_num, &site)))
1003 FIXME("Failed to create ttsenginesite: %#lx.\n", hr);
1004 goto fail;
1007 speak_task = malloc(sizeof(*speak_task));
1009 speak_task->task.proc = speak_proc;
1010 speak_task->result = NULL;
1011 speak_task->voice = This;
1012 speak_task->engine = engine;
1013 speak_task->frag_list = frag;
1014 speak_task->site = site;
1015 speak_task->flags = flags & SPF_NLP_SPEAK_PUNC;
1017 if (!(flags & SPF_ASYNC))
1019 if (!(result = malloc(sizeof(*result))))
1021 hr = E_OUTOFMEMORY;
1022 goto fail;
1024 result->hr = E_FAIL;
1025 result->done = CreateEventW(NULL, FALSE, FALSE, NULL);
1026 speak_task->result = result;
1029 if (FAILED(hr = async_queue_task(&This->queue, (struct async_task *)speak_task)))
1031 WARN("Failed to queue task: %#lx.\n", hr);
1032 goto fail;
1035 if (stream_num_out)
1036 *stream_num_out = stream_num;
1038 if (flags & SPF_ASYNC)
1039 return S_OK;
1040 else
1042 WaitForSingleObject(result->done, INFINITE);
1043 hr = result->hr;
1044 CloseHandle(result->done);
1045 free(result);
1046 return hr;
1049 fail:
1050 if (site) ISpTTSEngineSite_Release(site);
1051 if (engine) ISpTTSEngine_Release(engine);
1052 free(frag);
1053 free(speak_task);
1054 if (result)
1056 CloseHandle(result->done);
1057 free(result);
1059 return hr;
1062 static HRESULT WINAPI spvoice_SpeakStream(ISpVoice *iface, IStream *stream, DWORD flags, ULONG *number)
1064 FIXME("(%p, %p, %#lx, %p): stub.\n", iface, stream, flags, number);
1066 return E_NOTIMPL;
1069 static HRESULT WINAPI spvoice_GetStatus(ISpVoice *iface, SPVOICESTATUS *status, WCHAR **bookmark)
1071 static unsigned int once;
1073 if (!once++)
1074 FIXME("(%p, %p, %p): stub.\n", iface, status, bookmark);
1075 else
1076 WARN("(%p, %p, %p): stub.\n", iface, status, bookmark);
1078 return E_NOTIMPL;
1081 static HRESULT WINAPI spvoice_Skip(ISpVoice *iface, const WCHAR *type, LONG items, ULONG *skipped)
1083 FIXME("(%p, %s, %ld, %p): stub.\n", iface, debugstr_w(type), items, skipped);
1085 return E_NOTIMPL;
1088 static HRESULT WINAPI spvoice_SetPriority(ISpVoice *iface, SPVPRIORITY priority)
1090 FIXME("(%p, %d): stub.\n", iface, priority);
1092 return E_NOTIMPL;
1095 static HRESULT WINAPI spvoice_GetPriority(ISpVoice *iface, SPVPRIORITY *priority)
1097 FIXME("(%p, %p): stub.\n", iface, priority);
1099 return E_NOTIMPL;
1102 static HRESULT WINAPI spvoice_SetAlertBoundary(ISpVoice *iface, SPEVENTENUM boundary)
1104 FIXME("(%p, %d): stub.\n", iface, boundary);
1106 return E_NOTIMPL;
1109 static HRESULT WINAPI spvoice_GetAlertBoundary(ISpVoice *iface, SPEVENTENUM *boundary)
1111 FIXME("(%p, %p): stub.\n", iface, boundary);
1113 return E_NOTIMPL;
1116 static HRESULT WINAPI spvoice_SetRate(ISpVoice *iface, LONG rate)
1118 struct speech_voice *This = impl_from_ISpVoice(iface);
1120 TRACE("(%p, %ld).\n", iface, rate);
1122 EnterCriticalSection(&This->cs);
1123 This->rate = rate;
1124 This->actions |= SPVES_RATE;
1125 LeaveCriticalSection(&This->cs);
1127 return S_OK;
1130 static HRESULT WINAPI spvoice_GetRate(ISpVoice *iface, LONG *rate)
1132 struct speech_voice *This = impl_from_ISpVoice(iface);
1134 TRACE("(%p, %p).\n", iface, rate);
1136 EnterCriticalSection(&This->cs);
1137 *rate = This->rate;
1138 LeaveCriticalSection(&This->cs);
1140 return S_OK;
1143 static HRESULT WINAPI spvoice_SetVolume(ISpVoice *iface, USHORT volume)
1145 struct speech_voice *This = impl_from_ISpVoice(iface);
1147 TRACE("(%p, %d).\n", iface, volume);
1149 if (volume > 100)
1150 return E_INVALIDARG;
1152 EnterCriticalSection(&This->cs);
1153 This->volume = volume;
1154 This->actions |= SPVES_VOLUME;
1155 LeaveCriticalSection(&This->cs);
1157 return S_OK;
1160 static HRESULT WINAPI spvoice_GetVolume(ISpVoice *iface, USHORT *volume)
1162 struct speech_voice *This = impl_from_ISpVoice(iface);
1164 TRACE("(%p, %p).\n", iface, volume);
1166 EnterCriticalSection(&This->cs);
1167 *volume = This->volume;
1168 LeaveCriticalSection(&This->cs);
1170 return S_OK;
1173 static HRESULT WINAPI spvoice_WaitUntilDone(ISpVoice *iface, ULONG timeout)
1175 struct speech_voice *This = impl_from_ISpVoice(iface);
1176 HRESULT hr;
1178 TRACE("(%p, %ld).\n", iface, timeout);
1180 hr = async_wait_queue_empty(&This->queue, timeout);
1182 if (hr == WAIT_OBJECT_0) return S_OK;
1183 else if (hr == WAIT_TIMEOUT) return S_FALSE;
1184 return hr;
1187 static HRESULT WINAPI spvoice_SetSyncSpeakTimeout(ISpVoice *iface, ULONG timeout)
1189 FIXME("(%p, %ld): stub.\n", iface, timeout);
1191 return E_NOTIMPL;
1194 static HRESULT WINAPI spvoice_GetSyncSpeakTimeout(ISpVoice *iface, ULONG *timeout)
1196 FIXME("(%p, %p): stub.\n", iface, timeout);
1198 return E_NOTIMPL;
1201 static HANDLE WINAPI spvoice_SpeakCompleteEvent(ISpVoice *iface)
1203 FIXME("(%p): stub.\n", iface);
1205 return NULL;
1208 static HRESULT WINAPI spvoice_IsUISupported(ISpVoice *iface, const WCHAR *type, void *extra,
1209 ULONG count, BOOL *supported)
1211 FIXME("(%p, %p, %p, %ld, %p): stub.\n", iface, type, extra, count, supported);
1213 return E_NOTIMPL;
1216 static HRESULT WINAPI spvoice_DisplayUI(ISpVoice *iface, HWND parent, const WCHAR *title,
1217 const WCHAR *type, void *extra, ULONG count)
1219 FIXME("(%p, %p, %p, %p, %p, %ld): stub.\n", iface, parent, title, type, extra, count);
1221 return E_NOTIMPL;
1224 static const ISpVoiceVtbl spvoice_vtbl =
1226 spvoice_QueryInterface,
1227 spvoice_AddRef,
1228 spvoice_Release,
1229 spvoice_SetNotifySink,
1230 spvoice_SetNotifyWindowMessage,
1231 spvoice_SetNotifyCallbackFunction,
1232 spvoice_SetNotifyCallbackInterface,
1233 spvoice_SetNotifyWin32Event,
1234 spvoice_WaitForNotifyEvent,
1235 spvoice_GetNotifyEventHandle,
1236 spvoice_SetInterest,
1237 spvoice_GetEvents,
1238 spvoice_GetInfo,
1239 spvoice_SetOutput,
1240 spvoice_GetOutputObjectToken,
1241 spvoice_GetOutputStream,
1242 spvoice_Pause,
1243 spvoice_Resume,
1244 spvoice_SetVoice,
1245 spvoice_GetVoice,
1246 spvoice_Speak,
1247 spvoice_SpeakStream,
1248 spvoice_GetStatus,
1249 spvoice_Skip,
1250 spvoice_SetPriority,
1251 spvoice_GetPriority,
1252 spvoice_SetAlertBoundary,
1253 spvoice_GetAlertBoundary,
1254 spvoice_SetRate,
1255 spvoice_GetRate,
1256 spvoice_SetVolume,
1257 spvoice_GetVolume,
1258 spvoice_WaitUntilDone,
1259 spvoice_SetSyncSpeakTimeout,
1260 spvoice_GetSyncSpeakTimeout,
1261 spvoice_SpeakCompleteEvent,
1262 spvoice_IsUISupported,
1263 spvoice_DisplayUI
1266 /* ISpTTSEngineSite interface */
1267 static HRESULT WINAPI ttsenginesite_QueryInterface(ISpTTSEngineSite *iface, REFIID iid, void **obj)
1269 struct tts_engine_site *This = impl_from_ISpTTSEngineSite(iface);
1271 TRACE("(%p, %s %p).\n", iface, debugstr_guid(iid), obj);
1273 if (IsEqualIID(iid, &IID_IUnknown) ||
1274 IsEqualIID(iid, &IID_ISpTTSEngineSite))
1275 *obj = &This->ISpTTSEngineSite_iface;
1276 else
1278 *obj = NULL;
1279 FIXME("interface %s not implemented.\n", debugstr_guid(iid));
1280 return E_NOINTERFACE;
1283 IUnknown_AddRef((IUnknown *)*obj);
1284 return S_OK;
1287 static ULONG WINAPI ttsenginesite_AddRef(ISpTTSEngineSite *iface)
1289 struct tts_engine_site *This = impl_from_ISpTTSEngineSite(iface);
1290 ULONG ref = InterlockedIncrement(&This->ref);
1292 TRACE("(%p): ref=%lu.\n", iface, ref);
1294 return ref;
1297 static ULONG WINAPI ttsenginesite_Release(ISpTTSEngineSite *iface)
1299 struct tts_engine_site *This = impl_from_ISpTTSEngineSite(iface);
1301 ULONG ref = InterlockedDecrement(&This->ref);
1303 TRACE("(%p): ref=%lu.\n", iface, ref);
1305 if (!ref)
1307 if (This->voice)
1308 ISpeechVoice_Release(&This->voice->ISpeechVoice_iface);
1309 free(This);
1312 return ref;
1315 static HRESULT WINAPI ttsenginesite_AddEvents(ISpTTSEngineSite *iface, const SPEVENT *events, ULONG count)
1317 FIXME("(%p, %p, %ld): stub.\n", iface, events, count);
1319 return S_OK;
1322 static HRESULT WINAPI ttsenginesite_GetEventInterest(ISpTTSEngineSite *iface, ULONGLONG *interest)
1324 FIXME("(%p, %p): stub.\n", iface, interest);
1326 return E_NOTIMPL;
1329 static DWORD WINAPI ttsenginesite_GetActions(ISpTTSEngineSite *iface)
1331 struct tts_engine_site *This = impl_from_ISpTTSEngineSite(iface);
1332 DWORD actions;
1334 TRACE("(%p).\n", iface);
1336 EnterCriticalSection(&This->voice->cs);
1337 actions = This->voice->actions;
1338 LeaveCriticalSection(&This->voice->cs);
1340 return actions;
1343 static HRESULT WINAPI ttsenginesite_Write(ISpTTSEngineSite *iface, const void *buf, ULONG cb, ULONG *cb_written)
1345 struct tts_engine_site *This = impl_from_ISpTTSEngineSite(iface);
1347 TRACE("(%p, %p, %ld, %p).\n", iface, buf, cb, cb_written);
1349 if (!This->voice->output)
1350 return SPERR_UNINITIALIZED;
1352 return ISpStreamFormat_Write(This->voice->output, buf, cb, cb_written);
1355 static HRESULT WINAPI ttsenginesite_GetRate(ISpTTSEngineSite *iface, LONG *rate)
1357 struct tts_engine_site *This = impl_from_ISpTTSEngineSite(iface);
1359 TRACE("(%p, %p).\n", iface, rate);
1361 EnterCriticalSection(&This->voice->cs);
1362 *rate = This->voice->rate;
1363 This->voice->actions &= ~SPVES_RATE;
1364 LeaveCriticalSection(&This->voice->cs);
1366 return S_OK;
1369 static HRESULT WINAPI ttsenginesite_GetVolume(ISpTTSEngineSite *iface, USHORT *volume)
1371 struct tts_engine_site *This = impl_from_ISpTTSEngineSite(iface);
1373 TRACE("(%p, %p).\n", iface, volume);
1375 EnterCriticalSection(&This->voice->cs);
1376 *volume = This->voice->volume;
1377 This->voice->actions &= ~SPVES_VOLUME;
1378 LeaveCriticalSection(&This->voice->cs);
1380 return S_OK;
1383 static HRESULT WINAPI ttsenginesite_GetSkipInfo(ISpTTSEngineSite *iface, SPVSKIPTYPE *type, LONG *skip_count)
1385 FIXME("(%p, %p, %p): stub.\n", iface, type, skip_count);
1387 return E_NOTIMPL;
1390 static HRESULT WINAPI ttsenginesite_CompleteSkip(ISpTTSEngineSite *iface, LONG num_skipped)
1392 FIXME("(%p, %ld): stub.\n", iface, num_skipped);
1394 return E_NOTIMPL;
1397 const static ISpTTSEngineSiteVtbl ttsenginesite_vtbl =
1399 ttsenginesite_QueryInterface,
1400 ttsenginesite_AddRef,
1401 ttsenginesite_Release,
1402 ttsenginesite_AddEvents,
1403 ttsenginesite_GetEventInterest,
1404 ttsenginesite_GetActions,
1405 ttsenginesite_Write,
1406 ttsenginesite_GetRate,
1407 ttsenginesite_GetVolume,
1408 ttsenginesite_GetSkipInfo,
1409 ttsenginesite_CompleteSkip
1412 static HRESULT ttsenginesite_create(struct speech_voice *voice, ULONG stream_num, ISpTTSEngineSite **site)
1414 struct tts_engine_site *This = malloc(sizeof(*This));
1416 if (!This) return E_OUTOFMEMORY;
1418 This->ISpTTSEngineSite_iface.lpVtbl = &ttsenginesite_vtbl;
1420 This->ref = 1;
1421 This->voice = voice;
1422 This->stream_num = stream_num;
1424 ISpeechVoice_AddRef(&This->voice->ISpeechVoice_iface);
1426 *site = &This->ISpTTSEngineSite_iface;
1428 return S_OK;
1431 /* IConnectionPointContainer interface */
1432 static HRESULT WINAPI container_QueryInterface(IConnectionPointContainer *iface, REFIID iid, void **obj)
1434 struct speech_voice *This = impl_from_IConnectionPointContainer(iface);
1436 TRACE("(%p, %s %p).\n", iface, debugstr_guid(iid), obj);
1438 return ISpeechVoice_QueryInterface(&This->ISpeechVoice_iface, iid, obj);
1441 static ULONG WINAPI container_AddRef(IConnectionPointContainer *iface)
1443 struct speech_voice *This = impl_from_IConnectionPointContainer(iface);
1445 TRACE("(%p).\n", iface);
1447 return ISpeechVoice_AddRef(&This->ISpeechVoice_iface);
1450 static ULONG WINAPI container_Release(IConnectionPointContainer *iface)
1452 struct speech_voice *This = impl_from_IConnectionPointContainer(iface);
1454 TRACE("(%p).\n", iface);
1456 return ISpeechVoice_Release(&This->ISpeechVoice_iface);
1459 static HRESULT WINAPI container_EnumConnectionPoints(IConnectionPointContainer *iface,
1460 IEnumConnectionPoints **enum_cp)
1462 FIXME("(%p, %p): stub.\n", iface, enum_cp);
1464 return E_NOTIMPL;
1467 static HRESULT WINAPI container_FindConnectionPoint(IConnectionPointContainer *iface, REFIID riid,
1468 IConnectionPoint **cp)
1470 FIXME("(%p, %s, %p): stub.\n", iface, debugstr_guid(riid), cp);
1472 return E_NOTIMPL;
1475 const static IConnectionPointContainerVtbl container_vtbl =
1477 container_QueryInterface,
1478 container_AddRef,
1479 container_Release,
1480 container_EnumConnectionPoints,
1481 container_FindConnectionPoint
1484 HRESULT speech_voice_create(IUnknown *outer, REFIID iid, void **obj)
1486 struct speech_voice *This = malloc(sizeof(*This));
1487 HRESULT hr;
1489 if (!This) return E_OUTOFMEMORY;
1490 This->ISpeechVoice_iface.lpVtbl = &speech_voice_vtbl;
1491 This->ISpVoice_iface.lpVtbl = &spvoice_vtbl;
1492 This->IConnectionPointContainer_iface.lpVtbl = &container_vtbl;
1493 This->ref = 1;
1495 This->output = NULL;
1496 This->engine_token = NULL;
1497 This->engine = NULL;
1498 This->cur_stream_num = 0;
1499 This->actions = SPVES_CONTINUE;
1500 This->volume = 100;
1501 This->rate = 0;
1502 memset(&This->queue, 0, sizeof(This->queue));
1504 InitializeCriticalSection(&This->cs);
1506 hr = ISpeechVoice_QueryInterface(&This->ISpeechVoice_iface, iid, obj);
1508 ISpeechVoice_Release(&This->ISpeechVoice_iface);
1509 return hr;