OLD | NEW |
1 // Copyright (c) 2010 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2010 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "chrome/browser/extensions/extension_tts_api.h" | 5 #include "chrome/browser/extensions/extension_tts_api.h" |
6 | 6 |
7 #include <atlbase.h> | 7 #include <atlbase.h> |
8 #include <atlcom.h> | 8 #include <atlcom.h> |
9 #include <sapi.h> | 9 #include <sapi.h> |
10 | 10 |
11 #include "base/scoped_comptr_win.h" | 11 #include "base/scoped_comptr_win.h" |
12 #include "base/singleton.h" | 12 #include "base/singleton.h" |
13 #include "base/string_number_conversions.h" | 13 #include "base/string_number_conversions.h" |
| 14 #include "base/utf_string_conversions.h" |
14 #include "base/values.h" | 15 #include "base/values.h" |
15 | 16 |
16 namespace util = extension_tts_api_util; | 17 namespace util = extension_tts_api_util; |
17 | 18 |
18 class SpeechSynthesizerWrapper { | 19 class ExtensionTtsPlatformImplWin : public ExtensionTtsPlatformImpl { |
19 public: | 20 public: |
20 SpeechSynthesizerWrapper() : speech_synthesizer_(NULL), | 21 virtual bool Speak( |
21 paused_(false), | 22 const std::string& utterance, |
22 permanent_failure_(false) { | 23 const std::string& language, |
23 InitializeSpeechSynthesizer(); | 24 const std::string& gender, |
| 25 double rate, |
| 26 double pitch, |
| 27 double volume); |
| 28 |
| 29 virtual bool StopSpeaking(); |
| 30 |
| 31 virtual bool IsSpeaking(); |
| 32 |
| 33 // Get the single instance of this class. |
| 34 static ExtensionTtsPlatformImplWin* GetInstance(); |
| 35 |
| 36 private: |
| 37 ExtensionTtsPlatformImplWin(); |
| 38 virtual ~ExtensionTtsPlatformImplWin() {} |
| 39 |
| 40 ScopedComPtr<ISpVoice> speech_synthesizer_; |
| 41 bool paused_; |
| 42 |
| 43 friend struct DefaultSingletonTraits<ExtensionTtsPlatformImplWin>; |
| 44 |
| 45 DISALLOW_COPY_AND_ASSIGN(ExtensionTtsPlatformImplWin); |
| 46 }; |
| 47 |
| 48 // static |
| 49 ExtensionTtsPlatformImpl* ExtensionTtsPlatformImpl::GetInstance() { |
| 50 return ExtensionTtsPlatformImplWin::GetInstance(); |
| 51 } |
| 52 |
| 53 bool ExtensionTtsPlatformImplWin::Speak( |
| 54 const std::string& src_utterance, |
| 55 const std::string& language, |
| 56 const std::string& gender, |
| 57 double rate, |
| 58 double pitch, |
| 59 double volume) { |
| 60 std::wstring utterance = UTF8ToUTF16(src_utterance); |
| 61 |
| 62 if (!speech_synthesizer_) |
| 63 return false; |
| 64 |
| 65 // Speech API equivalents for kGenderKey and kLanguageNameKey do not |
| 66 // exist and thus are not supported. |
| 67 |
| 68 if (rate >= 0.0) { |
| 69 // The TTS api allows a range of -10 to 10 for speech rate. |
| 70 speech_synthesizer_->SetRate(static_cast<int32>(rate * 20 - 10)); |
24 } | 71 } |
25 | 72 |
26 bool InitializeSpeechSynthesizer() { | 73 if (pitch >= 0.0) { |
27 if (!SUCCEEDED(CoCreateInstance(CLSID_SpVoice, | 74 // The TTS api allows a range of -10 to 10 for speech pitch. |
28 NULL, | 75 // TODO(dtseng): cleanup if we ever use any other properties that |
29 CLSCTX_SERVER, | 76 // require xml. |
30 IID_ISpVoice, | 77 std::wstring pitch_value = |
31 reinterpret_cast<void**>( | 78 base::IntToString16(static_cast<int>(pitch * 20 - 10)); |
32 &speech_synthesizer_)))) { | 79 utterance = L"<pitch absmiddle=\"" + pitch_value + L"\">" + |
33 permanent_failure_ = true; | 80 utterance + L"</pitch>"; |
34 return false; | |
35 } | |
36 | |
37 if (paused_) | |
38 speech_synthesizer_->Resume(); | |
39 return true; | |
40 } | 81 } |
41 | 82 |
42 ScopedComPtr<ISpVoice> speech_synthesizer() { | 83 if (volume >= 0.0) { |
43 return speech_synthesizer_; | 84 // The TTS api allows a range of 0 to 100 for speech volume. |
| 85 speech_synthesizer_->SetVolume(static_cast<uint16>(volume * 100)); |
44 } | 86 } |
45 | 87 |
46 bool paused() { | 88 if (paused_) |
47 return paused_; | 89 speech_synthesizer_->Resume(); |
48 } | 90 speech_synthesizer_->Speak( |
| 91 utterance.c_str(), SPF_ASYNC | SPF_PURGEBEFORESPEAK, NULL); |
49 | 92 |
50 void paused(bool state) { | 93 return true; |
51 paused_ = state; | |
52 } | |
53 | |
54 private: | |
55 ScopedComPtr<ISpVoice> speech_synthesizer_; | |
56 bool paused_; | |
57 // Indicates an error retrieving the SAPI COM interface. | |
58 bool permanent_failure_; | |
59 }; | |
60 | |
61 typedef Singleton<SpeechSynthesizerWrapper> SpeechSynthesizerSingleton; | |
62 | |
63 bool ExtensionTtsSpeakFunction::RunImpl() { | |
64 ScopedComPtr<ISpVoice> speech_synthesizer = | |
65 SpeechSynthesizerSingleton::get()->speech_synthesizer(); | |
66 if (speech_synthesizer) { | |
67 std::wstring utterance; | |
68 EXTENSION_FUNCTION_VALIDATE(args_->GetString(0, &utterance)); | |
69 | |
70 std::string options = ""; | |
71 DictionaryValue* speak_options = NULL; | |
72 | |
73 // Parse speech properties. | |
74 if (args_->GetDictionary(1, &speak_options)) { | |
75 std::string str_value; | |
76 double real_value; | |
77 // Speech API equivalents for kGenderKey and kLanguageNameKey do not | |
78 // exist and thus are not supported. | |
79 if (util::ReadNumberByKey(speak_options, util::kRateKey, &real_value)) { | |
80 // The TTS api allows a range of -10 to 10 for speech rate. | |
81 speech_synthesizer->SetRate(static_cast<int32>(real_value*20 - 10)); | |
82 } | |
83 if (util::ReadNumberByKey(speak_options, util::kPitchKey, &real_value)) { | |
84 // The TTS api allows a range of -10 to 10 for speech pitch. | |
85 // TODO(dtseng): cleanup if we ever | |
86 // use any other properties that require xml. | |
87 std::wstring pitch_value = | |
88 base::IntToString16(static_cast<int>(real_value*20 - 10)); | |
89 utterance = L"<pitch absmiddle=\"" + pitch_value + L"\">" + | |
90 utterance + L"</pitch>"; | |
91 } | |
92 if (util::ReadNumberByKey( | |
93 speak_options, util::kVolumeKey, &real_value)) { | |
94 // The TTS api allows a range of 0 to 100 for speech volume. | |
95 speech_synthesizer->SetVolume(static_cast<uint16>(real_value * 100)); | |
96 } | |
97 } | |
98 | |
99 if (SpeechSynthesizerSingleton::get()->paused()) | |
100 speech_synthesizer->Resume(); | |
101 speech_synthesizer->Speak( | |
102 utterance.c_str(), SPF_ASYNC | SPF_PURGEBEFORESPEAK, NULL); | |
103 return true; | |
104 } | |
105 | |
106 return false; | |
107 } | 94 } |
108 | 95 |
109 bool ExtensionTtsStopSpeakingFunction::RunImpl() { | 96 bool ExtensionTtsPlatformImplWin::StopSpeaking() { |
110 // We need to keep track of the paused state since SAPI doesn't have a stop | 97 if (!speech_synthesizer_ && !paused_) { |
111 // method. | 98 speech_synthesizer_->Pause(); |
112 ScopedComPtr<ISpVoice> speech_synthesizer = | 99 paused_ = true; |
113 SpeechSynthesizerSingleton::get()->speech_synthesizer(); | |
114 if (speech_synthesizer && !SpeechSynthesizerSingleton::get()->paused()) { | |
115 speech_synthesizer->Pause(); | |
116 SpeechSynthesizerSingleton::get()->paused(true); | |
117 } | 100 } |
118 return true; | 101 return true; |
119 } | 102 } |
120 | 103 |
121 bool ExtensionTtsIsSpeakingFunction::RunImpl() { | 104 bool ExtensionTtsPlatformImplWin::IsSpeaking() { |
122 return false; | 105 return false; |
123 } | 106 } |
| 107 |
| 108 ExtensionTtsPlatformImplWin::ExtensionTtsPlatformImplWin() |
| 109 : speech_synthesizer_(NULL), |
| 110 paused_(false) { |
| 111 CoCreateInstance( |
| 112 CLSID_SpVoice, |
| 113 NULL, |
| 114 CLSCTX_SERVER, |
| 115 IID_ISpVoice, |
| 116 reinterpret_cast<void**>(&speech_synthesizer_)); |
| 117 } |
| 118 |
| 119 // static |
| 120 ExtensionTtsPlatformImplWin* ExtensionTtsPlatformImplWin::GetInstance() { |
| 121 return Singleton<ExtensionTtsPlatformImplWin>::get(); |
| 122 } |
OLD | NEW |