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
32 #include "wine/debug.h"
34 #include "sapi_private.h"
36 WINE_DEFAULT_DEBUG_CHANNEL(sapi
);
40 ISpeechVoice ISpeechVoice_iface
;
41 ISpVoice ISpVoice_iface
;
42 IConnectionPointContainer IConnectionPointContainer_iface
;
45 ISpStreamFormat
*output
;
46 ISpObjectToken
*engine_token
;
52 struct async_queue queue
;
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
;
76 struct speech_voice
*voice
;
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
)
88 if (FAILED(hr
= CoCreateInstance(&CLSID_SpObjectTokenCategory
, NULL
, CLSCTX_INPROC_SERVER
,
89 &IID_ISpObjectTokenCategory
, (void **)cat
)))
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
;
100 TRACE("(%s, %p).\n", debugstr_w(cat_id
), token
);
102 if (FAILED(hr
= create_token_category(cat_id
, &cat
)))
105 hr
= ISpObjectTokenCategory_GetDefaultTokenId(cat
, &default_token_id
);
106 ISpObjectTokenCategory_Release(cat
);
110 if (FAILED(hr
= CoCreateInstance(&CLSID_SpObjectToken
, NULL
, CLSCTX_INPROC_SERVER
,
111 &IID_ISpObjectToken
, (void **)token
)))
114 if (FAILED(hr
= ISpObjectToken_SetId(*token
, NULL
, default_token_id
, FALSE
)))
116 ISpObjectToken_Release(*token
);
121 CoTaskMemFree(default_token_id
);
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
;
143 FIXME("interface %s not implemented.\n", debugstr_guid(iid
));
144 return E_NOINTERFACE
;
147 IUnknown_AddRef((IUnknown
*)*obj
);
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
);
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
);
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
);
182 static HRESULT WINAPI
speech_voice_GetTypeInfoCount(ISpeechVoice
*iface
, UINT
*count
)
184 TRACE("(%p, %p).\n", iface
, count
);
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
)
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
)))
207 hr
= ITypeInfo_GetIDsOfNames(typeinfo
, names
, count
, dispids
);
208 ITypeInfo_Release(typeinfo
);
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
)
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
)))
224 hr
= ITypeInfo_Invoke(typeinfo
, iface
, dispid
, flags
, params
, result
, excepinfo
, argerr
);
225 ITypeInfo_Release(typeinfo
);
229 static HRESULT WINAPI
speech_voice_get_Status(ISpeechVoice
*iface
, ISpeechVoiceStatus
**status
)
231 FIXME("(%p, %p): stub.\n", iface
, status
);
236 static HRESULT WINAPI
speech_voice_get_Voice(ISpeechVoice
*iface
, ISpeechObjectToken
**voice
)
238 struct speech_voice
*This
= impl_from_ISpeechVoice(iface
);
239 ISpObjectToken
*token
;
242 TRACE("(%p, %p).\n", iface
, voice
);
244 if (!voice
) return E_POINTER
;
245 if (FAILED(hr
= ISpVoice_GetVoice(&This
->ISpVoice_iface
, &token
)))
247 hr
= ISpObjectToken_QueryInterface(token
, &IID_ISpeechObjectToken
, (void **)voice
);
248 ISpObjectToken_Release(token
);
252 static HRESULT WINAPI
speech_voice_putref_Voice(ISpeechVoice
*iface
, ISpeechObjectToken
*voice
)
254 struct speech_voice
*This
= impl_from_ISpeechVoice(iface
);
255 ISpObjectToken
*token
;
258 TRACE("(%p, %p).\n", iface
, voice
);
260 if (!voice
) return E_INVALIDARG
;
261 if (FAILED(hr
= ISpeechObjectToken_QueryInterface(voice
, &IID_ISpObjectToken
, (void **)&token
)))
263 hr
= ISpVoice_SetVoice(&This
->ISpVoice_iface
, token
);
264 ISpObjectToken_Release(token
);
268 static HRESULT WINAPI
speech_voice_get_AudioOutput(ISpeechVoice
*iface
, ISpeechObjectToken
**output
)
270 FIXME("(%p, %p): stub.\n", iface
, output
);
275 static HRESULT WINAPI
speech_voice_putref_AudioOutput(ISpeechVoice
*iface
, ISpeechObjectToken
*output
)
277 FIXME("(%p, %p): stub.\n", iface
, output
);
282 static HRESULT WINAPI
speech_voice_get_AudioOutputStream(ISpeechVoice
*iface
, ISpeechBaseStream
**output
)
284 FIXME("(%p, %p): stub.\n", iface
, output
);
289 static HRESULT WINAPI
speech_voice_putref_AudioOutputStream(ISpeechVoice
*iface
, ISpeechBaseStream
*output
)
291 FIXME("(%p, %p): stub.\n", iface
, output
);
296 static HRESULT WINAPI
speech_voice_get_Rate(ISpeechVoice
*iface
, LONG
*rate
)
298 FIXME("(%p, %p): stub.\n", iface
, rate
);
303 static HRESULT WINAPI
speech_voice_put_Rate(ISpeechVoice
*iface
, LONG rate
)
305 FIXME("(%p, %ld): stub.\n", iface
, rate
);
310 static HRESULT WINAPI
speech_voice_get_Volume(ISpeechVoice
*iface
, LONG
*volume
)
312 struct speech_voice
*This
= impl_from_ISpeechVoice(iface
);
316 TRACE("(%p, %p).\n", iface
, volume
);
318 if (!volume
) return E_POINTER
;
319 hr
= ISpVoice_GetVolume(&This
->ISpVoice_iface
, &res
);
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
,
336 FIXME("(%p, %d): stub.\n", iface
, allow
);
341 static HRESULT WINAPI
speech_voice_get_AllowAudioOutputFormatChangesOnNextSet(ISpeechVoice
*iface
, VARIANT_BOOL
*allow
)
343 FIXME("(%p, %p): stub.\n", iface
, allow
);
348 static HRESULT WINAPI
speech_voice_get_EventInterests(ISpeechVoice
*iface
, SpeechVoiceEvents
*flags
)
350 FIXME("(%p, %p): stub.\n", iface
, flags
);
355 static HRESULT WINAPI
speech_voice_put_EventInterests(ISpeechVoice
*iface
, SpeechVoiceEvents flags
)
357 FIXME("(%p, %#x): stub.\n", iface
, flags
);
362 static HRESULT WINAPI
speech_voice_put_Priority(ISpeechVoice
*iface
, SpeechVoicePriority priority
)
364 FIXME("(%p, %d): stub.\n", iface
, priority
);
369 static HRESULT WINAPI
speech_voice_get_Priority(ISpeechVoice
*iface
, SpeechVoicePriority
*priority
)
371 FIXME("(%p, %p): stub.\n", iface
, priority
);
376 static HRESULT WINAPI
speech_voice_put_AlertBoundary(ISpeechVoice
*iface
, SpeechVoiceEvents boundary
)
378 FIXME("(%p, %#x): stub.\n", iface
, boundary
);
383 static HRESULT WINAPI
speech_voice_get_AlertBoundary(ISpeechVoice
*iface
, SpeechVoiceEvents
*boundary
)
385 FIXME("(%p, %p): stub.\n", iface
, boundary
);
390 static HRESULT WINAPI
speech_voice_put_SynchronousSpeakTimeout(ISpeechVoice
*iface
, LONG timeout
)
392 FIXME("(%p, %ld): stub.\n", iface
, timeout
);
397 static HRESULT WINAPI
speech_voice_get_SynchronousSpeakTimeout(ISpeechVoice
*iface
, LONG
*timeout
)
399 FIXME("(%p, %p): stub.\n", iface
, timeout
);
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
);
421 static HRESULT WINAPI
speech_voice_Pause(ISpeechVoice
*iface
)
423 FIXME("(%p): stub.\n", iface
);
428 static HRESULT WINAPI
speech_voice_Resume(ISpeechVoice
*iface
)
430 FIXME("(%p): stub.\n", iface
);
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
);
442 static HRESULT WINAPI
speech_voice_GetVoices(ISpeechVoice
*iface
, BSTR required
, BSTR optional
,
443 ISpeechObjectTokens
**tokens
)
446 ISpObjectTokenCategory
*cat
;
447 IEnumSpObjectTokens
*token_enum
;
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
)))
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
);
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
);
475 static HRESULT WINAPI
speech_voice_WaitUntilDone(ISpeechVoice
*iface
, LONG timeout
, VARIANT_BOOL
*done
)
477 FIXME("(%p, %ld, %p): stub.\n", iface
, timeout
, done
);
482 static HRESULT WINAPI
speech_voice_SpeakCompleteEvent(ISpeechVoice
*iface
, LONG
*handle
)
484 FIXME("(%p, %p): stub.\n", iface
, handle
);
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
);
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
);
505 static const ISpeechVoiceVtbl speech_voice_vtbl
=
507 speech_voice_QueryInterface
,
509 speech_voice_Release
,
510 speech_voice_GetTypeInfoCount
,
511 speech_voice_GetTypeInfo
,
512 speech_voice_GetIDsOfNames
,
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
,
536 speech_voice_SpeakStream
,
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
);
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
);
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
);
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
);
607 static HRESULT WINAPI
spvoice_SetNotifyWin32Event(ISpVoice
*iface
)
609 FIXME("(%p): stub.\n", iface
);
614 static HRESULT WINAPI
spvoice_WaitForNotifyEvent(ISpVoice
*iface
, DWORD milliseconds
)
616 FIXME("(%p, %ld): stub.\n", iface
, milliseconds
);
621 static HANDLE WINAPI
spvoice_GetNotifyEventHandle(ISpVoice
*iface
)
623 FIXME("(%p): stub.\n", iface
);
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
));
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
);
642 static HRESULT WINAPI
spvoice_GetInfo(ISpVoice
*iface
, SPEVENTSOURCEINFO
*info
)
644 FIXME("(%p, %p): stub.\n", iface
, info
);
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
;
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
)))
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
)))
673 if (FAILED(IUnknown_QueryInterface(unk
, &IID_ISpStreamFormat
, (void **)&stream
)))
675 if (FAILED(IUnknown_QueryInterface(unk
, &IID_ISpObjectToken
, (void **)&token
)))
682 hr
= ISpObjectToken_CreateInstance(token
, NULL
, CLSCTX_ALL
, &IID_ISpStreamFormat
, (void **)&stream
);
683 ISpObjectToken_Release(token
);
688 EnterCriticalSection(&This
->cs
);
691 ISpStreamFormat_Release(This
->output
);
692 This
->output
= stream
;
694 LeaveCriticalSection(&This
->cs
);
699 static HRESULT WINAPI
spvoice_GetOutputObjectToken(ISpVoice
*iface
, ISpObjectToken
**token
)
701 FIXME("(%p, %p): stub.\n", iface
, token
);
706 static HRESULT WINAPI
spvoice_GetOutputStream(ISpVoice
*iface
, ISpStreamFormat
**stream
)
708 FIXME("(%p, %p): stub.\n", iface
, stream
);
713 static HRESULT WINAPI
spvoice_Pause(ISpVoice
*iface
)
715 FIXME("(%p): stub.\n", iface
);
720 static HRESULT WINAPI
spvoice_Resume(ISpVoice
*iface
)
722 FIXME("(%p): stub.\n", iface
);
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
;
733 TRACE("(%p, %p).\n", iface
, token
);
737 if (FAILED(hr
= create_default_token(SPCAT_VOICES
, &token
)))
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
)) &&
750 ISpObjectToken_Release(token
);
754 if (This
->engine_token
)
755 ISpObjectToken_Release(This
->engine_token
);
756 This
->engine_token
= token
;
760 ISpTTSEngine_Release(This
->engine
);
765 LeaveCriticalSection(&This
->cs
);
767 CoTaskMemFree(old_id
);
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
);
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
);
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
;
814 static HRESULT
set_output_format(ISpStreamFormat
*output
, ISpTTSEngine
*engine
, GUID
*fmtid
, WAVEFORMATEX
**wfx
)
817 WAVEFORMATEX
*output_wfx
= NULL
;
818 ISpAudio
*audio
= NULL
;
821 if (FAILED(hr
= ISpStreamFormat_GetFormat(output
, &output_fmtid
, &output_wfx
)))
823 if (FAILED(hr
= ISpTTSEngine_GetOutputFormat(engine
, &output_fmtid
, output_wfx
, fmtid
, wfx
)))
825 if (!IsEqualGUID(fmtid
, &SPDFID_WaveFormatEx
))
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
)))
840 CoTaskMemFree(output_wfx
);
841 if (audio
) ISpAudio_Release(audio
);
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
;
850 WAVEFORMATEX
*wfx
= NULL
;
851 ISpAudio
*audio
= NULL
;
854 TRACE("(%p).\n", task
);
856 EnterCriticalSection(&This
->cs
);
858 if (This
->actions
& SPVES_ABORT
)
860 LeaveCriticalSection(&This
->cs
);
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
);
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
);
882 ISpStreamFormat_Commit(This
->output
, STGC_DEFAULT
);
884 WaitForSingleObject(ISpAudio_EventHandle(audio
), INFINITE
);
887 WARN("ISpTTSEngine_Speak failed: %#lx.\n", hr
);
892 ISpAudio_SetState(audio
, SPAS_CLOSED
, 0);
893 ISpAudio_Release(audio
);
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
;
915 struct speak_task
*speak_task
= NULL
;
916 struct async_result
*result
= NULL
;
917 size_t contents_len
, contents_size
;
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
));
930 if (flags
& SPF_PURGEBEFORESPEAK
)
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
)
957 contents_len
= wcslen(contents
);
958 contents_size
= sizeof(WCHAR
) * (contents_len
+ 1);
962 /* Create a new output stream with the default output. */
963 if (FAILED(hr
= ISpVoice_SetOutput(iface
, NULL
, TRUE
)))
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
);
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
);
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
);
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
))))
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
);
1036 *stream_num_out
= stream_num
;
1038 if (flags
& SPF_ASYNC
)
1042 WaitForSingleObject(result
->done
, INFINITE
);
1044 CloseHandle(result
->done
);
1050 if (site
) ISpTTSEngineSite_Release(site
);
1051 if (engine
) ISpTTSEngine_Release(engine
);
1056 CloseHandle(result
->done
);
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
);
1069 static HRESULT WINAPI
spvoice_GetStatus(ISpVoice
*iface
, SPVOICESTATUS
*status
, WCHAR
**bookmark
)
1071 static unsigned int once
;
1074 FIXME("(%p, %p, %p): stub.\n", iface
, status
, bookmark
);
1076 WARN("(%p, %p, %p): stub.\n", iface
, status
, bookmark
);
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
);
1088 static HRESULT WINAPI
spvoice_SetPriority(ISpVoice
*iface
, SPVPRIORITY priority
)
1090 FIXME("(%p, %d): stub.\n", iface
, priority
);
1095 static HRESULT WINAPI
spvoice_GetPriority(ISpVoice
*iface
, SPVPRIORITY
*priority
)
1097 FIXME("(%p, %p): stub.\n", iface
, priority
);
1102 static HRESULT WINAPI
spvoice_SetAlertBoundary(ISpVoice
*iface
, SPEVENTENUM boundary
)
1104 FIXME("(%p, %d): stub.\n", iface
, boundary
);
1109 static HRESULT WINAPI
spvoice_GetAlertBoundary(ISpVoice
*iface
, SPEVENTENUM
*boundary
)
1111 FIXME("(%p, %p): stub.\n", iface
, boundary
);
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
);
1124 This
->actions
|= SPVES_RATE
;
1125 LeaveCriticalSection(&This
->cs
);
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
);
1138 LeaveCriticalSection(&This
->cs
);
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
);
1150 return E_INVALIDARG
;
1152 EnterCriticalSection(&This
->cs
);
1153 This
->volume
= volume
;
1154 This
->actions
|= SPVES_VOLUME
;
1155 LeaveCriticalSection(&This
->cs
);
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
);
1173 static HRESULT WINAPI
spvoice_WaitUntilDone(ISpVoice
*iface
, ULONG timeout
)
1175 struct speech_voice
*This
= impl_from_ISpVoice(iface
);
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
;
1187 static HRESULT WINAPI
spvoice_SetSyncSpeakTimeout(ISpVoice
*iface
, ULONG timeout
)
1189 FIXME("(%p, %ld): stub.\n", iface
, timeout
);
1194 static HRESULT WINAPI
spvoice_GetSyncSpeakTimeout(ISpVoice
*iface
, ULONG
*timeout
)
1196 FIXME("(%p, %p): stub.\n", iface
, timeout
);
1201 static HANDLE WINAPI
spvoice_SpeakCompleteEvent(ISpVoice
*iface
)
1203 FIXME("(%p): stub.\n", iface
);
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
);
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
);
1224 static const ISpVoiceVtbl spvoice_vtbl
=
1226 spvoice_QueryInterface
,
1229 spvoice_SetNotifySink
,
1230 spvoice_SetNotifyWindowMessage
,
1231 spvoice_SetNotifyCallbackFunction
,
1232 spvoice_SetNotifyCallbackInterface
,
1233 spvoice_SetNotifyWin32Event
,
1234 spvoice_WaitForNotifyEvent
,
1235 spvoice_GetNotifyEventHandle
,
1236 spvoice_SetInterest
,
1240 spvoice_GetOutputObjectToken
,
1241 spvoice_GetOutputStream
,
1247 spvoice_SpeakStream
,
1250 spvoice_SetPriority
,
1251 spvoice_GetPriority
,
1252 spvoice_SetAlertBoundary
,
1253 spvoice_GetAlertBoundary
,
1258 spvoice_WaitUntilDone
,
1259 spvoice_SetSyncSpeakTimeout
,
1260 spvoice_GetSyncSpeakTimeout
,
1261 spvoice_SpeakCompleteEvent
,
1262 spvoice_IsUISupported
,
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
;
1279 FIXME("interface %s not implemented.\n", debugstr_guid(iid
));
1280 return E_NOINTERFACE
;
1283 IUnknown_AddRef((IUnknown
*)*obj
);
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
);
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
);
1308 ISpeechVoice_Release(&This
->voice
->ISpeechVoice_iface
);
1315 static HRESULT WINAPI
ttsenginesite_AddEvents(ISpTTSEngineSite
*iface
, const SPEVENT
*events
, ULONG count
)
1317 FIXME("(%p, %p, %ld): stub.\n", iface
, events
, count
);
1322 static HRESULT WINAPI
ttsenginesite_GetEventInterest(ISpTTSEngineSite
*iface
, ULONGLONG
*interest
)
1324 FIXME("(%p, %p): stub.\n", iface
, interest
);
1329 static DWORD WINAPI
ttsenginesite_GetActions(ISpTTSEngineSite
*iface
)
1331 struct tts_engine_site
*This
= impl_from_ISpTTSEngineSite(iface
);
1334 TRACE("(%p).\n", iface
);
1336 EnterCriticalSection(&This
->voice
->cs
);
1337 actions
= This
->voice
->actions
;
1338 LeaveCriticalSection(&This
->voice
->cs
);
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
);
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
);
1383 static HRESULT WINAPI
ttsenginesite_GetSkipInfo(ISpTTSEngineSite
*iface
, SPVSKIPTYPE
*type
, LONG
*skip_count
)
1385 FIXME("(%p, %p, %p): stub.\n", iface
, type
, skip_count
);
1390 static HRESULT WINAPI
ttsenginesite_CompleteSkip(ISpTTSEngineSite
*iface
, LONG num_skipped
)
1392 FIXME("(%p, %ld): stub.\n", iface
, num_skipped
);
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
;
1421 This
->voice
= voice
;
1422 This
->stream_num
= stream_num
;
1424 ISpeechVoice_AddRef(&This
->voice
->ISpeechVoice_iface
);
1426 *site
= &This
->ISpTTSEngineSite_iface
;
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
);
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
);
1475 const static IConnectionPointContainerVtbl container_vtbl
=
1477 container_QueryInterface
,
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
));
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
;
1495 This
->output
= NULL
;
1496 This
->engine_token
= NULL
;
1497 This
->engine
= NULL
;
1498 This
->cur_stream_num
= 0;
1499 This
->actions
= SPVES_CONTINUE
;
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
);