diff options
author | dmazzoni@chromium.org <dmazzoni@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-12-14 00:04:25 +0000 |
---|---|---|
committer | dmazzoni@chromium.org <dmazzoni@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-12-14 00:04:25 +0000 |
commit | 28db40c7faa4c0fa772b4467e4afc0a0c3e618c8 (patch) | |
tree | 4b211ac000b3fd616d5561dd8127ce8945accc11 /chrome/browser/speech | |
parent | ae3d357e37bcafa1544a624a0929ea90ef8bd1b2 (diff) | |
download | chromium_src-28db40c7faa4c0fa772b4467e4afc0a0c3e618c8.zip chromium_src-28db40c7faa4c0fa772b4467e4afc0a0c3e618c8.tar.gz chromium_src-28db40c7faa4c0fa772b4467e4afc0a0c3e618c8.tar.bz2 |
Support multiple TTS voices on Windows.
BUG=88059
Review URL: https://codereview.chromium.org/97793002
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@240810 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome/browser/speech')
-rw-r--r-- | chrome/browser/speech/tts_win.cc | 109 |
1 files changed, 95 insertions, 14 deletions
diff --git a/chrome/browser/speech/tts_win.cc b/chrome/browser/speech/tts_win.cc index 1602fc2..5df7f82 100644 --- a/chrome/browser/speech/tts_win.cc +++ b/chrome/browser/speech/tts_win.cc @@ -4,15 +4,27 @@ #include <math.h> #include <sapi.h> +#include <sphelper.h> #include "base/memory/singleton.h" #include "base/strings/string_number_conversions.h" +#include "base/strings/string_piece.h" #include "base/strings/utf_string_conversions.h" #include "base/values.h" +#include "base/win/scoped_co_mem.h" #include "base/win/scoped_comptr.h" #include "chrome/browser/speech/tts_controller.h" #include "chrome/browser/speech/tts_platform.h" +namespace { + +// ISpObjectToken key and value names. +const wchar_t kAttributesKey[] = L"Attributes"; +const wchar_t kGenderValue[] = L"Gender"; +const wchar_t kLanguageValue[] = L"Language"; + +} // anonymous namespace. + class TtsPlatformImplWin : public TtsPlatformImpl { public: virtual bool PlatformImplAvailable() { @@ -47,6 +59,8 @@ class TtsPlatformImplWin : public TtsPlatformImpl { void OnSpeechEvent(); + void SetVoiceFromName(const std::string& name); + base::win::ScopedComPtr<ISpVoice> speech_synthesizer_; // These apply to the current utterance only. @@ -56,6 +70,7 @@ class TtsPlatformImplWin : public TtsPlatformImpl { ULONG stream_number_; int char_position_; bool paused_; + std::string last_voice_name_; friend struct DefaultSingletonTraits<TtsPlatformImplWin>; @@ -79,7 +94,7 @@ bool TtsPlatformImplWin::Speak( if (!speech_synthesizer_.get()) return false; - // TODO(dmazzoni): support languages other than the default: crbug.com/88059 + SetVoiceFromName(voice.name); if (params.rate >= 0.0) { // Map our multiplicative range of 0.1x to 10.0x onto Microsoft's @@ -172,19 +187,57 @@ bool TtsPlatformImplWin::IsSpeaking() { void TtsPlatformImplWin::GetVoices( std::vector<VoiceData>* out_voices) { - // TODO: get all voices, not just default voice. - // http://crbug.com/88059 - out_voices->push_back(VoiceData()); - VoiceData& voice = out_voices->back(); - voice.native = true; - voice.name = "native"; - voice.events.insert(TTS_EVENT_START); - voice.events.insert(TTS_EVENT_END); - voice.events.insert(TTS_EVENT_MARKER); - voice.events.insert(TTS_EVENT_WORD); - voice.events.insert(TTS_EVENT_SENTENCE); - voice.events.insert(TTS_EVENT_PAUSE); - voice.events.insert(TTS_EVENT_RESUME); + base::win::ScopedComPtr<IEnumSpObjectTokens> voice_tokens; + unsigned long voice_count; + if (S_OK != SpEnumTokens(SPCAT_VOICES, NULL, NULL, voice_tokens.Receive())) + return; + if (S_OK != voice_tokens->GetCount(&voice_count)) + return; + + for (unsigned i = 0; i < voice_count; i++) { + VoiceData voice; + + base::win::ScopedComPtr<ISpObjectToken> voice_token; + if (S_OK != voice_tokens->Next(1, voice_token.Receive(), NULL)) + return; + + base::win::ScopedCoMem<WCHAR> description; + if (S_OK != SpGetDescription(voice_token, &description)) + continue; + voice.name = WideToUTF8(description.get()); + + base::win::ScopedComPtr<ISpDataKey> attributes; + if (S_OK != voice_token->OpenKey(kAttributesKey, attributes.Receive())) + continue; + + base::win::ScopedCoMem<WCHAR> gender; + if (S_OK == attributes->GetStringValue(kGenderValue, &gender)) { + if (0 == _wcsicmp(gender.get(), L"male")) + voice.gender = TTS_GENDER_MALE; + else if (0 == _wcsicmp(gender.get(), L"female")) + voice.gender = TTS_GENDER_FEMALE; + } + + base::win::ScopedCoMem<WCHAR> language; + if (S_OK == attributes->GetStringValue(kLanguageValue, &language)) { + int lcid_value; + base::HexStringToInt(WideToUTF8(language.get()), &lcid_value); + LCID lcid = MAKELCID(lcid_value, SORT_DEFAULT); + WCHAR locale_name[LOCALE_NAME_MAX_LENGTH] = {0}; + LCIDToLocaleName(lcid, locale_name, LOCALE_NAME_MAX_LENGTH, 0); + voice.lang = WideToUTF8(locale_name); + } + + voice.native = true; + voice.events.insert(TTS_EVENT_START); + voice.events.insert(TTS_EVENT_END); + voice.events.insert(TTS_EVENT_MARKER); + voice.events.insert(TTS_EVENT_WORD); + voice.events.insert(TTS_EVENT_SENTENCE); + voice.events.insert(TTS_EVENT_PAUSE); + voice.events.insert(TTS_EVENT_RESUME); + out_voices->push_back(voice); + } } void TtsPlatformImplWin::OnSpeechEvent() { @@ -224,6 +277,34 @@ void TtsPlatformImplWin::OnSpeechEvent() { } } +void TtsPlatformImplWin::SetVoiceFromName(const std::string& name) { + if (name.empty() || name == last_voice_name_) + return; + + last_voice_name_ = name; + + base::win::ScopedComPtr<IEnumSpObjectTokens> voice_tokens; + unsigned long voice_count; + if (S_OK != SpEnumTokens(SPCAT_VOICES, NULL, NULL, voice_tokens.Receive())) + return; + if (S_OK != voice_tokens->GetCount(&voice_count)) + return; + + for (unsigned i = 0; i < voice_count; i++) { + base::win::ScopedComPtr<ISpObjectToken> voice_token; + if (S_OK != voice_tokens->Next(1, voice_token.Receive(), NULL)) + return; + + base::win::ScopedCoMem<WCHAR> description; + if (S_OK != SpGetDescription(voice_token, &description)) + continue; + if (name == WideToUTF8(description.get())) { + speech_synthesizer_->SetVoice(voice_token); + break; + } + } +} + TtsPlatformImplWin::TtsPlatformImplWin() : utterance_id_(0), prefix_len_(0), |