[PATCH v8 0/9] MR3048: Media.speech improvements.
This series adds a couple of features to WinRT's media.speech: - IVoiceInformation, and synthetizer's options are present (mainly use to store information, not yet to make fully use of it) - add a dummy implementation (1 single voice); that should be extended in future series by using ISpVoice instead - add a couple of more tests to go with implementation (the serie should prevent MS Flight simulator to crash on first connection. Crash is due to exception handling of C# generated exceptions from E_NOTIMPL with some X11 related critical resources). A+ -- v8: windows.media.speech: Implement get/put voice on synthesizer. windows.media.speech: Select a default voice in synthesizer. windows.media.speech/tests: Add more tests about default voice. windows.media.speech: Add basic implementation of IVoiceInformation. windows.media.speech: Implement IVectorView<VoiceInformation> on top of a stub provider. https://gitlab.winehq.org/wine/wine/-/merge_requests/3048
From: Eric Pouech <epouech@codeweavers.com> Signed-off-by: Eric Pouech <epouech@codeweavers.com> --- dlls/windows.media.speech/tests/speech.c | 43 +++++++++++++++++++++++- 1 file changed, 42 insertions(+), 1 deletion(-) diff --git a/dlls/windows.media.speech/tests/speech.c b/dlls/windows.media.speech/tests/speech.c index 0f8bbb8f563..02e3182c565 100644 --- a/dlls/windows.media.speech/tests/speech.c +++ b/dlls/windows.media.speech/tests/speech.c @@ -1071,16 +1071,57 @@ static void test_SpeechSynthesizer(void) if (hr == S_OK) { + enum SpeechPunctuationSilence punctuation_value; + enum SpeechAppendedSilence silence_value; + ISpeechSynthesizerOptions2 *options2; ISpeechSynthesizerOptions3 *options3; + boolean bool_value; + DOUBLE double_value; check_interface(options, &IID_IAgileObject, TRUE); - check_optional_interface(options, &IID_ISpeechSynthesizerOptions2, TRUE); /* Requires Win10 >= 1709 */ + bool_value = 0xff; + hr = ISpeechSynthesizerOptions_get_IncludeSentenceBoundaryMetadata(options, &bool_value); + ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr); + ok(! bool_value, "Got unepected option %u\n", bool_value); + bool_value = 0xff; + hr = ISpeechSynthesizerOptions_get_IncludeWordBoundaryMetadata(options, &bool_value); + ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr); + ok(!bool_value, "Got unepected option %u\n", bool_value); + + hr = ISpeechSynthesizerOptions_QueryInterface(options, &IID_ISpeechSynthesizerOptions2, (void **)&options2); + ok(hr == S_OK || broken(hr == E_NOINTERFACE), "Got unexpected hr %#lx.\n", hr); /* Requires Win10 >= 1709 */ + + if (hr == S_OK) + { + hr = ISpeechSynthesizerOptions2_get_AudioPitch(options2, &double_value); + ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr); + ok(double_value == 1.0f, "Got unepected option %f\n", double_value); + + hr = ISpeechSynthesizerOptions2_get_AudioVolume(options2, &double_value); + ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr); + ok(double_value == 1.0f, "Got unepected option %f\n", double_value); + + hr = ISpeechSynthesizerOptions2_get_SpeakingRate(options2, &double_value); + ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr); + ok(double_value == 1.0f, "Got unepected option %f\n", double_value); + + ref = ISpeechSynthesizerOptions2_Release(options2); + ok(ref == 2, "Got unexpected ref %lu.\n", ref); + } hr = ISpeechSynthesizerOptions_QueryInterface(options, &IID_ISpeechSynthesizerOptions3, (void **)&options3); ok(hr == S_OK || broken(hr == E_NOINTERFACE), "Got unexpected hr %#lx.\n", hr); /* Requires Win10 >= 1803 */ if (hr == S_OK) { + hr = ISpeechSynthesizerOptions3_get_AppendedSilence(options3, &silence_value); + ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr); + ok(silence_value == SpeechAppendedSilence_Default, "Got unepected option %u\n", silence_value); + + hr = ISpeechSynthesizerOptions3_get_PunctuationSilence(options3, &punctuation_value); + ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr); + ok(punctuation_value == SpeechPunctuationSilence_Default, "Got unepected option %u\n", punctuation_value); + ref = ISpeechSynthesizerOptions3_Release(options3); ok(ref == 2, "Got unexpected ref %lu.\n", ref); } -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/3048
From: Eric Pouech <epouech@codeweavers.com> Signed-off-by: Eric Pouech <epouech@codeweavers.com> --- dlls/windows.media.speech/synthesizer.c | 327 ++++++++++++++++++++++- dlls/windows.media.speech/tests/speech.c | 2 +- 2 files changed, 326 insertions(+), 3 deletions(-) diff --git a/dlls/windows.media.speech/synthesizer.c b/dlls/windows.media.speech/synthesizer.c index d463bf1e6aa..3bb9d2069e4 100644 --- a/dlls/windows.media.speech/synthesizer.c +++ b/dlls/windows.media.speech/synthesizer.c @@ -743,6 +743,312 @@ error: return hr; } +/* + * + * SpeechSynthesizerOptions runtimeclass + * + */ +struct synthesizer_options +{ + ISpeechSynthesizerOptions ISpeechSynthesizerOptions_iface; + ISpeechSynthesizerOptions2 ISpeechSynthesizerOptions2_iface; + ISpeechSynthesizerOptions3 ISpeechSynthesizerOptions3_iface; + LONG ref; + + /* options */ + boolean include_word_boundary; + boolean include_sentence_boundary; + + /* options 2 */ + double audio_volume; + double speaking_rate; + double audio_pitch; + + /* options 3 */ + enum SpeechAppendedSilence appended_silence; + enum SpeechPunctuationSilence punctuation_silence; +}; + +static inline struct synthesizer_options *impl_from_ISpeechSynthesizerOptions( ISpeechSynthesizerOptions *iface ) +{ + return CONTAINING_RECORD(iface, struct synthesizer_options, ISpeechSynthesizerOptions_iface); +} + +static HRESULT WINAPI synthesizer_options_QueryInterface( ISpeechSynthesizerOptions *iface, REFIID iid, void **out) +{ + struct synthesizer_options *impl = impl_from_ISpeechSynthesizerOptions(iface); + + TRACE("iface %p, iid %s, out %p stub!\n", iface, debugstr_guid(iid), out); + + if (IsEqualGUID(iid, &IID_IUnknown) || + IsEqualGUID(iid, &IID_IInspectable) || + IsEqualGUID(iid, &IID_IAgileObject) || + IsEqualGUID(iid, &IID_ISpeechSynthesizerOptions)) + { + IInspectable_AddRef((*out = &impl->ISpeechSynthesizerOptions_iface)); + return S_OK; + } + + if (IsEqualGUID(iid, &IID_ISpeechSynthesizerOptions2)) + { + IInspectable_AddRef((*out = &impl->ISpeechSynthesizerOptions2_iface)); + return S_OK; + } + + if (IsEqualGUID(iid, &IID_ISpeechSynthesizerOptions3)) + { + IInspectable_AddRef((*out = &impl->ISpeechSynthesizerOptions3_iface)); + return S_OK; + } + + FIXME("%s not implemented, returning E_NOINTERFACE.\n", debugstr_guid(iid)); + *out = NULL; + return E_NOINTERFACE; +} + +static ULONG WINAPI synthesizer_options_AddRef( ISpeechSynthesizerOptions *iface ) +{ + struct synthesizer_options *impl = impl_from_ISpeechSynthesizerOptions(iface); + ULONG ref = InterlockedIncrement(&impl->ref); + + TRACE("iface %p, ref %lu.\n", iface, ref); + return ref; +} + +static ULONG WINAPI synthesizer_options_Release( ISpeechSynthesizerOptions *iface ) +{ + struct synthesizer_options *impl = impl_from_ISpeechSynthesizerOptions(iface); + ULONG ref = InterlockedDecrement(&impl->ref); + + TRACE("iface %p, ref %lu.\n", iface, ref); + if (!ref) + free(impl); + return ref; +} + +static HRESULT WINAPI synthesizer_options_GetIids( ISpeechSynthesizerOptions *iface, ULONG *iid_count, IID **iids ) +{ + FIXME("iface %p, iid_count %p, iids %p stub.\n", iface, iid_count, iids); + return E_NOTIMPL; +} + +static HRESULT WINAPI synthesizer_options_GetRuntimeClassName( ISpeechSynthesizerOptions *iface, HSTRING *class_name ) +{ + FIXME("iface %p, class_name %p stub.\n", iface, class_name); + return E_NOTIMPL; +} + +static HRESULT WINAPI synthesizer_options_GetTrustLevel( ISpeechSynthesizerOptions *iface, TrustLevel *trust_level ) +{ + FIXME("iface %p, trust_level %p stub.\n", iface, trust_level); + return E_NOTIMPL; +} + +static HRESULT WINAPI synthesizer_options_get_IncludeWordBoundaryMetadata( ISpeechSynthesizerOptions *iface, boolean *value ) +{ + struct synthesizer_options *impl = impl_from_ISpeechSynthesizerOptions(iface); + TRACE("iface %p, value %p semi-stub.\n", iface, value); + + *value = impl->include_word_boundary; + return S_OK; +} + +static HRESULT WINAPI synthesizer_options_put_IncludeWordBoundaryMetadata( ISpeechSynthesizerOptions *iface, boolean value ) +{ + struct synthesizer_options *impl = impl_from_ISpeechSynthesizerOptions(iface); + TRACE("iface %p, value %s semi-stub.\n", iface, value ? "true" : "false"); + + impl->include_word_boundary = value; + return S_OK; +} + +static HRESULT WINAPI synthesizer_options_get_IncludeSentenceBoundaryMetadata( ISpeechSynthesizerOptions *iface, boolean *value ) +{ + struct synthesizer_options *impl = impl_from_ISpeechSynthesizerOptions(iface); + TRACE("iface %p, value %p stub.\n", iface, value); + + *value = impl->include_sentence_boundary; + return S_OK; +} + +static HRESULT WINAPI synthesizer_options_put_IncludeSentenceBoundaryMetadata( ISpeechSynthesizerOptions *iface, boolean value ) +{ + struct synthesizer_options *impl = impl_from_ISpeechSynthesizerOptions(iface); + TRACE("iface %p, value %s stub.\n", iface, value ? "true" : "false"); + + impl->include_sentence_boundary = value; + return S_OK; +} + +static const struct ISpeechSynthesizerOptionsVtbl synthesizer_options_vtbl = +{ + /*** IUnknown methods ***/ + synthesizer_options_QueryInterface, + synthesizer_options_AddRef, + synthesizer_options_Release, + /*** IInspectable methods ***/ + synthesizer_options_GetIids, + synthesizer_options_GetRuntimeClassName, + synthesizer_options_GetTrustLevel, + /*** ISpeechSynthesizerOptions methods ***/ + synthesizer_options_get_IncludeWordBoundaryMetadata, + synthesizer_options_put_IncludeWordBoundaryMetadata, + synthesizer_options_get_IncludeSentenceBoundaryMetadata, + synthesizer_options_put_IncludeSentenceBoundaryMetadata, +}; + +DEFINE_IINSPECTABLE(synthesizer_options2, ISpeechSynthesizerOptions2, struct synthesizer_options, ISpeechSynthesizerOptions_iface) + +static HRESULT WINAPI synthesizer_options2_get_AudioVolume( ISpeechSynthesizerOptions2 *iface, DOUBLE *value) +{ + struct synthesizer_options *impl = impl_from_ISpeechSynthesizerOptions2(iface); + + TRACE("iface %p value %p semi-stub!\n", iface, value); + *value = impl->audio_volume; + return S_OK; +} + +static HRESULT WINAPI synthesizer_options2_put_AudioVolume( ISpeechSynthesizerOptions2 *iface, DOUBLE value) +{ + struct synthesizer_options *impl = impl_from_ISpeechSynthesizerOptions2(iface); + + TRACE("iface %p value %g semi-stub!\n", iface, value); + impl->audio_volume = value; + return S_OK; +} + +static HRESULT WINAPI synthesizer_options2_get_SpeakingRate( ISpeechSynthesizerOptions2 *iface, DOUBLE *value) +{ + struct synthesizer_options *impl = impl_from_ISpeechSynthesizerOptions2(iface); + + TRACE("iface %p value %p semi-stub!\n", iface, value); + *value = impl->speaking_rate; + return S_OK; +} + +static HRESULT WINAPI synthesizer_options2_put_SpeakingRate( ISpeechSynthesizerOptions2 *iface, DOUBLE value) +{ + struct synthesizer_options *impl = impl_from_ISpeechSynthesizerOptions2(iface); + + TRACE("iface %p value %g semi-stub!\n", iface, value); + impl->speaking_rate = value; + return S_OK; +} + +static HRESULT WINAPI synthesizer_options2_get_AudioPitch( ISpeechSynthesizerOptions2 *iface, DOUBLE *value) +{ + struct synthesizer_options *impl = impl_from_ISpeechSynthesizerOptions2(iface); + + TRACE("iface %p value %p semi-stub!\n", iface, value); + *value = impl->audio_pitch; + return S_OK; +} + +static HRESULT WINAPI synthesizer_options2_put_AudioPitch( ISpeechSynthesizerOptions2 *iface, DOUBLE value) +{ + struct synthesizer_options *impl = impl_from_ISpeechSynthesizerOptions2(iface); + + TRACE("iface %p value %g semi-stub!\n", iface, value); + impl->audio_pitch = value; + return S_OK; +} + +static const struct ISpeechSynthesizerOptions2Vtbl synthesizer_options2_vtbl = +{ + /*** IUnknown methods ***/ + synthesizer_options2_QueryInterface, + synthesizer_options2_AddRef, + synthesizer_options2_Release, + /*** IInspectable methods ***/ + synthesizer_options2_GetIids, + synthesizer_options2_GetRuntimeClassName, + synthesizer_options2_GetTrustLevel, + /*** ISpeechSynthesizerOptions methods ***/ + synthesizer_options2_get_AudioVolume, + synthesizer_options2_put_AudioVolume, + synthesizer_options2_get_SpeakingRate, + synthesizer_options2_put_SpeakingRate, + synthesizer_options2_get_AudioPitch, + synthesizer_options2_put_AudioPitch, +}; + +DEFINE_IINSPECTABLE(synthesizer_options3, ISpeechSynthesizerOptions3, struct synthesizer_options, ISpeechSynthesizerOptions_iface) + +static HRESULT WINAPI synthesizer_options3_get_AppendedSilence( ISpeechSynthesizerOptions3 *iface, enum SpeechAppendedSilence *value) +{ + struct synthesizer_options *impl = impl_from_ISpeechSynthesizerOptions3(iface); + + TRACE("iface %p value %p semi-stub!\n", iface, value); + *value = impl->appended_silence; + return S_OK; +} + +static HRESULT WINAPI synthesizer_options3_put_AppendedSilence( ISpeechSynthesizerOptions3 *iface, enum SpeechAppendedSilence value) +{ + struct synthesizer_options *impl = impl_from_ISpeechSynthesizerOptions3(iface); + + TRACE("iface %p value %u semi-stub!\n", iface, value); + impl->appended_silence = value; + return S_OK; +} + +static HRESULT WINAPI synthesizer_options3_get_PunctuationSilence( ISpeechSynthesizerOptions3 *iface, enum SpeechPunctuationSilence *value) +{ + struct synthesizer_options *impl = impl_from_ISpeechSynthesizerOptions3(iface); + + TRACE("iface %p value %p semi-stub!\n", iface, value); + *value = impl->punctuation_silence; + return S_OK; +} + +static HRESULT WINAPI synthesizer_options3_put_PunctuationSilence( ISpeechSynthesizerOptions3 *iface, enum SpeechPunctuationSilence value) +{ + struct synthesizer_options *impl = impl_from_ISpeechSynthesizerOptions3(iface); + + TRACE("iface %p value %u semi-stub!\n", iface, value); + impl->punctuation_silence = value; + return S_OK; +} + +static const struct ISpeechSynthesizerOptions3Vtbl synthesizer_options3_vtbl = +{ + /*** IUnknown methods ***/ + synthesizer_options3_QueryInterface, + synthesizer_options3_AddRef, + synthesizer_options3_Release, + /*** IInspectable methods ***/ + synthesizer_options3_GetIids, + synthesizer_options3_GetRuntimeClassName, + synthesizer_options3_GetTrustLevel, + /*** ISpeechSynthesizerOptions methods ***/ + synthesizer_options3_get_AppendedSilence, + synthesizer_options3_put_AppendedSilence, + synthesizer_options3_get_PunctuationSilence, + synthesizer_options3_put_PunctuationSilence, +}; + +static HRESULT synthesizer_options_create( struct synthesizer_options **out ) +{ + struct synthesizer_options *options; + + if (!(options = calloc(1, sizeof(*options)))) return E_OUTOFMEMORY; + + options->ISpeechSynthesizerOptions_iface.lpVtbl = &synthesizer_options_vtbl; + options->ISpeechSynthesizerOptions2_iface.lpVtbl = &synthesizer_options2_vtbl; + options->ISpeechSynthesizerOptions3_iface.lpVtbl = &synthesizer_options3_vtbl; + /* all other values default to 0 or false */ + options->audio_pitch = 1.0; + options->audio_volume = 1.0; + options->speaking_rate = 1.0; + options->ref = 1; + + *out = options; + + TRACE("Created ISpeechSynthesizerOptions %p.\n", *out); + + return S_OK; +} + /* * * SpeechSynthesizer runtimeclass @@ -755,6 +1061,8 @@ struct synthesizer ISpeechSynthesizer2 ISpeechSynthesizer2_iface; IClosable IClosable_iface; LONG ref; + + struct synthesizer_options *options; }; /* @@ -815,7 +1123,10 @@ static ULONG WINAPI synthesizer_Release( ISpeechSynthesizer *iface ) TRACE("iface %p, ref %lu.\n", iface, ref); if (!ref) + { + ISpeechSynthesizerOptions_Release(&impl->options->ISpeechSynthesizerOptions_iface); free(impl); + } return ref; } @@ -923,8 +1234,11 @@ DEFINE_IINSPECTABLE(synthesizer2, ISpeechSynthesizer2, struct synthesizer, ISpee static HRESULT WINAPI synthesizer2_get_Options( ISpeechSynthesizer2 *iface, ISpeechSynthesizerOptions **value ) { - FIXME("iface %p, value %p stub.\n", iface, value); - return E_NOTIMPL; + struct synthesizer *impl = impl_from_ISpeechSynthesizer2(iface); + + TRACE("iface %p, value %p.\n", iface, value); + ISpeechSynthesizerOptions_AddRef((*value = &impl->options->ISpeechSynthesizerOptions_iface)); + return S_OK; } static const struct ISpeechSynthesizer2Vtbl synthesizer2_vtbl = @@ -1056,6 +1370,7 @@ static HRESULT WINAPI factory_GetTrustLevel( IActivationFactory *iface, TrustLev static HRESULT WINAPI factory_ActivateInstance( IActivationFactory *iface, IInspectable **instance ) { struct synthesizer *impl; + HRESULT hr; TRACE("iface %p, instance %p.\n", iface, instance); @@ -1065,6 +1380,9 @@ static HRESULT WINAPI factory_ActivateInstance( IActivationFactory *iface, IInsp return E_OUTOFMEMORY; } + if (FAILED(hr = synthesizer_options_create(&impl->options))) + goto failed; + impl->ISpeechSynthesizer_iface.lpVtbl = &synthesizer_vtbl; impl->ISpeechSynthesizer2_iface.lpVtbl = &synthesizer2_vtbl; impl->IClosable_iface.lpVtbl = &closable_vtbl; @@ -1072,6 +1390,11 @@ static HRESULT WINAPI factory_ActivateInstance( IActivationFactory *iface, IInsp *instance = (IInspectable *)&impl->ISpeechSynthesizer_iface; return S_OK; + +failed: + free(impl); + *instance = NULL; + return hr; } static const struct IActivationFactoryVtbl factory_vtbl = diff --git a/dlls/windows.media.speech/tests/speech.c b/dlls/windows.media.speech/tests/speech.c index 02e3182c565..851d8c28375 100644 --- a/dlls/windows.media.speech/tests/speech.c +++ b/dlls/windows.media.speech/tests/speech.c @@ -1067,7 +1067,7 @@ static void test_SpeechSynthesizer(void) ISpeechSynthesizerOptions *options; hr = ISpeechSynthesizer2_get_Options(synthesizer2, &options); - todo_wine ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr); + ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr); if (hr == S_OK) { -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/3048
From: Eric Pouech <epouech@codeweavers.com> Signed-off-by: Eric Pouech <epouech@codeweavers.com> --- dlls/windows.media.speech/tests/speech.c | 73 +++++++++++++++++++++++- 1 file changed, 70 insertions(+), 3 deletions(-) diff --git a/dlls/windows.media.speech/tests/speech.c b/dlls/windows.media.speech/tests/speech.c index 851d8c28375..e2279a7a905 100644 --- a/dlls/windows.media.speech/tests/speech.c +++ b/dlls/windows.media.speech/tests/speech.c @@ -673,6 +673,66 @@ static HRESULT WINAPI iterable_hstring_create_static( struct iterable_hstring *i return S_OK; } +#define check_comparable_presence(a, b) _check_comparable_presence( __LINE__, (a), (b)) +static void _check_comparable_presence( unsigned line, IVectorView_VoiceInformation *voices, IVoiceInformation *voice) +{ + HSTRING in_display, in_id, in_language; + HSTRING vc_display, vc_id, vc_language; + IVoiceInformation *vc_voice; + enum VoiceGender in_gender, vc_gender; + UINT32 size, idx, found_count = 0; + HRESULT hr; + INT32 cmp; + + hr = IVoiceInformation_get_DisplayName(voice, &in_display); + ok_(__FILE__, line)(hr == S_OK, "Got unexpected hr %#lx.\n", hr); + hr = IVoiceInformation_get_Id(voice, &in_id); + ok_(__FILE__, line)(hr == S_OK, "Got unexpected hr %#lx.\n", hr); + hr = IVoiceInformation_get_Language(voice, &in_language); + ok_(__FILE__, line)(hr == S_OK, "Got unexpected hr %#lx.\n", hr); + hr = IVoiceInformation_get_Gender(voice, &in_gender); + ok_(__FILE__, line)(hr == S_OK, "Got unexpected hr %#lx.\n", hr); + hr = IVectorView_VoiceInformation_get_Size(voices, &size); + ok_(__FILE__, line)(hr == S_OK, "Got unexpected hr %#lx.\n", hr); + + for (idx = 0; SUCCEEDED(hr = IVectorView_VoiceInformation_GetAt(voices, idx, &vc_voice)); idx++) + { + hr = IVoiceInformation_get_DisplayName(vc_voice, &vc_display); + ok_(__FILE__, line)(hr == S_OK, "Got unexpected hr %#lx.\n", hr); + hr = IVoiceInformation_get_Id(vc_voice, &vc_id); + ok_(__FILE__, line)(hr == S_OK, "Got unexpected hr %#lx.\n", hr); + hr = IVoiceInformation_get_Language(vc_voice, &vc_language); + ok_(__FILE__, line)(hr == S_OK, "Got unexpected hr %#lx.\n", hr); + hr = IVoiceInformation_get_Gender(vc_voice, &vc_gender); + ok_(__FILE__, line)(hr == S_OK, "Got unexpected hr %#lx.\n", hr); + trace("%2u] %s | %s | %s | %u\n", + idx, debugstr_hstring(vc_display), debugstr_hstring(vc_id), debugstr_hstring(vc_language), vc_gender); + + if (SUCCEEDED(WindowsCompareStringOrdinal(in_display, vc_display, &cmp)) && !cmp && + SUCCEEDED(WindowsCompareStringOrdinal(in_id, vc_id, &cmp)) && !cmp && + SUCCEEDED(WindowsCompareStringOrdinal(in_language, vc_language, &cmp)) && !cmp && + in_gender == vc_gender) + { + found_count++; + } + WindowsDeleteString(vc_display); + WindowsDeleteString(vc_id); + WindowsDeleteString(vc_language); + IVoiceInformation_Release(vc_voice); + } + ok(hr == E_BOUNDS, "Got unexpected hr %#lx.\n", hr); + ok(idx != 0, "Vector view shouldn't be empty!\n"); + ok(idx == size, "Incoherent index/size %u/%u!\n", idx, size); + + ok_(__FILE__, line)(found_count == 1, "Found several (%u) instances of %s | %s | %s | %u\n", + found_count, + debugstr_hstring(in_display), debugstr_hstring(in_id), debugstr_hstring(in_language), in_gender); + + WindowsDeleteString(in_display); + WindowsDeleteString(in_id); + WindowsDeleteString(in_language); +} + static void test_ActivationFactory(void) { static const WCHAR *synthesizer_name = L"Windows.Media.SpeechSynthesis.SpeechSynthesizer"; @@ -804,7 +864,8 @@ static void test_SpeechSynthesizer(void) HSTRING str, str2; UINT64 value; HRESULT hr; - UINT32 size; + UINT32 size, idx; + BOOLEAN found; ULONG ref; hr = RoInitialize(RO_INIT_MULTITHREADED); @@ -898,13 +959,19 @@ static void test_SpeechSynthesizer(void) ok(hr == S_OK, "IVectorView_VoiceInformation_GetMany failed, hr %#lx\n", hr); ok(size == 0, "IVectorView_VoiceInformation_GetMany returned count %u\n", size); - IVectorView_VoiceInformation_Release(voices); - hr = IInstalledVoicesStatic_get_DefaultVoice(voices_static, &voice); todo_wine ok(hr == S_OK, "IInstalledVoicesStatic_get_DefaultVoice failed, hr %#lx\n", hr); if (hr == S_OK) { + /* check that VoiceInformation in static vector voice are not shared when exposed to user */ + idx = size; + hr = IVectorView_VoiceInformation_IndexOf(voices, voice, &idx, &found); + ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr); + ok(!found, "Shouldn't find default element\n"); + + check_comparable_presence(voices, voice); + IVoiceInformation_get_Description(voice, &str2); trace("SpeechSynthesizer default voice %s.\n", debugstr_hstring(str2)); -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/3048
From: Eric Pouech <epouech@codeweavers.com> Signed-off-by: Eric Pouech <epouech@codeweavers.com> --- dlls/windows.media.speech/tests/speech.c | 32 +++++++++++++++++++++++- 1 file changed, 31 insertions(+), 1 deletion(-) diff --git a/dlls/windows.media.speech/tests/speech.c b/dlls/windows.media.speech/tests/speech.c index e2279a7a905..c5025e9898e 100644 --- a/dlls/windows.media.speech/tests/speech.c +++ b/dlls/windows.media.speech/tests/speech.c @@ -853,7 +853,7 @@ static void test_SpeechSynthesizer(void) IRandomAccessStream *ra_stream; IInputStream *inp_stream; IBuffer *buffer = NULL, *buffer2 = NULL; - IVoiceInformation *voice; + IVoiceInformation *voice, *voice2; IInspectable *inspectable = NULL, *tmp_inspectable = NULL; IAgileObject *agile_object = NULL, *tmp_agile_object = NULL; ISpeechSynthesizer *synthesizer; @@ -950,6 +950,23 @@ static void test_SpeechSynthesizer(void) ok(hr == S_OK, "IVectorView_VoiceInformation_get_Size voices failed, hr %#lx\n", hr); todo_wine ok(size != 0 && size != 0xdeadbeef, "IVectorView_VoiceInformation_get_Size returned %u\n", size); + voice = (IVoiceInformation *)0xdeadbeef; + hr = IVectorView_VoiceInformation_GetAt(voices, 0, &voice); + todo_wine ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr); + + hr = IVectorView_VoiceInformation_GetAt(voices, 0, &voice2); + todo_wine ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr); + ok(voice == voice2, "Didn't get the same object\n"); + + if (voice && voice != (IVoiceInformation *)0xdeadbeef){ + ref = IVoiceInformation_Release(voice); + ok(ref == 2, "Unexpected ref count %ld\n", ref); + } + if (voice2 && voice2 != (IVoiceInformation *)0xdeadbeef){ + ref = IVoiceInformation_Release(voice2); + ok(ref == 1, "Unexpected ref count %ld\n", ref); + } + voice = (IVoiceInformation *)0xdeadbeef; hr = IVectorView_VoiceInformation_GetAt(voices, size, &voice); ok(hr == E_BOUNDS, "IVectorView_VoiceInformation_GetAt failed, hr %#lx\n", hr); @@ -959,6 +976,16 @@ static void test_SpeechSynthesizer(void) ok(hr == S_OK, "IVectorView_VoiceInformation_GetMany failed, hr %#lx\n", hr); ok(size == 0, "IVectorView_VoiceInformation_GetMany returned count %u\n", size); + hr = IVectorView_VoiceInformation_GetMany(voices, 0, 1, &voice, &size); + ok(hr == S_OK, "IVectorView_VoiceInformation_GetMany failed, hr %#lx\n", hr); + todo_wine ok(size == 1, "IVectorView_VoiceInformation_GetMany returned count %u\n", size); + ok(voice == voice2, "Didn't get the same object\n"); + if (size) + { + ref = IVoiceInformation_Release(voice); + ok(ref == 1, "Unexpected ref count %ld\n", ref); + } + hr = IInstalledVoicesStatic_get_DefaultVoice(voices_static, &voice); todo_wine ok(hr == S_OK, "IInstalledVoicesStatic_get_DefaultVoice failed, hr %#lx\n", hr); @@ -980,6 +1007,9 @@ static void test_SpeechSynthesizer(void) ok(ref == 0, "Got unexpected ref %lu.\n", ref); } + ref = IVectorView_VoiceInformation_Release(voices); + ok(!ref, "Got unexpected ref %lu.\n", ref); + IInstalledVoicesStatic_Release(voices_static); IAgileObject_Release(agile_object); IInspectable_Release(inspectable); -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/3048
From: Bernhard Kölbl <bkoelbl@codeweavers.com> Based on patch by Eric Pouech. --- dlls/windows.media.speech/synthesizer.c | 76 +++++++++++++++++++++---- 1 file changed, 65 insertions(+), 11 deletions(-) diff --git a/dlls/windows.media.speech/synthesizer.c b/dlls/windows.media.speech/synthesizer.c index 3bb9d2069e4..229e119c75b 100644 --- a/dlls/windows.media.speech/synthesizer.c +++ b/dlls/windows.media.speech/synthesizer.c @@ -33,6 +33,12 @@ struct voice_information_vector { IVectorView_VoiceInformation IVectorView_VoiceInformation_iface; LONG ref; + + struct voices_provider + { + struct IVoiceInformation **voices; + unsigned num_voices; + } provider; }; static inline struct voice_information_vector *impl_from_IVectorView_VoiceInformation( IVectorView_VoiceInformation *iface ) @@ -44,7 +50,7 @@ static HRESULT WINAPI vector_view_voice_information_QueryInterface( IVectorView_ { struct voice_information_vector *impl = impl_from_IVectorView_VoiceInformation(iface); - TRACE("iface %p, iid %s, out %p stub!\n", iface, debugstr_guid(iid), out); + TRACE("iface %p, iid %s, out %p\n", iface, debugstr_guid(iid), out); if (IsEqualGUID(iid, &IID_IUnknown) || IsEqualGUID(iid, &IID_IInspectable) || @@ -71,7 +77,20 @@ static ULONG WINAPI vector_view_voice_information_Release( IVectorView_VoiceInfo { struct voice_information_vector *impl = impl_from_IVectorView_VoiceInformation(iface); ULONG ref = InterlockedDecrement(&impl->ref); + UINT32 i; + TRACE("iface %p, ref %lu.\n", iface, ref); + + if (!ref) + { + for (i = 0; i < impl->provider.num_voices; ++i) + { + IVoiceInformation_Release(impl->provider.voices[i]); + impl->provider.voices[i] = 0; + } + impl->provider.num_voices = 0; + } + return ref; } @@ -95,23 +114,44 @@ static HRESULT WINAPI vector_view_voice_information_GetTrustLevel( IVectorView_V static HRESULT WINAPI vector_view_voice_information_GetAt( IVectorView_VoiceInformation *iface, UINT32 index, IVoiceInformation **value ) { - FIXME("iface %p, index %#x, value %p stub!\n", iface, index, value); - *value = NULL; - return E_BOUNDS; + struct voice_information_vector *impl = impl_from_IVectorView_VoiceInformation(iface); + + TRACE("iface %p, index %#x, value %p\n", iface, index, value); + + if (index >= impl->provider.num_voices) + { + *value = NULL; + return E_BOUNDS; + } + + IVoiceInformation_AddRef((*value = impl->provider.voices[index])); + return S_OK; } static HRESULT WINAPI vector_view_voice_information_get_Size( IVectorView_VoiceInformation *iface, UINT32 *value ) { - FIXME("iface %p, value %p stub!\n", iface, value); - *value = 0; + struct voice_information_vector *impl = impl_from_IVectorView_VoiceInformation(iface); + TRACE("iface %p, value %p\n", iface, value); + *value = impl->provider.num_voices; return S_OK; } static HRESULT WINAPI vector_view_voice_information_IndexOf( IVectorView_VoiceInformation *iface, IVoiceInformation *element, UINT32 *index, BOOLEAN *found ) { - FIXME("iface %p, element %p, index %p, found %p stub!\n", iface, element, index, found); - *index = 0; + struct voice_information_vector *impl = impl_from_IVectorView_VoiceInformation(iface); + UINT32 i; + + TRACE("iface %p, element %p, index %p, found %p\n", iface, element, index, found); + + for (i = 0; i < impl->provider.num_voices; i++) + if (element == impl->provider.voices[i]) + { + *index = i; + *found = TRUE; + return S_OK; + } + *found = FALSE; return S_OK; } @@ -119,8 +159,21 @@ static HRESULT WINAPI vector_view_voice_information_IndexOf( IVectorView_VoiceIn static HRESULT WINAPI vector_view_voice_information_GetMany( IVectorView_VoiceInformation *iface, UINT32 start_index, UINT32 items_size, IVoiceInformation **items, UINT *value ) { - FIXME("iface %p, start_index %#x, items %p, value %p stub!\n", iface, start_index, items, value); - *value = 0; + struct voice_information_vector *impl = impl_from_IVectorView_VoiceInformation(iface); + UINT32 i; + + TRACE("iface %p, start_index %#x, items %p, value %p\n", iface, start_index, items, value); + + if (start_index >= impl->provider.num_voices) + { + *value = 0; + return S_OK; + } + + *value = min(impl->provider.num_voices - start_index, items_size); + for (i = 0; i < *value; i++) + IVoiceInformation_AddRef(items[i] = impl->provider.voices[start_index + i]); + return S_OK; } @@ -143,7 +196,8 @@ static const struct IVectorView_VoiceInformationVtbl vector_view_voice_informati static struct voice_information_vector all_voices = { {&vector_view_voice_information_vtbl}, - 0 + 0, + {0}, }; /* -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/3048
From: Eric Pouech <epouech@codeweavers.com> Signed-off-by: Eric Pouech <epouech@codeweavers.com> --- dlls/windows.media.speech/synthesizer.c | 234 ++++++++++++++++++++++- dlls/windows.media.speech/tests/speech.c | 8 +- 2 files changed, 235 insertions(+), 7 deletions(-) diff --git a/dlls/windows.media.speech/synthesizer.c b/dlls/windows.media.speech/synthesizer.c index 229e119c75b..5b33f1ab7fd 100644 --- a/dlls/windows.media.speech/synthesizer.c +++ b/dlls/windows.media.speech/synthesizer.c @@ -23,6 +23,195 @@ WINE_DEFAULT_DEBUG_CHANNEL(speech); +/* + * + * IVoiceInformation + * + */ +struct voice_information +{ + IVoiceInformation IVoiceInformation_iface; + LONG ref; + + HSTRING id; + HSTRING display_name; + HSTRING language; + HSTRING description; + VoiceGender gender; +}; + +static inline struct voice_information *impl_from_IVoiceInformation( IVoiceInformation *iface ) +{ + return CONTAINING_RECORD(iface, struct voice_information, IVoiceInformation_iface); +} + +static void voice_information_delete( struct voice_information *voice_info ) +{ + WindowsDeleteString(voice_info->id); + WindowsDeleteString(voice_info->display_name); + WindowsDeleteString(voice_info->language); + WindowsDeleteString(voice_info->description); + free(voice_info); +} + +static HRESULT WINAPI voice_information_QueryInterface( IVoiceInformation *iface, REFIID iid, void **out ) +{ + struct voice_information *impl = impl_from_IVoiceInformation( iface ); + + TRACE("iface %p, riid %s, out %p\n", iface, wine_dbgstr_guid(iid), out); + + if (IsEqualGUID(iid, &IID_IUnknown) || + IsEqualGUID(iid, &IID_IInspectable) || + IsEqualGUID(iid, &IID_IVoiceInformation)) + { + IInspectable_AddRef((*out = &impl->IVoiceInformation_iface)); + return S_OK; + } + + WARN("%s not implemented, returning E_NOINTERFACE.\n", debugstr_guid(iid)); + *out = NULL; + return E_NOINTERFACE; +} + +static ULONG WINAPI voice_information_AddRef( IVoiceInformation *iface ) +{ + struct voice_information *impl = impl_from_IVoiceInformation(iface); + ULONG ref = InterlockedIncrement(&impl->ref); + TRACE("iface %p, ref %lu.\n", iface, ref); + return ref; +} + +static ULONG WINAPI voice_information_Release( IVoiceInformation *iface ) +{ + struct voice_information *impl = impl_from_IVoiceInformation(iface); + ULONG ref = InterlockedDecrement(&impl->ref); + TRACE("iface %p, ref %lu.\n", iface, ref); + + if (!ref) + voice_information_delete(impl); + + return ref; +} + +static HRESULT WINAPI voice_information_GetIids( IVoiceInformation *iface, ULONG *iid_count, IID **iids ) +{ + FIXME("iface %p, iid_count %p, iids %p stub!\n", iface, iid_count, iids); + return E_NOTIMPL; +} + +static HRESULT WINAPI voice_information_GetRuntimeClassName( IVoiceInformation *iface, HSTRING *class_name ) +{ + FIXME("iface %p, class_name %p stub!\n", iface, class_name); + return E_NOTIMPL; +} + +static HRESULT WINAPI voice_information_GetTrustLevel( IVoiceInformation *iface, TrustLevel *trust_level ) +{ + FIXME("iface %p, trust_level %p stub!\n", iface, trust_level); + return E_NOTIMPL; +} + +static HRESULT WINAPI voice_information_get_DisplayName( IVoiceInformation *iface, HSTRING *value ) +{ + struct voice_information *impl = impl_from_IVoiceInformation(iface); + + TRACE("iface %p, value %p!n", iface, value); + return WindowsDuplicateString(impl->display_name, value); +} + +static HRESULT WINAPI voice_information_get_Id( IVoiceInformation *iface, HSTRING *value ) +{ + struct voice_information *impl = impl_from_IVoiceInformation(iface); + + TRACE("iface %p, value %p\n", iface, value); + return WindowsDuplicateString(impl->id, value); +} + +static HRESULT WINAPI voice_information_get_Language( IVoiceInformation *iface, HSTRING *value ) +{ + struct voice_information *impl = impl_from_IVoiceInformation(iface); + + TRACE("iface %p, value %p\n", iface, value); + return WindowsDuplicateString(impl->language, value); +} + +static HRESULT WINAPI voice_information_get_Description( IVoiceInformation *iface, HSTRING *value ) +{ + struct voice_information *impl = impl_from_IVoiceInformation(iface); + + TRACE("iface %p, value %p\n", iface, value); + return WindowsDuplicateString(impl->description, value); +} + +static HRESULT WINAPI voice_information_get_Gender( IVoiceInformation *iface, VoiceGender *value ) +{ + struct voice_information *impl = impl_from_IVoiceInformation(iface); + + TRACE("iface %p, value %p\n", iface, value); + *value = impl->gender; + return S_OK; +} + +static const struct IVoiceInformationVtbl voice_information_vtbl = +{ + /* IUnknown methods */ + voice_information_QueryInterface, + voice_information_AddRef, + voice_information_Release, + /* IInspectable methods */ + voice_information_GetIids, + voice_information_GetRuntimeClassName, + voice_information_GetTrustLevel, + /* IVoiceInformation methods */ + voice_information_get_DisplayName, + voice_information_get_Id, + voice_information_get_Language, + voice_information_get_Description, + voice_information_get_Gender, +}; + +static HRESULT voice_information_create( const WCHAR *display_name, const WCHAR *id, const WCHAR *locale, + VoiceGender gender, IVoiceInformation **out ) +{ + struct voice_information *voice_info; + WCHAR *description; + HRESULT hr; + size_t len, langlen; + + if (!(voice_info = calloc(1, sizeof(*voice_info)))) return E_OUTOFMEMORY; + + len = wcslen(display_name) + 3; + langlen = GetLocaleInfoEx(locale, LOCALE_SLOCALIZEDDISPLAYNAME, NULL, 0); + description = malloc((len + langlen) * sizeof(WCHAR)); + wcscpy(description, display_name); + wcscat(description, L" - "); + GetLocaleInfoEx(locale, LOCALE_SLOCALIZEDDISPLAYNAME, description + len, langlen); + + hr = WindowsCreateString(display_name, wcslen(display_name), &voice_info->display_name); + if (SUCCEEDED(hr)) + hr = WindowsCreateString(id, wcslen(id), &voice_info->id); + if (SUCCEEDED(hr)) + hr = WindowsCreateString(locale, wcslen(locale), &voice_info->language); + if (SUCCEEDED(hr)) + hr = WindowsCreateString(description, len + langlen - 1, &voice_info->description); + if (SUCCEEDED(hr)) + { + voice_info->gender = gender; + voice_info->ref = 1; + voice_info->IVoiceInformation_iface.lpVtbl = &voice_information_vtbl; + + *out = &voice_info->IVoiceInformation_iface; + + TRACE("created IVoiceInformation %p.\n", *out); + } + else + { + voice_information_delete(voice_info); + } + free(description); + return hr; +} + /* * * IVectorView_VoiceInformation @@ -1464,6 +1653,28 @@ static const struct IActivationFactoryVtbl factory_vtbl = factory_ActivateInstance, }; +static HRESULT static_installed_voices_init(void) +{ + WCHAR locale[LOCALE_NAME_MAX_LENGTH]; + HRESULT hr; + + if (all_voices.provider.num_voices) + return S_OK; + + if (GetUserDefaultLocaleName(locale, ARRAY_SIZE(locale)) > ARRAY_SIZE(locale)) + return E_OUTOFMEMORY; + + if (!(all_voices.provider.voices = calloc(1, sizeof(all_voices.provider.voices[0])))) + return E_OUTOFMEMORY; + + if (FAILED(hr = voice_information_create(L"Dummy voice", L"--void--", locale, VoiceGender_Male, &all_voices.provider.voices[0]))) + free(all_voices.provider.voices); + else + all_voices.provider.num_voices = 1; + + return hr; +} + /* * * IInstalledVoicesStatic for SpeechSynthesizer runtimeclass @@ -1472,12 +1683,29 @@ static const struct IActivationFactoryVtbl factory_vtbl = DEFINE_IINSPECTABLE(installed_voices_static, IInstalledVoicesStatic, struct synthesizer_statics, IActivationFactory_iface) +static CRITICAL_SECTION allvoices_cs; +static CRITICAL_SECTION_DEBUG allvoices_critsect_debug = +{ + 0, 0, &allvoices_cs, + { &allvoices_critsect_debug.ProcessLocksList, &allvoices_critsect_debug.ProcessLocksList }, + 0, 0, { (DWORD_PTR)(__FILE__ ": allvoices_cs") } +}; +static CRITICAL_SECTION allvoices_cs = { &allvoices_critsect_debug, -1, 0, 0, 0, 0 }; + static HRESULT WINAPI installed_voices_static_get_AllVoices( IInstalledVoicesStatic *iface, IVectorView_VoiceInformation **value ) { + HRESULT hr; + TRACE("iface %p, value %p.\n", iface, value); - *value = &all_voices.IVectorView_VoiceInformation_iface; - IVectorView_VoiceInformation_AddRef(*value); - return S_OK; + + EnterCriticalSection(&allvoices_cs); + + if (SUCCEEDED(hr = static_installed_voices_init())) + IVectorView_VoiceInformation_AddRef((*value = &all_voices.IVectorView_VoiceInformation_iface)); + + LeaveCriticalSection(&allvoices_cs); + + return hr; } static HRESULT WINAPI installed_voices_static_get_DefaultVoice( IInstalledVoicesStatic *iface, IVoiceInformation **value ) diff --git a/dlls/windows.media.speech/tests/speech.c b/dlls/windows.media.speech/tests/speech.c index c5025e9898e..3f01b93be44 100644 --- a/dlls/windows.media.speech/tests/speech.c +++ b/dlls/windows.media.speech/tests/speech.c @@ -948,14 +948,14 @@ static void test_SpeechSynthesizer(void) size = 0xdeadbeef; hr = IVectorView_VoiceInformation_get_Size(voices, &size); ok(hr == S_OK, "IVectorView_VoiceInformation_get_Size voices failed, hr %#lx\n", hr); - todo_wine ok(size != 0 && size != 0xdeadbeef, "IVectorView_VoiceInformation_get_Size returned %u\n", size); + ok(size != 0 && size != 0xdeadbeef, "IVectorView_VoiceInformation_get_Size returned %u\n", size); voice = (IVoiceInformation *)0xdeadbeef; hr = IVectorView_VoiceInformation_GetAt(voices, 0, &voice); - todo_wine ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr); + ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr); hr = IVectorView_VoiceInformation_GetAt(voices, 0, &voice2); - todo_wine ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr); + ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr); ok(voice == voice2, "Didn't get the same object\n"); if (voice && voice != (IVoiceInformation *)0xdeadbeef){ @@ -978,7 +978,7 @@ static void test_SpeechSynthesizer(void) hr = IVectorView_VoiceInformation_GetMany(voices, 0, 1, &voice, &size); ok(hr == S_OK, "IVectorView_VoiceInformation_GetMany failed, hr %#lx\n", hr); - todo_wine ok(size == 1, "IVectorView_VoiceInformation_GetMany returned count %u\n", size); + ok(size == 1, "IVectorView_VoiceInformation_GetMany returned count %u\n", size); ok(voice == voice2, "Didn't get the same object\n"); if (size) { -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/3048
From: Eric Pouech <epouech@codeweavers.com> Signed-off-by: Eric Pouech <epouech@codeweavers.com> --- dlls/windows.media.speech/tests/speech.c | 34 +++++++++++++++++++++--- 1 file changed, 31 insertions(+), 3 deletions(-) diff --git a/dlls/windows.media.speech/tests/speech.c b/dlls/windows.media.speech/tests/speech.c index 3f01b93be44..49fafee07a0 100644 --- a/dlls/windows.media.speech/tests/speech.c +++ b/dlls/windows.media.speech/tests/speech.c @@ -861,7 +861,7 @@ static void test_SpeechSynthesizer(void) IClosable *closable; struct async_inspectable_handler async_inspectable_handler; HMODULE hdll; - HSTRING str, str2; + HSTRING str, str2, default_voice_id; UINT64 value; HRESULT hr; UINT32 size, idx; @@ -999,13 +999,18 @@ static void test_SpeechSynthesizer(void) check_comparable_presence(voices, voice); - IVoiceInformation_get_Description(voice, &str2); + hr = IVoiceInformation_get_Description(voice, &str2); + ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr); trace("SpeechSynthesizer default voice %s.\n", debugstr_hstring(str2)); - WindowsDeleteString(str2); + + hr = IVoiceInformation_get_Id(voice, &default_voice_id); + ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr); + ref = IVoiceInformation_Release(voice); ok(ref == 0, "Got unexpected ref %lu.\n", ref); } + else default_voice_id = NULL; ref = IVectorView_VoiceInformation_Release(voices); ok(!ref, "Got unexpected ref %lu.\n", ref); @@ -1025,6 +1030,29 @@ static void test_SpeechSynthesizer(void) hr = IInspectable_QueryInterface(inspectable, &IID_ISpeechSynthesizer, (void **)&synthesizer); ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr); + hr = ISpeechSynthesizer_get_Voice(synthesizer, &voice); + todo_wine ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr); + + if (default_voice_id) + { + if (hr == S_OK) + { + INT32 cmp; + hr = IVoiceInformation_get_Id(voice, &str); + ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr); + hr = WindowsCompareStringOrdinal(str, default_voice_id, &cmp); + ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr); + + hr = WindowsDeleteString(str); + ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr); + + IVoiceInformation_Release(voice); + } + + hr = WindowsDeleteString(default_voice_id); + ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr); + } + /* Test SynthesizeTextToStreamAsync */ hr = WindowsCreateString(simple_synth_text, wcslen(simple_synth_text), &str); ok(hr == S_OK, "WindowsCreateString failed, hr %#lx\n", hr); -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/3048
From: Eric Pouech <epouech@codeweavers.com> Signed-off-by: Eric Pouech <epouech@codeweavers.com> --- dlls/windows.media.speech/synthesizer.c | 47 +++++++++++++++++- dlls/windows.media.speech/tests/speech.c | 61 +++++++++++------------- 2 files changed, 72 insertions(+), 36 deletions(-) diff --git a/dlls/windows.media.speech/synthesizer.c b/dlls/windows.media.speech/synthesizer.c index 5b33f1ab7fd..c07cb40a335 100644 --- a/dlls/windows.media.speech/synthesizer.c +++ b/dlls/windows.media.speech/synthesizer.c @@ -212,6 +212,35 @@ static HRESULT voice_information_create( const WCHAR *display_name, const WCHAR return hr; } +HRESULT voice_information_clone( IVoiceInformation *voice, IVoiceInformation **out ) +{ + struct voice_information *voice_info; + HRESULT hr; + + if (!(voice_info = calloc(1, sizeof(*voice_info)))) + return E_OUTOFMEMORY; + + hr = IVoiceInformation_get_DisplayName(voice, &voice_info->display_name); + if (SUCCEEDED(hr)) + hr = IVoiceInformation_get_Id(voice, &voice_info->id); + if (SUCCEEDED(hr)) + hr = IVoiceInformation_get_Language(voice, &voice_info->language); + if (SUCCEEDED(hr)) + hr = IVoiceInformation_get_Description(voice, &voice_info->description); + if (SUCCEEDED(hr)) + hr = IVoiceInformation_get_Gender(voice, &voice_info->gender); + if (SUCCEEDED(hr)) + { + voice_info->IVoiceInformation_iface.lpVtbl = &voice_information_vtbl; + voice_info->ref = 1; + *out = &voice_info->IVoiceInformation_iface; + } + else + voice_information_delete(voice_info); + + return hr; +} + /* * * IVectorView_VoiceInformation @@ -1710,8 +1739,22 @@ static HRESULT WINAPI installed_voices_static_get_AllVoices( IInstalledVoicesSta static HRESULT WINAPI installed_voices_static_get_DefaultVoice( IInstalledVoicesStatic *iface, IVoiceInformation **value ) { - FIXME("iface %p, value %p stub!\n", iface, value); - return E_NOTIMPL; + struct IVoiceInformation *static_voice; + HRESULT hr; + + TRACE("iface %p, value %p\n", iface, value); + + EnterCriticalSection(&allvoices_cs); + + if (SUCCEEDED(hr = IVectorView_VoiceInformation_GetAt(&all_voices.IVectorView_VoiceInformation_iface, 0, &static_voice))) + { + hr = voice_information_clone(static_voice, value); + IVoiceInformation_Release(static_voice); + } + + LeaveCriticalSection(&allvoices_cs); + + return hr; } static const struct IInstalledVoicesStaticVtbl installed_voices_static_vtbl = diff --git a/dlls/windows.media.speech/tests/speech.c b/dlls/windows.media.speech/tests/speech.c index 49fafee07a0..9798304cf07 100644 --- a/dlls/windows.media.speech/tests/speech.c +++ b/dlls/windows.media.speech/tests/speech.c @@ -987,30 +987,26 @@ static void test_SpeechSynthesizer(void) } hr = IInstalledVoicesStatic_get_DefaultVoice(voices_static, &voice); - todo_wine ok(hr == S_OK, "IInstalledVoicesStatic_get_DefaultVoice failed, hr %#lx\n", hr); + ok(hr == S_OK, "IInstalledVoicesStatic_get_DefaultVoice failed, hr %#lx\n", hr); - if (hr == S_OK) - { - /* check that VoiceInformation in static vector voice are not shared when exposed to user */ - idx = size; - hr = IVectorView_VoiceInformation_IndexOf(voices, voice, &idx, &found); - ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr); - ok(!found, "Shouldn't find default element\n"); + /* check that VoiceInformation in static vector voice are not shared when exposed to user */ + idx = size; + hr = IVectorView_VoiceInformation_IndexOf(voices, voice, &idx, &found); + ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr); + ok(!found, "Shouldn't find default element\n"); - check_comparable_presence(voices, voice); + check_comparable_presence(voices, voice); - hr = IVoiceInformation_get_Description(voice, &str2); - ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr); - trace("SpeechSynthesizer default voice %s.\n", debugstr_hstring(str2)); - WindowsDeleteString(str2); + hr = IVoiceInformation_get_Description(voice, &str2); + ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr); + trace("SpeechSynthesizer default voice %s.\n", debugstr_hstring(str2)); + WindowsDeleteString(str2); - hr = IVoiceInformation_get_Id(voice, &default_voice_id); - ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr); + hr = IVoiceInformation_get_Id(voice, &default_voice_id); + ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr); - ref = IVoiceInformation_Release(voice); - ok(ref == 0, "Got unexpected ref %lu.\n", ref); - } - else default_voice_id = NULL; + ref = IVoiceInformation_Release(voice); + ok(ref == 0, "Got unexpected ref %lu.\n", ref); ref = IVectorView_VoiceInformation_Release(voices); ok(!ref, "Got unexpected ref %lu.\n", ref); @@ -1033,26 +1029,23 @@ static void test_SpeechSynthesizer(void) hr = ISpeechSynthesizer_get_Voice(synthesizer, &voice); todo_wine ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr); - if (default_voice_id) + if (hr == S_OK) { - if (hr == S_OK) - { - INT32 cmp; - hr = IVoiceInformation_get_Id(voice, &str); - ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr); - hr = WindowsCompareStringOrdinal(str, default_voice_id, &cmp); - ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr); - - hr = WindowsDeleteString(str); - ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr); - - IVoiceInformation_Release(voice); - } + INT32 cmp; + hr = IVoiceInformation_get_Id(voice, &str); + ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr); + hr = WindowsCompareStringOrdinal(str, default_voice_id, &cmp); + ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr); - hr = WindowsDeleteString(default_voice_id); + hr = WindowsDeleteString(str); ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr); + + IVoiceInformation_Release(voice); } + hr = WindowsDeleteString(default_voice_id); + ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr); + /* Test SynthesizeTextToStreamAsync */ hr = WindowsCreateString(simple_synth_text, wcslen(simple_synth_text), &str); ok(hr == S_OK, "WindowsCreateString failed, hr %#lx\n", hr); -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/3048
From: Eric Pouech <epouech@codeweavers.com> Signed-off-by: Eric Pouech <epouech@codeweavers.com> --- dlls/windows.media.speech/synthesizer.c | 63 ++++++++++++++++++++++-- dlls/windows.media.speech/tests/speech.c | 22 ++++----- 2 files changed, 69 insertions(+), 16 deletions(-) diff --git a/dlls/windows.media.speech/synthesizer.c b/dlls/windows.media.speech/synthesizer.c index c07cb40a335..bbe6181c1d4 100644 --- a/dlls/windows.media.speech/synthesizer.c +++ b/dlls/windows.media.speech/synthesizer.c @@ -1335,6 +1335,7 @@ struct synthesizer LONG ref; struct synthesizer_options *options; + IVoiceInformation *current_voice; }; /* @@ -1397,6 +1398,7 @@ static ULONG WINAPI synthesizer_Release( ISpeechSynthesizer *iface ) if (!ref) { ISpeechSynthesizerOptions_Release(&impl->options->ISpeechSynthesizerOptions_iface); + IVoiceInformation_Release(impl->current_voice); free(impl); } @@ -1469,14 +1471,47 @@ static HRESULT WINAPI synthesizer_SynthesizeSsmlToStreamAsync( ISpeechSynthesize static HRESULT WINAPI synthesizer_put_Voice( ISpeechSynthesizer *iface, IVoiceInformation *value ) { - FIXME("iface %p, value %p stub.\n", iface, value); - return E_NOTIMPL; + struct synthesizer *impl = impl_from_ISpeechSynthesizer(iface); + IVoiceInformation *voice; + HSTRING id, id2; + HRESULT hr; + INT32 cmp, idx; + + TRACE("iface %p, value %p semi-stub.\n", iface, value); + + if (FAILED(hr = IVoiceInformation_get_Id(value, &id))) + return hr; + + for (idx = 0; ; idx++) + { + if (SUCCEEDED(hr = IVectorView_VoiceInformation_GetAt(&all_voices.IVectorView_VoiceInformation_iface, idx, &voice))) + { + if (SUCCEEDED(hr = IVoiceInformation_get_Id(voice, &id2))) + { + hr = WindowsCompareStringOrdinal(id, id2, &cmp); + WindowsDeleteString(id2); + } + IVoiceInformation_Release(voice); + } + if (FAILED(hr) || cmp == 0) break; + } + WindowsDeleteString(id); + + if (SUCCEEDED(hr)) + { + IVoiceInformation_Release(impl->current_voice); + IVoiceInformation_AddRef(impl->current_voice = value); + } + return hr; } static HRESULT WINAPI synthesizer_get_Voice( ISpeechSynthesizer *iface, IVoiceInformation **value ) { - FIXME("iface %p, value %p stub.\n", iface, value); - return E_NOTIMPL; + struct synthesizer *impl = impl_from_ISpeechSynthesizer(iface); + + TRACE("iface %p, value %p.\n", iface, value); + IVoiceInformation_AddRef((*value = impl->current_voice)); + return S_OK; } static const struct ISpeechSynthesizerVtbl synthesizer_vtbl = @@ -1641,6 +1676,9 @@ static HRESULT WINAPI factory_GetTrustLevel( IActivationFactory *iface, TrustLev static HRESULT WINAPI factory_ActivateInstance( IActivationFactory *iface, IInspectable **instance ) { + struct synthesizer_statics *statics = impl_from_IActivationFactory(iface); + IVectorView_VoiceInformation *voice_infos = NULL; + IVoiceInformation *static_voice = NULL; struct synthesizer *impl; HRESULT hr; @@ -1655,6 +1693,20 @@ static HRESULT WINAPI factory_ActivateInstance( IActivationFactory *iface, IInsp if (FAILED(hr = synthesizer_options_create(&impl->options))) goto failed; + /* make sure the provider is initialized */ + if (FAILED(hr = IInstalledVoicesStatic_get_AllVoices(&statics->IInstalledVoicesStatic_iface, &voice_infos))) + goto failed; + + /* assuming default is the first one... */ + if (FAILED(hr = IVectorView_VoiceInformation_GetAt(voice_infos, 0, &static_voice))) + goto failed; + + if (FAILED(hr = voice_information_clone(static_voice, &impl->current_voice))) + goto failed; + + IVoiceInformation_Release(static_voice); + IVectorView_VoiceInformation_Release(voice_infos); + impl->ISpeechSynthesizer_iface.lpVtbl = &synthesizer_vtbl; impl->ISpeechSynthesizer2_iface.lpVtbl = &synthesizer2_vtbl; impl->IClosable_iface.lpVtbl = &closable_vtbl; @@ -1664,6 +1716,9 @@ static HRESULT WINAPI factory_ActivateInstance( IActivationFactory *iface, IInsp return S_OK; failed: + if (static_voice) IVoiceInformation_Release(static_voice); + if (voice_infos) IVectorView_VoiceInformation_Release(voice_infos); + if (impl->options) ISpeechSynthesizerOptions_Release(&impl->options->ISpeechSynthesizerOptions_iface); free(impl); *instance = NULL; return hr; diff --git a/dlls/windows.media.speech/tests/speech.c b/dlls/windows.media.speech/tests/speech.c index 9798304cf07..0146f635b2a 100644 --- a/dlls/windows.media.speech/tests/speech.c +++ b/dlls/windows.media.speech/tests/speech.c @@ -867,6 +867,7 @@ static void test_SpeechSynthesizer(void) UINT32 size, idx; BOOLEAN found; ULONG ref; + INT32 cmp; hr = RoInitialize(RO_INIT_MULTITHREADED); ok(hr == S_OK, "RoInitialize failed, hr %#lx\n", hr); @@ -1027,21 +1028,18 @@ static void test_SpeechSynthesizer(void) ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr); hr = ISpeechSynthesizer_get_Voice(synthesizer, &voice); - todo_wine ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr); + ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr); - if (hr == S_OK) - { - INT32 cmp; - hr = IVoiceInformation_get_Id(voice, &str); - ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr); - hr = WindowsCompareStringOrdinal(str, default_voice_id, &cmp); - ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr); + hr = IVoiceInformation_get_Id(voice, &str); + ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr); - hr = WindowsDeleteString(str); - ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr); + hr = WindowsCompareStringOrdinal(str, default_voice_id, &cmp); + ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr); - IVoiceInformation_Release(voice); - } + hr = WindowsDeleteString(str); + ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr); + + IVoiceInformation_Release(voice); hr = WindowsDeleteString(default_voice_id); ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr); -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/3048
Got rid of some laking code. -- https://gitlab.winehq.org/wine/wine/-/merge_requests/3048#note_131474
This merge request was approved by Bernhard Kölbl. -- https://gitlab.winehq.org/wine/wine/-/merge_requests/3048
participants (3)
-
Bernhard Kölbl -
Eric Pouech -
eric pouech (@epo)