| Index: chrome/browser/speech/tts_win.cc
|
| diff --git a/chrome/browser/speech/tts_win.cc b/chrome/browser/speech/tts_win.cc
|
| index 1602fc246d166b41c9b471d43140cf202cdf1771..5df7f82156b1cea70c8551ce9756bc700922809f 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),
|
|
|