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
;
51 struct async_queue queue
;
55 static inline struct speech_voice
*impl_from_ISpeechVoice(ISpeechVoice
*iface
)
57 return CONTAINING_RECORD(iface
, struct speech_voice
, ISpeechVoice_iface
);
60 static inline struct speech_voice
*impl_from_ISpVoice(ISpVoice
*iface
)
62 return CONTAINING_RECORD(iface
, struct speech_voice
, ISpVoice_iface
);
65 static inline struct speech_voice
*impl_from_IConnectionPointContainer(IConnectionPointContainer
*iface
)
67 return CONTAINING_RECORD(iface
, struct speech_voice
, IConnectionPointContainer_iface
);
70 struct tts_engine_site
72 ISpTTSEngineSite ISpTTSEngineSite_iface
;
75 struct speech_voice
*voice
;
79 static inline struct tts_engine_site
*impl_from_ISpTTSEngineSite(ISpTTSEngineSite
*iface
)
81 return CONTAINING_RECORD(iface
, struct tts_engine_site
, ISpTTSEngineSite_iface
);
84 static HRESULT
create_default_token(const WCHAR
*cat_id
, ISpObjectToken
**token
)
86 ISpObjectTokenCategory
*cat
;
87 WCHAR
*default_token_id
= NULL
;
90 TRACE("(%s, %p).\n", debugstr_w(cat_id
), token
);
92 if (FAILED(hr
= CoCreateInstance(&CLSID_SpObjectTokenCategory
, NULL
, CLSCTX_INPROC_SERVER
,
93 &IID_ISpObjectTokenCategory
, (void **)&cat
)))
96 if (FAILED(hr
= ISpObjectTokenCategory_SetId(cat
, cat_id
, FALSE
)) ||
97 FAILED(hr
= ISpObjectTokenCategory_GetDefaultTokenId(cat
, &default_token_id
)))
99 ISpObjectTokenCategory_Release(cat
);
102 ISpObjectTokenCategory_Release(cat
);
104 if (FAILED(hr
= CoCreateInstance(&CLSID_SpObjectToken
, NULL
, CLSCTX_INPROC_SERVER
,
105 &IID_ISpObjectToken
, (void **)token
)))
108 if (FAILED(hr
= ISpObjectToken_SetId(*token
, NULL
, default_token_id
, FALSE
)))
110 ISpObjectToken_Release(*token
);
115 CoTaskMemFree(default_token_id
);
119 /* ISpeechVoice interface */
120 static HRESULT WINAPI
speech_voice_QueryInterface(ISpeechVoice
*iface
, REFIID iid
, void **obj
)
122 struct speech_voice
*This
= impl_from_ISpeechVoice(iface
);
124 TRACE("(%p, %s %p).\n", iface
, debugstr_guid(iid
), obj
);
126 if (IsEqualIID(iid
, &IID_IUnknown
) ||
127 IsEqualIID(iid
, &IID_IDispatch
) ||
128 IsEqualIID(iid
, &IID_ISpeechVoice
))
129 *obj
= &This
->ISpeechVoice_iface
;
130 else if (IsEqualIID(iid
, &IID_ISpVoice
))
131 *obj
= &This
->ISpVoice_iface
;
132 else if (IsEqualIID(iid
, &IID_IConnectionPointContainer
))
133 *obj
= &This
->IConnectionPointContainer_iface
;
137 FIXME("interface %s not implemented.\n", debugstr_guid(iid
));
138 return E_NOINTERFACE
;
141 IUnknown_AddRef((IUnknown
*)*obj
);
145 static ULONG WINAPI
speech_voice_AddRef(ISpeechVoice
*iface
)
147 struct speech_voice
*This
= impl_from_ISpeechVoice(iface
);
148 ULONG ref
= InterlockedIncrement(&This
->ref
);
150 TRACE("(%p): ref=%lu.\n", iface
, ref
);
155 static ULONG WINAPI
speech_voice_Release(ISpeechVoice
*iface
)
157 struct speech_voice
*This
= impl_from_ISpeechVoice(iface
);
158 ULONG ref
= InterlockedDecrement(&This
->ref
);
160 TRACE("(%p): ref=%lu.\n", iface
, ref
);
164 async_cancel_queue(&This
->queue
);
165 if (This
->output
) ISpStreamFormat_Release(This
->output
);
166 if (This
->engine
) ISpTTSEngine_Release(This
->engine
);
167 DeleteCriticalSection(&This
->cs
);
175 static HRESULT WINAPI
speech_voice_GetTypeInfoCount(ISpeechVoice
*iface
, UINT
*info
)
177 FIXME("(%p, %p): stub.\n", iface
, info
);
182 static HRESULT WINAPI
speech_voice_GetTypeInfo(ISpeechVoice
*iface
, UINT info
, LCID lcid
,
183 ITypeInfo
**type_info
)
185 FIXME("(%p, %u, %lu, %p): stub.\n", iface
, info
, lcid
, type_info
);
190 static HRESULT WINAPI
speech_voice_GetIDsOfNames(ISpeechVoice
*iface
, REFIID riid
, LPOLESTR
*names
,
191 UINT count
, LCID lcid
, DISPID
*dispid
)
193 FIXME("(%p, %s, %p, %u, %lu, %p): stub.\n", iface
, debugstr_guid(riid
), names
, count
, lcid
, dispid
);
198 static HRESULT WINAPI
speech_voice_Invoke(ISpeechVoice
*iface
, DISPID dispid
, REFIID riid
, LCID lcid
,
199 WORD flags
, DISPPARAMS
*params
, VARIANT
*result
,
200 EXCEPINFO
*excepinfo
, UINT
*argerr
)
202 FIXME("(%p, %ld, %s, %#lx, %#x, %p, %p, %p, %p): stub.\n", iface
, dispid
, debugstr_guid(riid
),
203 lcid
, flags
, params
, result
, excepinfo
, argerr
);
208 static HRESULT WINAPI
speech_voice_get_Status(ISpeechVoice
*iface
, ISpeechVoiceStatus
**status
)
210 FIXME("(%p, %p): stub.\n", iface
, status
);
215 static HRESULT WINAPI
speech_voice_get_Voice(ISpeechVoice
*iface
, ISpeechObjectToken
**voice
)
217 FIXME("(%p, %p): stub.\n", iface
, voice
);
222 static HRESULT WINAPI
speech_voice_putref_Voice(ISpeechVoice
*iface
, ISpeechObjectToken
*voice
)
224 FIXME("(%p, %p): stub.\n", iface
, voice
);
229 static HRESULT WINAPI
speech_voice_get_AudioOutput(ISpeechVoice
*iface
, ISpeechObjectToken
**output
)
231 FIXME("(%p, %p): stub.\n", iface
, output
);
236 static HRESULT WINAPI
speech_voice_putref_AudioOutput(ISpeechVoice
*iface
, ISpeechObjectToken
*output
)
238 FIXME("(%p, %p): stub.\n", iface
, output
);
243 static HRESULT WINAPI
speech_voice_get_AudioOutputStream(ISpeechVoice
*iface
, ISpeechBaseStream
**output
)
245 FIXME("(%p, %p): stub.\n", iface
, output
);
250 static HRESULT WINAPI
speech_voice_putref_AudioOutputStream(ISpeechVoice
*iface
, ISpeechBaseStream
*output
)
252 FIXME("(%p, %p): stub.\n", iface
, output
);
257 static HRESULT WINAPI
speech_voice_get_Rate(ISpeechVoice
*iface
, LONG
*rate
)
259 FIXME("(%p, %p): stub.\n", iface
, rate
);
264 static HRESULT WINAPI
speech_voice_put_Rate(ISpeechVoice
*iface
, LONG rate
)
266 FIXME("(%p, %ld): stub.\n", iface
, rate
);
271 static HRESULT WINAPI
speech_voice_get_Volume(ISpeechVoice
*iface
, LONG
*volume
)
273 FIXME("(%p, %p): stub.\n", iface
, volume
);
278 static HRESULT WINAPI
speech_voice_put_Volume(ISpeechVoice
*iface
, LONG volume
)
280 FIXME("(%p, %ld): stub.\n", iface
, volume
);
285 static HRESULT WINAPI
speech_voice_put_AllowAudioOutputFormatChangesOnNextSet(ISpeechVoice
*iface
,
288 FIXME("(%p, %d): stub.\n", iface
, allow
);
293 static HRESULT WINAPI
speech_voice_get_AllowAudioOutputFormatChangesOnNextSet(ISpeechVoice
*iface
, VARIANT_BOOL
*allow
)
295 FIXME("(%p, %p): stub.\n", iface
, allow
);
300 static HRESULT WINAPI
speech_voice_get_EventInterests(ISpeechVoice
*iface
, SpeechVoiceEvents
*flags
)
302 FIXME("(%p, %p): stub.\n", iface
, flags
);
307 static HRESULT WINAPI
speech_voice_put_EventInterests(ISpeechVoice
*iface
, SpeechVoiceEvents flags
)
309 FIXME("(%p, %#x): stub.\n", iface
, flags
);
314 static HRESULT WINAPI
speech_voice_put_Priority(ISpeechVoice
*iface
, SpeechVoicePriority priority
)
316 FIXME("(%p, %d): stub.\n", iface
, priority
);
321 static HRESULT WINAPI
speech_voice_get_Priority(ISpeechVoice
*iface
, SpeechVoicePriority
*priority
)
323 FIXME("(%p, %p): stub.\n", iface
, priority
);
328 static HRESULT WINAPI
speech_voice_put_AlertBoundary(ISpeechVoice
*iface
, SpeechVoiceEvents boundary
)
330 FIXME("(%p, %#x): stub.\n", iface
, boundary
);
335 static HRESULT WINAPI
speech_voice_get_AlertBoundary(ISpeechVoice
*iface
, SpeechVoiceEvents
*boundary
)
337 FIXME("(%p, %p): stub.\n", iface
, boundary
);
342 static HRESULT WINAPI
speech_voice_put_SynchronousSpeakTimeout(ISpeechVoice
*iface
, LONG timeout
)
344 FIXME("(%p, %ld): stub.\n", iface
, timeout
);
349 static HRESULT WINAPI
speech_voice_get_SynchronousSpeakTimeout(ISpeechVoice
*iface
, LONG
*timeout
)
351 FIXME("(%p, %p): stub.\n", iface
, timeout
);
356 static HRESULT WINAPI
speech_voice_Speak(ISpeechVoice
*iface
, BSTR text
, SpeechVoiceSpeakFlags flags
, LONG
*number
)
358 FIXME("(%p, %s, %#x, %p): stub.\n", iface
, debugstr_w(text
), flags
, number
);
363 static HRESULT WINAPI
speech_voice_SpeakStream(ISpeechVoice
*iface
, ISpeechBaseStream
*stream
,
364 SpeechVoiceSpeakFlags flags
, LONG
*number
)
366 FIXME("(%p, %p, %#x, %p): stub.\n", iface
, stream
, flags
, number
);
371 static HRESULT WINAPI
speech_voice_Pause(ISpeechVoice
*iface
)
373 FIXME("(%p): stub.\n", iface
);
378 static HRESULT WINAPI
speech_voice_Resume(ISpeechVoice
*iface
)
380 FIXME("(%p): stub.\n", iface
);
385 static HRESULT WINAPI
speech_voice_Skip(ISpeechVoice
*iface
, const BSTR type
, LONG items
, LONG
*skipped
)
387 FIXME("(%p, %s, %ld, %p): stub.\n", iface
, debugstr_w(type
), items
, skipped
);
392 static HRESULT WINAPI
speech_voice_GetVoices(ISpeechVoice
*iface
, BSTR required
, BSTR optional
,
393 ISpeechObjectTokens
**tokens
)
395 FIXME("(%p, %s, %s, %p): stub.\n", iface
, debugstr_w(required
), debugstr_w(optional
), tokens
);
400 static HRESULT WINAPI
speech_voice_GetAudioOutputs(ISpeechVoice
*iface
, BSTR required
, BSTR optional
,
401 ISpeechObjectTokens
**tokens
)
403 FIXME("(%p, %s, %s, %p): stub.\n", iface
, debugstr_w(required
), debugstr_w(optional
), tokens
);
408 static HRESULT WINAPI
speech_voice_WaitUntilDone(ISpeechVoice
*iface
, LONG timeout
, VARIANT_BOOL
*done
)
410 FIXME("(%p, %ld, %p): stub.\n", iface
, timeout
, done
);
415 static HRESULT WINAPI
speech_voice_SpeakCompleteEvent(ISpeechVoice
*iface
, LONG
*handle
)
417 FIXME("(%p, %p): stub.\n", iface
, handle
);
422 static HRESULT WINAPI
speech_voice_IsUISupported(ISpeechVoice
*iface
, const BSTR typeui
, const VARIANT
*data
,
423 VARIANT_BOOL
*supported
)
425 FIXME("(%p, %s, %p, %p): stub.\n", iface
, debugstr_w(typeui
), data
, supported
);
430 static HRESULT WINAPI
speech_voice_DisplayUI(ISpeechVoice
*iface
, LONG hwnd
, BSTR title
,
431 const BSTR typeui
, const VARIANT
*data
)
433 FIXME("(%p, %ld, %s, %s, %p): stub.\n", iface
, hwnd
, debugstr_w(title
), debugstr_w(typeui
), data
);
438 static const ISpeechVoiceVtbl speech_voice_vtbl
=
440 speech_voice_QueryInterface
,
442 speech_voice_Release
,
443 speech_voice_GetTypeInfoCount
,
444 speech_voice_GetTypeInfo
,
445 speech_voice_GetIDsOfNames
,
447 speech_voice_get_Status
,
448 speech_voice_get_Voice
,
449 speech_voice_putref_Voice
,
450 speech_voice_get_AudioOutput
,
451 speech_voice_putref_AudioOutput
,
452 speech_voice_get_AudioOutputStream
,
453 speech_voice_putref_AudioOutputStream
,
454 speech_voice_get_Rate
,
455 speech_voice_put_Rate
,
456 speech_voice_get_Volume
,
457 speech_voice_put_Volume
,
458 speech_voice_put_AllowAudioOutputFormatChangesOnNextSet
,
459 speech_voice_get_AllowAudioOutputFormatChangesOnNextSet
,
460 speech_voice_get_EventInterests
,
461 speech_voice_put_EventInterests
,
462 speech_voice_put_Priority
,
463 speech_voice_get_Priority
,
464 speech_voice_put_AlertBoundary
,
465 speech_voice_get_AlertBoundary
,
466 speech_voice_put_SynchronousSpeakTimeout
,
467 speech_voice_get_SynchronousSpeakTimeout
,
469 speech_voice_SpeakStream
,
473 speech_voice_GetVoices
,
474 speech_voice_GetAudioOutputs
,
475 speech_voice_WaitUntilDone
,
476 speech_voice_SpeakCompleteEvent
,
477 speech_voice_IsUISupported
,
478 speech_voice_DisplayUI
,
481 /* ISpVoice interface */
482 static HRESULT WINAPI
spvoice_QueryInterface(ISpVoice
*iface
, REFIID iid
, void **obj
)
484 struct speech_voice
*This
= impl_from_ISpVoice(iface
);
486 TRACE("(%p, %s %p).\n", iface
, debugstr_guid(iid
), obj
);
488 return ISpeechVoice_QueryInterface(&This
->ISpeechVoice_iface
, iid
, obj
);
491 static ULONG WINAPI
spvoice_AddRef(ISpVoice
*iface
)
493 struct speech_voice
*This
= impl_from_ISpVoice(iface
);
495 TRACE("(%p).\n", iface
);
497 return ISpeechVoice_AddRef(&This
->ISpeechVoice_iface
);
500 static ULONG WINAPI
spvoice_Release(ISpVoice
*iface
)
502 struct speech_voice
*This
= impl_from_ISpVoice(iface
);
504 TRACE("(%p).\n", iface
);
506 return ISpeechVoice_Release(&This
->ISpeechVoice_iface
);
509 static HRESULT WINAPI
spvoice_SetNotifySink(ISpVoice
*iface
, ISpNotifySink
*sink
)
511 FIXME("(%p, %p): stub.\n", iface
, sink
);
516 static HRESULT WINAPI
spvoice_SetNotifyWindowMessage(ISpVoice
*iface
, HWND hwnd
, UINT msg
,
517 WPARAM wparam
, LPARAM lparam
)
519 FIXME("(%p, %p, %u, %Ix, %Ix): stub.\n", iface
, hwnd
, msg
, wparam
, lparam
);
524 static HRESULT WINAPI
spvoice_SetNotifyCallbackFunction(ISpVoice
*iface
, SPNOTIFYCALLBACK
*callback
,
525 WPARAM wparam
, LPARAM lparam
)
527 FIXME("(%p, %p, %Ix, %Ix): stub.\n", iface
, callback
, wparam
, lparam
);
532 static HRESULT WINAPI
spvoice_SetNotifyCallbackInterface(ISpVoice
*iface
, ISpNotifyCallback
*callback
,
533 WPARAM wparam
, LPARAM lparam
)
535 FIXME("(%p, %p, %Ix, %Ix): stub.\n", iface
, callback
, wparam
, lparam
);
540 static HRESULT WINAPI
spvoice_SetNotifyWin32Event(ISpVoice
*iface
)
542 FIXME("(%p): stub.\n", iface
);
547 static HRESULT WINAPI
spvoice_WaitForNotifyEvent(ISpVoice
*iface
, DWORD milliseconds
)
549 FIXME("(%p, %ld): stub.\n", iface
, milliseconds
);
554 static HANDLE WINAPI
spvoice_GetNotifyEventHandle(ISpVoice
*iface
)
556 FIXME("(%p): stub.\n", iface
);
561 static HRESULT WINAPI
spvoice_SetInterest(ISpVoice
*iface
, ULONGLONG event
, ULONGLONG queued
)
563 FIXME("(%p, %s, %s): stub.\n", iface
, wine_dbgstr_longlong(event
), wine_dbgstr_longlong(queued
));
568 static HRESULT WINAPI
spvoice_GetEvents(ISpVoice
*iface
, ULONG count
, SPEVENT
*array
, ULONG
*fetched
)
570 FIXME("(%p, %lu, %p, %p): stub.\n", iface
, count
, array
, fetched
);
575 static HRESULT WINAPI
spvoice_GetInfo(ISpVoice
*iface
, SPEVENTSOURCEINFO
*info
)
577 FIXME("(%p, %p): stub.\n", iface
, info
);
582 static HRESULT WINAPI
spvoice_SetOutput(ISpVoice
*iface
, IUnknown
*unk
, BOOL allow_format_changes
)
584 struct speech_voice
*This
= impl_from_ISpVoice(iface
);
585 ISpStreamFormat
*stream
= NULL
;
586 ISpObjectToken
*token
= NULL
;
589 TRACE("(%p, %p, %d).\n", iface
, unk
, allow_format_changes
);
591 if (!allow_format_changes
)
592 FIXME("ignoring allow_format_changes = FALSE.\n");
594 if (FAILED(hr
= async_start_queue(&This
->queue
)))
599 /* TODO: Create the default SpAudioOut token here once SpMMAudioEnum is implemented. */
600 if (FAILED(hr
= CoCreateInstance(&CLSID_SpMMAudioOut
, NULL
, CLSCTX_INPROC_SERVER
,
601 &IID_ISpStreamFormat
, (void **)&stream
)))
606 if (FAILED(IUnknown_QueryInterface(unk
, &IID_ISpStreamFormat
, (void **)&stream
)))
608 if (FAILED(IUnknown_QueryInterface(unk
, &IID_ISpObjectToken
, (void **)&token
)))
615 hr
= ISpObjectToken_CreateInstance(token
, NULL
, CLSCTX_ALL
, &IID_ISpStreamFormat
, (void **)&stream
);
616 ISpObjectToken_Release(token
);
621 EnterCriticalSection(&This
->cs
);
624 ISpStreamFormat_Release(This
->output
);
625 This
->output
= stream
;
627 LeaveCriticalSection(&This
->cs
);
632 static HRESULT WINAPI
spvoice_GetOutputObjectToken(ISpVoice
*iface
, ISpObjectToken
**token
)
634 FIXME("(%p, %p): stub.\n", iface
, token
);
639 static HRESULT WINAPI
spvoice_GetOutputStream(ISpVoice
*iface
, ISpStreamFormat
**stream
)
641 FIXME("(%p, %p): stub.\n", iface
, stream
);
646 static HRESULT WINAPI
spvoice_Pause(ISpVoice
*iface
)
648 FIXME("(%p): stub.\n", iface
);
653 static HRESULT WINAPI
spvoice_Resume(ISpVoice
*iface
)
655 FIXME("(%p): stub.\n", iface
);
660 static HRESULT WINAPI
spvoice_SetVoice(ISpVoice
*iface
, ISpObjectToken
*token
)
662 struct speech_voice
*This
= impl_from_ISpVoice(iface
);
663 ISpTTSEngine
*engine
;
666 TRACE("(%p, %p).\n", iface
, token
);
670 if (FAILED(hr
= create_default_token(SPCAT_VOICES
, &token
)))
674 ISpObjectToken_AddRef(token
);
676 hr
= ISpObjectToken_CreateInstance(token
, NULL
, CLSCTX_ALL
, &IID_ISpTTSEngine
, (void **)&engine
);
677 ISpObjectToken_Release(token
);
681 EnterCriticalSection(&This
->cs
);
684 ISpTTSEngine_Release(This
->engine
);
685 This
->engine
= engine
;
687 LeaveCriticalSection(&This
->cs
);
692 static HRESULT WINAPI
spvoice_GetVoice(ISpVoice
*iface
, ISpObjectToken
**token
)
694 struct speech_voice
*This
= impl_from_ISpVoice(iface
);
695 ISpObjectWithToken
*engine_token_iface
;
698 TRACE("(%p, %p).\n", iface
, token
);
703 EnterCriticalSection(&This
->cs
);
707 LeaveCriticalSection(&This
->cs
);
708 return create_default_token(SPCAT_VOICES
, token
);
711 if (SUCCEEDED(hr
= ISpTTSEngine_QueryInterface(This
->engine
, &IID_ISpObjectWithToken
, (void **)&engine_token_iface
)))
713 hr
= ISpObjectWithToken_GetObjectToken(engine_token_iface
, token
);
714 ISpObjectWithToken_Release(engine_token_iface
);
717 LeaveCriticalSection(&This
->cs
);
730 struct async_task task
;
731 struct async_result
*result
;
733 struct speech_voice
*voice
;
734 SPVTEXTFRAG
*frag_list
;
735 ISpTTSEngineSite
*site
;
739 static HRESULT
set_output_format(ISpStreamFormat
*output
, ISpTTSEngine
*engine
, GUID
*fmtid
, WAVEFORMATEX
**wfx
)
742 WAVEFORMATEX
*output_wfx
= NULL
;
743 ISpAudio
*audio
= NULL
;
746 if (FAILED(hr
= ISpStreamFormat_GetFormat(output
, &output_fmtid
, &output_wfx
)))
748 if (FAILED(hr
= ISpTTSEngine_GetOutputFormat(engine
, &output_fmtid
, output_wfx
, fmtid
, wfx
)))
750 if (!IsEqualGUID(fmtid
, &SPDFID_WaveFormatEx
))
756 if (memcmp(output_wfx
, *wfx
, sizeof(WAVEFORMATEX
)) ||
757 memcmp(output_wfx
+ 1, *wfx
+ 1, output_wfx
->cbSize
))
759 if (FAILED(hr
= ISpStreamFormat_QueryInterface(output
, &IID_ISpAudio
, (void **)&audio
)) ||
760 FAILED(hr
= ISpAudio_SetFormat(audio
, &SPDFID_WaveFormatEx
, *wfx
)))
765 CoTaskMemFree(output_wfx
);
766 if (audio
) ISpAudio_Release(audio
);
770 static void speak_proc(struct async_task
*task
)
772 struct speak_task
*speak_task
= (struct speak_task
*)task
;
773 struct speech_voice
*This
= speak_task
->voice
;
775 WAVEFORMATEX
*wfx
= NULL
;
776 ISpTTSEngine
*engine
= NULL
;
777 ISpAudio
*audio
= NULL
;
780 TRACE("(%p).\n", task
);
782 EnterCriticalSection(&This
->cs
);
784 if (This
->actions
& SPVES_ABORT
)
786 LeaveCriticalSection(&This
->cs
);
791 if (FAILED(hr
= set_output_format(This
->output
, This
->engine
, &fmtid
, &wfx
)))
793 LeaveCriticalSection(&This
->cs
);
794 ERR("failed setting output format: %#lx.\n", hr
);
797 engine
= This
->engine
;
798 ISpTTSEngine_AddRef(engine
);
800 if (SUCCEEDED(ISpStreamFormat_QueryInterface(This
->output
, &IID_ISpAudio
, (void **)&audio
)))
801 ISpAudio_SetState(audio
, SPAS_RUN
, 0);
803 This
->actions
= SPVES_RATE
| SPVES_VOLUME
;
805 LeaveCriticalSection(&This
->cs
);
807 hr
= ISpTTSEngine_Speak(engine
, speak_task
->flags
, &fmtid
, wfx
, speak_task
->frag_list
, speak_task
->site
);
810 ISpStreamFormat_Commit(This
->output
, STGC_DEFAULT
);
812 WaitForSingleObject(ISpAudio_EventHandle(audio
), INFINITE
);
815 WARN("ISpTTSEngine_Speak failed: %#lx.\n", hr
);
820 ISpAudio_SetState(audio
, SPAS_CLOSED
, 0);
821 ISpAudio_Release(audio
);
824 if (engine
) ISpTTSEngine_Release(engine
);
825 heap_free(speak_task
->frag_list
);
826 ISpTTSEngineSite_Release(speak_task
->site
);
828 if (speak_task
->result
)
830 speak_task
->result
->hr
= hr
;
831 SetEvent(speak_task
->result
->done
);
835 static HRESULT
ttsenginesite_create(struct speech_voice
*voice
, ULONG stream_num
, ISpTTSEngineSite
**site
);
837 static HRESULT WINAPI
spvoice_Speak(ISpVoice
*iface
, const WCHAR
*contents
, DWORD flags
, ULONG
*stream_num_out
)
839 struct speech_voice
*This
= impl_from_ISpVoice(iface
);
840 ISpTTSEngineSite
*site
= NULL
;
842 struct speak_task
*speak_task
= NULL
;
843 struct async_result
*result
= NULL
;
844 size_t contents_len
, contents_size
;
848 TRACE("(%p, %p, %#lx, %p).\n", iface
, contents
, flags
, stream_num_out
);
850 flags
&= ~SPF_IS_NOT_XML
;
851 if (flags
& ~(SPF_ASYNC
| SPF_PURGEBEFORESPEAK
| SPF_NLP_SPEAK_PUNC
))
853 FIXME("flags %#lx not implemented.\n", flags
& ~(SPF_ASYNC
| SPF_PURGEBEFORESPEAK
| SPF_NLP_SPEAK_PUNC
));
857 if (flags
& SPF_PURGEBEFORESPEAK
)
861 EnterCriticalSection(&This
->cs
);
863 This
->actions
= SPVES_ABORT
;
864 if (This
->output
&& SUCCEEDED(ISpStreamFormat_QueryInterface(This
->output
, &IID_ISpAudio
, (void **)&audio
)))
866 ISpAudio_SetState(audio
, SPAS_CLOSED
, 0);
867 ISpAudio_Release(audio
);
870 LeaveCriticalSection(&This
->cs
);
872 async_empty_queue(&This
->queue
);
874 EnterCriticalSection(&This
->cs
);
875 This
->actions
= SPVES_CONTINUE
;
876 LeaveCriticalSection(&This
->cs
);
878 if (!contents
|| !*contents
)
884 contents_len
= wcslen(contents
);
885 contents_size
= sizeof(WCHAR
) * (contents_len
+ 1);
889 /* Create a new output stream with the default output. */
890 if (FAILED(hr
= ISpVoice_SetOutput(iface
, NULL
, TRUE
)))
896 /* Create a new engine with the default voice. */
897 if (FAILED(hr
= ISpVoice_SetVoice(iface
, NULL
)))
901 if (!(frag
= heap_alloc(sizeof(*frag
) + contents_size
)))
902 return E_OUTOFMEMORY
;
903 memset(frag
, 0, sizeof(*frag
));
904 memcpy(frag
+ 1, contents
, contents_size
);
905 frag
->State
.eAction
= SPVA_Speak
;
906 frag
->State
.Volume
= 100;
907 frag
->pTextStart
= (WCHAR
*)(frag
+ 1);
908 frag
->ulTextLen
= contents_len
;
909 frag
->ulTextSrcOffset
= 0;
911 stream_num
= InterlockedIncrement(&This
->cur_stream_num
);
912 if (FAILED(hr
= ttsenginesite_create(This
, stream_num
, &site
)))
914 FIXME("Failed to create ttsenginesite: %#lx.\n", hr
);
918 speak_task
= heap_alloc(sizeof(*speak_task
));
920 speak_task
->task
.proc
= speak_proc
;
921 speak_task
->result
= NULL
;
922 speak_task
->voice
= This
;
923 speak_task
->frag_list
= frag
;
924 speak_task
->site
= site
;
925 speak_task
->flags
= flags
& SPF_NLP_SPEAK_PUNC
;
927 if (!(flags
& SPF_ASYNC
))
929 if (!(result
= heap_alloc(sizeof(*result
))))
935 result
->done
= CreateEventW(NULL
, FALSE
, FALSE
, NULL
);
936 speak_task
->result
= result
;
939 if (FAILED(hr
= async_queue_task(&This
->queue
, (struct async_task
*)speak_task
)))
941 WARN("Failed to queue task: %#lx.\n", hr
);
946 *stream_num_out
= stream_num
;
948 if (flags
& SPF_ASYNC
)
952 WaitForSingleObject(result
->done
, INFINITE
);
954 CloseHandle(result
->done
);
960 if (site
) ISpTTSEngineSite_Release(site
);
962 heap_free(speak_task
);
965 CloseHandle(result
->done
);
971 static HRESULT WINAPI
spvoice_SpeakStream(ISpVoice
*iface
, IStream
*stream
, DWORD flags
, ULONG
*number
)
973 FIXME("(%p, %p, %#lx, %p): stub.\n", iface
, stream
, flags
, number
);
978 static HRESULT WINAPI
spvoice_GetStatus(ISpVoice
*iface
, SPVOICESTATUS
*status
, WCHAR
**bookmark
)
980 FIXME("(%p, %p, %p): stub.\n", iface
, status
, bookmark
);
985 static HRESULT WINAPI
spvoice_Skip(ISpVoice
*iface
, const WCHAR
*type
, LONG items
, ULONG
*skipped
)
987 FIXME("(%p, %s, %ld, %p): stub.\n", iface
, debugstr_w(type
), items
, skipped
);
992 static HRESULT WINAPI
spvoice_SetPriority(ISpVoice
*iface
, SPVPRIORITY priority
)
994 FIXME("(%p, %d): stub.\n", iface
, priority
);
999 static HRESULT WINAPI
spvoice_GetPriority(ISpVoice
*iface
, SPVPRIORITY
*priority
)
1001 FIXME("(%p, %p): stub.\n", iface
, priority
);
1006 static HRESULT WINAPI
spvoice_SetAlertBoundary(ISpVoice
*iface
, SPEVENTENUM boundary
)
1008 FIXME("(%p, %d): stub.\n", iface
, boundary
);
1013 static HRESULT WINAPI
spvoice_GetAlertBoundary(ISpVoice
*iface
, SPEVENTENUM
*boundary
)
1015 FIXME("(%p, %p): stub.\n", iface
, boundary
);
1020 static HRESULT WINAPI
spvoice_SetRate(ISpVoice
*iface
, LONG rate
)
1022 struct speech_voice
*This
= impl_from_ISpVoice(iface
);
1024 TRACE("(%p, %ld).\n", iface
, rate
);
1026 EnterCriticalSection(&This
->cs
);
1028 This
->actions
|= SPVES_RATE
;
1029 LeaveCriticalSection(&This
->cs
);
1034 static HRESULT WINAPI
spvoice_GetRate(ISpVoice
*iface
, LONG
*rate
)
1036 struct speech_voice
*This
= impl_from_ISpVoice(iface
);
1038 TRACE("(%p, %p).\n", iface
, rate
);
1040 EnterCriticalSection(&This
->cs
);
1042 LeaveCriticalSection(&This
->cs
);
1047 static HRESULT WINAPI
spvoice_SetVolume(ISpVoice
*iface
, USHORT volume
)
1049 struct speech_voice
*This
= impl_from_ISpVoice(iface
);
1051 TRACE("(%p, %d).\n", iface
, volume
);
1054 return E_INVALIDARG
;
1056 EnterCriticalSection(&This
->cs
);
1057 This
->volume
= volume
;
1058 This
->actions
|= SPVES_VOLUME
;
1059 LeaveCriticalSection(&This
->cs
);
1064 static HRESULT WINAPI
spvoice_GetVolume(ISpVoice
*iface
, USHORT
*volume
)
1066 struct speech_voice
*This
= impl_from_ISpVoice(iface
);
1068 TRACE("(%p, %p).\n", iface
, volume
);
1070 EnterCriticalSection(&This
->cs
);
1071 *volume
= This
->volume
;
1072 LeaveCriticalSection(&This
->cs
);
1077 static HRESULT WINAPI
spvoice_WaitUntilDone(ISpVoice
*iface
, ULONG timeout
)
1079 struct speech_voice
*This
= impl_from_ISpVoice(iface
);
1082 TRACE("(%p, %ld).\n", iface
, timeout
);
1084 hr
= async_wait_queue_empty(&This
->queue
, timeout
);
1086 if (hr
== WAIT_OBJECT_0
) return S_OK
;
1087 else if (hr
== WAIT_TIMEOUT
) return S_FALSE
;
1091 static HRESULT WINAPI
spvoice_SetSyncSpeakTimeout(ISpVoice
*iface
, ULONG timeout
)
1093 FIXME("(%p, %ld): stub.\n", iface
, timeout
);
1098 static HRESULT WINAPI
spvoice_GetSyncSpeakTimeout(ISpVoice
*iface
, ULONG
*timeout
)
1100 FIXME("(%p, %p): stub.\n", iface
, timeout
);
1105 static HANDLE WINAPI
spvoice_SpeakCompleteEvent(ISpVoice
*iface
)
1107 FIXME("(%p): stub.\n", iface
);
1112 static HRESULT WINAPI
spvoice_IsUISupported(ISpVoice
*iface
, const WCHAR
*type
, void *extra
,
1113 ULONG count
, BOOL
*supported
)
1115 FIXME("(%p, %p, %p, %ld, %p): stub.\n", iface
, type
, extra
, count
, supported
);
1120 static HRESULT WINAPI
spvoice_DisplayUI(ISpVoice
*iface
, HWND parent
, const WCHAR
*title
,
1121 const WCHAR
*type
, void *extra
, ULONG count
)
1123 FIXME("(%p, %p, %p, %p, %p, %ld): stub.\n", iface
, parent
, title
, type
, extra
, count
);
1128 static const ISpVoiceVtbl spvoice_vtbl
=
1130 spvoice_QueryInterface
,
1133 spvoice_SetNotifySink
,
1134 spvoice_SetNotifyWindowMessage
,
1135 spvoice_SetNotifyCallbackFunction
,
1136 spvoice_SetNotifyCallbackInterface
,
1137 spvoice_SetNotifyWin32Event
,
1138 spvoice_WaitForNotifyEvent
,
1139 spvoice_GetNotifyEventHandle
,
1140 spvoice_SetInterest
,
1144 spvoice_GetOutputObjectToken
,
1145 spvoice_GetOutputStream
,
1151 spvoice_SpeakStream
,
1154 spvoice_SetPriority
,
1155 spvoice_GetPriority
,
1156 spvoice_SetAlertBoundary
,
1157 spvoice_GetAlertBoundary
,
1162 spvoice_WaitUntilDone
,
1163 spvoice_SetSyncSpeakTimeout
,
1164 spvoice_GetSyncSpeakTimeout
,
1165 spvoice_SpeakCompleteEvent
,
1166 spvoice_IsUISupported
,
1170 /* ISpTTSEngineSite interface */
1171 static HRESULT WINAPI
ttsenginesite_QueryInterface(ISpTTSEngineSite
*iface
, REFIID iid
, void **obj
)
1173 struct tts_engine_site
*This
= impl_from_ISpTTSEngineSite(iface
);
1175 TRACE("(%p, %s %p).\n", iface
, debugstr_guid(iid
), obj
);
1177 if (IsEqualIID(iid
, &IID_IUnknown
) ||
1178 IsEqualIID(iid
, &IID_ISpTTSEngineSite
))
1179 *obj
= &This
->ISpTTSEngineSite_iface
;
1183 FIXME("interface %s not implemented.\n", debugstr_guid(iid
));
1184 return E_NOINTERFACE
;
1187 IUnknown_AddRef((IUnknown
*)*obj
);
1191 static ULONG WINAPI
ttsenginesite_AddRef(ISpTTSEngineSite
*iface
)
1193 struct tts_engine_site
*This
= impl_from_ISpTTSEngineSite(iface
);
1194 ULONG ref
= InterlockedIncrement(&This
->ref
);
1196 TRACE("(%p): ref=%lu.\n", iface
, ref
);
1201 static ULONG WINAPI
ttsenginesite_Release(ISpTTSEngineSite
*iface
)
1203 struct tts_engine_site
*This
= impl_from_ISpTTSEngineSite(iface
);
1205 ULONG ref
= InterlockedDecrement(&This
->ref
);
1207 TRACE("(%p): ref=%lu.\n", iface
, ref
);
1212 ISpeechVoice_Release(&This
->voice
->ISpeechVoice_iface
);
1219 static HRESULT WINAPI
ttsenginesite_AddEvents(ISpTTSEngineSite
*iface
, const SPEVENT
*events
, ULONG count
)
1221 FIXME("(%p, %p, %ld): stub.\n", iface
, events
, count
);
1226 static HRESULT WINAPI
ttsenginesite_GetEventInterest(ISpTTSEngineSite
*iface
, ULONGLONG
*interest
)
1228 FIXME("(%p, %p): stub.\n", iface
, interest
);
1233 static DWORD WINAPI
ttsenginesite_GetActions(ISpTTSEngineSite
*iface
)
1235 struct tts_engine_site
*This
= impl_from_ISpTTSEngineSite(iface
);
1238 TRACE("(%p).\n", iface
);
1240 EnterCriticalSection(&This
->voice
->cs
);
1241 actions
= This
->voice
->actions
;
1242 LeaveCriticalSection(&This
->voice
->cs
);
1247 static HRESULT WINAPI
ttsenginesite_Write(ISpTTSEngineSite
*iface
, const void *buf
, ULONG cb
, ULONG
*cb_written
)
1249 struct tts_engine_site
*This
= impl_from_ISpTTSEngineSite(iface
);
1251 TRACE("(%p, %p, %ld, %p).\n", iface
, buf
, cb
, cb_written
);
1253 if (!This
->voice
->output
)
1254 return SPERR_UNINITIALIZED
;
1256 return ISpStreamFormat_Write(This
->voice
->output
, buf
, cb
, cb_written
);
1259 static HRESULT WINAPI
ttsenginesite_GetRate(ISpTTSEngineSite
*iface
, LONG
*rate
)
1261 struct tts_engine_site
*This
= impl_from_ISpTTSEngineSite(iface
);
1263 TRACE("(%p, %p).\n", iface
, rate
);
1265 EnterCriticalSection(&This
->voice
->cs
);
1266 *rate
= This
->voice
->rate
;
1267 This
->voice
->actions
&= ~SPVES_RATE
;
1268 LeaveCriticalSection(&This
->voice
->cs
);
1273 static HRESULT WINAPI
ttsenginesite_GetVolume(ISpTTSEngineSite
*iface
, USHORT
*volume
)
1275 struct tts_engine_site
*This
= impl_from_ISpTTSEngineSite(iface
);
1277 TRACE("(%p, %p).\n", iface
, volume
);
1279 EnterCriticalSection(&This
->voice
->cs
);
1280 *volume
= This
->voice
->volume
;
1281 This
->voice
->actions
&= ~SPVES_VOLUME
;
1282 LeaveCriticalSection(&This
->voice
->cs
);
1287 static HRESULT WINAPI
ttsenginesite_GetSkipInfo(ISpTTSEngineSite
*iface
, SPVSKIPTYPE
*type
, LONG
*skip_count
)
1289 FIXME("(%p, %p, %p): stub.\n", iface
, type
, skip_count
);
1294 static HRESULT WINAPI
ttsenginesite_CompleteSkip(ISpTTSEngineSite
*iface
, LONG num_skipped
)
1296 FIXME("(%p, %ld): stub.\n", iface
, num_skipped
);
1301 const static ISpTTSEngineSiteVtbl ttsenginesite_vtbl
=
1303 ttsenginesite_QueryInterface
,
1304 ttsenginesite_AddRef
,
1305 ttsenginesite_Release
,
1306 ttsenginesite_AddEvents
,
1307 ttsenginesite_GetEventInterest
,
1308 ttsenginesite_GetActions
,
1309 ttsenginesite_Write
,
1310 ttsenginesite_GetRate
,
1311 ttsenginesite_GetVolume
,
1312 ttsenginesite_GetSkipInfo
,
1313 ttsenginesite_CompleteSkip
1316 static HRESULT
ttsenginesite_create(struct speech_voice
*voice
, ULONG stream_num
, ISpTTSEngineSite
**site
)
1318 struct tts_engine_site
*This
= heap_alloc(sizeof(*This
));
1320 if (!This
) return E_OUTOFMEMORY
;
1322 This
->ISpTTSEngineSite_iface
.lpVtbl
= &ttsenginesite_vtbl
;
1325 This
->voice
= voice
;
1326 This
->stream_num
= stream_num
;
1328 ISpeechVoice_AddRef(&This
->voice
->ISpeechVoice_iface
);
1330 *site
= &This
->ISpTTSEngineSite_iface
;
1335 /* IConnectionPointContainer interface */
1336 static HRESULT WINAPI
container_QueryInterface(IConnectionPointContainer
*iface
, REFIID iid
, void **obj
)
1338 struct speech_voice
*This
= impl_from_IConnectionPointContainer(iface
);
1340 TRACE("(%p, %s %p).\n", iface
, debugstr_guid(iid
), obj
);
1342 return ISpeechVoice_QueryInterface(&This
->ISpeechVoice_iface
, iid
, obj
);
1345 static ULONG WINAPI
container_AddRef(IConnectionPointContainer
*iface
)
1347 struct speech_voice
*This
= impl_from_IConnectionPointContainer(iface
);
1349 TRACE("(%p).\n", iface
);
1351 return ISpeechVoice_AddRef(&This
->ISpeechVoice_iface
);
1354 static ULONG WINAPI
container_Release(IConnectionPointContainer
*iface
)
1356 struct speech_voice
*This
= impl_from_IConnectionPointContainer(iface
);
1358 TRACE("(%p).\n", iface
);
1360 return ISpeechVoice_Release(&This
->ISpeechVoice_iface
);
1363 static HRESULT WINAPI
container_EnumConnectionPoints(IConnectionPointContainer
*iface
,
1364 IEnumConnectionPoints
**enum_cp
)
1366 FIXME("(%p, %p): stub.\n", iface
, enum_cp
);
1371 static HRESULT WINAPI
container_FindConnectionPoint(IConnectionPointContainer
*iface
, REFIID riid
,
1372 IConnectionPoint
**cp
)
1374 FIXME("(%p, %s, %p): stub.\n", iface
, debugstr_guid(riid
), cp
);
1379 const static IConnectionPointContainerVtbl container_vtbl
=
1381 container_QueryInterface
,
1384 container_EnumConnectionPoints
,
1385 container_FindConnectionPoint
1388 HRESULT
speech_voice_create(IUnknown
*outer
, REFIID iid
, void **obj
)
1390 struct speech_voice
*This
= heap_alloc(sizeof(*This
));
1393 if (!This
) return E_OUTOFMEMORY
;
1394 This
->ISpeechVoice_iface
.lpVtbl
= &speech_voice_vtbl
;
1395 This
->ISpVoice_iface
.lpVtbl
= &spvoice_vtbl
;
1396 This
->IConnectionPointContainer_iface
.lpVtbl
= &container_vtbl
;
1399 This
->output
= NULL
;
1400 This
->engine
= NULL
;
1401 This
->cur_stream_num
= 0;
1402 This
->actions
= SPVES_CONTINUE
;
1405 memset(&This
->queue
, 0, sizeof(This
->queue
));
1407 InitializeCriticalSection(&This
->cs
);
1409 hr
= ISpeechVoice_QueryInterface(&This
->ISpeechVoice_iface
, iid
, obj
);
1411 ISpeechVoice_Release(&This
->ISpeechVoice_iface
);