| OLD | NEW |
| (Empty) |
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | |
| 2 // Use of this source code is governed by a BSD-style license that can be | |
| 3 // found in the LICENSE file. | |
| 4 | |
| 5 #include "chrome/browser/extensions/extension_tts_api_controller.h" | |
| 6 | |
| 7 #include <string> | |
| 8 #include <vector> | |
| 9 | |
| 10 #include "base/float_util.h" | |
| 11 #include "base/json/json_writer.h" | |
| 12 #include "base/values.h" | |
| 13 #include "chrome/browser/extensions/extension_event_router.h" | |
| 14 #include "chrome/browser/extensions/extension_tts_api.h" | |
| 15 #include "chrome/browser/extensions/extension_tts_api_constants.h" | |
| 16 #include "chrome/browser/extensions/extension_tts_api_platform.h" | |
| 17 #include "chrome/browser/extensions/extension_tts_engine_api.h" | |
| 18 #include "chrome/browser/profiles/profile.h" | |
| 19 #include "chrome/common/extensions/extension.h" | |
| 20 | |
| 21 namespace constants = extension_tts_api_constants; | |
| 22 | |
| 23 namespace { | |
| 24 | |
| 25 // A value to be used to indicate that there is no char index available. | |
| 26 const int kInvalidCharIndex = -1; | |
| 27 | |
| 28 namespace events { | |
| 29 const char kOnEvent[] = "tts.onEvent"; | |
| 30 }; // namespace events | |
| 31 | |
| 32 std::string TtsEventTypeToString(TtsEventType event_type) { | |
| 33 switch (event_type) { | |
| 34 case TTS_EVENT_START: | |
| 35 return constants::kEventTypeStart; | |
| 36 case TTS_EVENT_END: | |
| 37 return constants::kEventTypeEnd; | |
| 38 case TTS_EVENT_WORD: | |
| 39 return constants::kEventTypeWord; | |
| 40 case TTS_EVENT_SENTENCE: | |
| 41 return constants::kEventTypeSentence; | |
| 42 case TTS_EVENT_MARKER: | |
| 43 return constants::kEventTypeMarker; | |
| 44 case TTS_EVENT_INTERRUPTED: | |
| 45 return constants::kEventTypeInterrupted; | |
| 46 case TTS_EVENT_CANCELLED: | |
| 47 return constants::kEventTypeCancelled; | |
| 48 case TTS_EVENT_ERROR: | |
| 49 return constants::kEventTypeError; | |
| 50 default: | |
| 51 NOTREACHED(); | |
| 52 return std::string(); | |
| 53 } | |
| 54 } | |
| 55 | |
| 56 } // namespace | |
| 57 | |
| 58 // | |
| 59 // UtteranceContinuousParameters | |
| 60 // | |
| 61 | |
| 62 | |
| 63 UtteranceContinuousParameters::UtteranceContinuousParameters() | |
| 64 : rate(-1), | |
| 65 pitch(-1), | |
| 66 volume(-1) {} | |
| 67 | |
| 68 | |
| 69 // | |
| 70 // Utterance | |
| 71 // | |
| 72 | |
| 73 // static | |
| 74 int Utterance::next_utterance_id_ = 0; | |
| 75 | |
| 76 Utterance::Utterance(Profile* profile) | |
| 77 : profile_(profile), | |
| 78 id_(next_utterance_id_++), | |
| 79 src_id_(-1), | |
| 80 can_enqueue_(false), | |
| 81 char_index_(0), | |
| 82 finished_(false) { | |
| 83 options_.reset(new DictionaryValue()); | |
| 84 } | |
| 85 | |
| 86 Utterance::~Utterance() { | |
| 87 DCHECK(finished_); | |
| 88 } | |
| 89 | |
| 90 void Utterance::OnTtsEvent(TtsEventType event_type, | |
| 91 int char_index, | |
| 92 const std::string& error_message) { | |
| 93 std::string event_type_string = TtsEventTypeToString(event_type); | |
| 94 if (char_index >= 0) | |
| 95 char_index_ = char_index; | |
| 96 if (event_type == TTS_EVENT_END || | |
| 97 event_type == TTS_EVENT_INTERRUPTED || | |
| 98 event_type == TTS_EVENT_CANCELLED || | |
| 99 event_type == TTS_EVENT_ERROR) { | |
| 100 finished_ = true; | |
| 101 } | |
| 102 if (desired_event_types_.size() > 0 && | |
| 103 desired_event_types_.find(event_type_string) == | |
| 104 desired_event_types_.end()) { | |
| 105 return; | |
| 106 } | |
| 107 | |
| 108 if (src_id_ < 0) | |
| 109 return; | |
| 110 | |
| 111 ListValue args; | |
| 112 DictionaryValue* event = new DictionaryValue(); | |
| 113 if (char_index != kInvalidCharIndex) | |
| 114 event->SetInteger(constants::kCharIndexKey, char_index); | |
| 115 event->SetString(constants::kEventTypeKey, event_type_string); | |
| 116 if (event_type == TTS_EVENT_ERROR) { | |
| 117 event->SetString(constants::kErrorMessageKey, error_message); | |
| 118 } | |
| 119 event->SetInteger(constants::kSrcIdKey, src_id_); | |
| 120 event->SetBoolean(constants::kIsFinalEventKey, finished_); | |
| 121 args.Set(0, event); | |
| 122 std::string json_args; | |
| 123 base::JSONWriter::Write(&args, &json_args); | |
| 124 | |
| 125 profile_->GetExtensionEventRouter()->DispatchEventToExtension( | |
| 126 src_extension_id_, | |
| 127 events::kOnEvent, | |
| 128 json_args, | |
| 129 profile_, | |
| 130 src_url_); | |
| 131 } | |
| 132 | |
| 133 void Utterance::Finish() { | |
| 134 finished_ = true; | |
| 135 } | |
| 136 | |
| 137 void Utterance::set_options(const Value* options) { | |
| 138 options_.reset(options->DeepCopy()); | |
| 139 } | |
| 140 | |
| 141 // | |
| 142 // ExtensionTtsController | |
| 143 // | |
| 144 | |
| 145 // static | |
| 146 ExtensionTtsController* ExtensionTtsController::GetInstance() { | |
| 147 return Singleton<ExtensionTtsController>::get(); | |
| 148 } | |
| 149 | |
| 150 ExtensionTtsController::ExtensionTtsController() | |
| 151 : current_utterance_(NULL), | |
| 152 platform_impl_(NULL) { | |
| 153 } | |
| 154 | |
| 155 ExtensionTtsController::~ExtensionTtsController() { | |
| 156 if (current_utterance_) { | |
| 157 current_utterance_->Finish(); | |
| 158 delete current_utterance_; | |
| 159 } | |
| 160 | |
| 161 // Clear any queued utterances too. | |
| 162 ClearUtteranceQueue(false); // Don't sent events. | |
| 163 } | |
| 164 | |
| 165 void ExtensionTtsController::SpeakOrEnqueue(Utterance* utterance) { | |
| 166 if (IsSpeaking() && utterance->can_enqueue()) { | |
| 167 utterance_queue_.push(utterance); | |
| 168 } else { | |
| 169 Stop(); | |
| 170 SpeakNow(utterance); | |
| 171 } | |
| 172 } | |
| 173 | |
| 174 void ExtensionTtsController::SpeakNow(Utterance* utterance) { | |
| 175 const Extension* extension; | |
| 176 size_t voice_index; | |
| 177 if (GetMatchingExtensionVoice(utterance, &extension, &voice_index)) { | |
| 178 current_utterance_ = utterance; | |
| 179 utterance->set_extension_id(extension->id()); | |
| 180 | |
| 181 ExtensionTtsEngineSpeak(utterance, extension, voice_index); | |
| 182 | |
| 183 const std::set<std::string> event_types = | |
| 184 extension->tts_voices()[voice_index].event_types; | |
| 185 bool sends_end_event = | |
| 186 (event_types.find(constants::kEventTypeEnd) != event_types.end()); | |
| 187 if (!sends_end_event) { | |
| 188 utterance->Finish(); | |
| 189 delete utterance; | |
| 190 current_utterance_ = NULL; | |
| 191 SpeakNextUtterance(); | |
| 192 } | |
| 193 return; | |
| 194 } | |
| 195 | |
| 196 GetPlatformImpl()->clear_error(); | |
| 197 bool success = GetPlatformImpl()->Speak( | |
| 198 utterance->id(), | |
| 199 utterance->text(), | |
| 200 utterance->lang(), | |
| 201 utterance->continuous_parameters()); | |
| 202 if (!success) { | |
| 203 utterance->OnTtsEvent(TTS_EVENT_ERROR, kInvalidCharIndex, | |
| 204 GetPlatformImpl()->error()); | |
| 205 delete utterance; | |
| 206 return; | |
| 207 } | |
| 208 current_utterance_ = utterance; | |
| 209 } | |
| 210 | |
| 211 void ExtensionTtsController::Stop() { | |
| 212 if (current_utterance_ && !current_utterance_->extension_id().empty()) { | |
| 213 ExtensionTtsEngineStop(current_utterance_); | |
| 214 } else { | |
| 215 GetPlatformImpl()->clear_error(); | |
| 216 GetPlatformImpl()->StopSpeaking(); | |
| 217 } | |
| 218 | |
| 219 if (current_utterance_) | |
| 220 current_utterance_->OnTtsEvent(TTS_EVENT_INTERRUPTED, kInvalidCharIndex, | |
| 221 std::string()); | |
| 222 FinishCurrentUtterance(); | |
| 223 ClearUtteranceQueue(true); // Send events. | |
| 224 } | |
| 225 | |
| 226 void ExtensionTtsController::OnTtsEvent(int utterance_id, | |
| 227 TtsEventType event_type, | |
| 228 int char_index, | |
| 229 const std::string& error_message) { | |
| 230 // We may sometimes receive completion callbacks "late", after we've | |
| 231 // already finished the utterance (for example because another utterance | |
| 232 // interrupted or we got a call to Stop). This is normal and we can | |
| 233 // safely just ignore these events. | |
| 234 if (!current_utterance_ || utterance_id != current_utterance_->id()) | |
| 235 return; | |
| 236 | |
| 237 current_utterance_->OnTtsEvent(event_type, char_index, error_message); | |
| 238 if (current_utterance_->finished()) { | |
| 239 FinishCurrentUtterance(); | |
| 240 SpeakNextUtterance(); | |
| 241 } | |
| 242 } | |
| 243 | |
| 244 ListValue* ExtensionTtsController::GetVoices(Profile* profile) { | |
| 245 ListValue* result_voices = new ListValue(); | |
| 246 ExtensionTtsPlatformImpl* platform_impl = GetPlatformImpl(); | |
| 247 if (platform_impl && platform_impl->PlatformImplAvailable()) { | |
| 248 DictionaryValue* result_voice = new DictionaryValue(); | |
| 249 result_voice->SetString( | |
| 250 constants::kVoiceNameKey, constants::kNativeVoiceName); | |
| 251 if (!platform_impl->gender().empty()) | |
| 252 result_voice->SetString(constants::kGenderKey, platform_impl->gender()); | |
| 253 ListValue* event_types = new ListValue(); | |
| 254 | |
| 255 // All platforms must send end events, and cancelled and interrupted | |
| 256 // events are generated from the controller. | |
| 257 DCHECK(platform_impl->SendsEvent(TTS_EVENT_END)); | |
| 258 event_types->Append(Value::CreateStringValue(constants::kEventTypeEnd)); | |
| 259 event_types->Append(Value::CreateStringValue( | |
| 260 constants::kEventTypeCancelled)); | |
| 261 event_types->Append(Value::CreateStringValue( | |
| 262 constants::kEventTypeInterrupted)); | |
| 263 | |
| 264 if (platform_impl->SendsEvent(TTS_EVENT_START)) | |
| 265 event_types->Append(Value::CreateStringValue(constants::kEventTypeStart)); | |
| 266 if (platform_impl->SendsEvent(TTS_EVENT_WORD)) | |
| 267 event_types->Append(Value::CreateStringValue(constants::kEventTypeWord)); | |
| 268 if (platform_impl->SendsEvent(TTS_EVENT_SENTENCE)) | |
| 269 event_types->Append(Value::CreateStringValue( | |
| 270 constants::kEventTypeSentence)); | |
| 271 if (platform_impl->SendsEvent(TTS_EVENT_MARKER)) | |
| 272 event_types->Append(Value::CreateStringValue( | |
| 273 constants::kEventTypeMarker)); | |
| 274 if (platform_impl->SendsEvent(TTS_EVENT_ERROR)) | |
| 275 event_types->Append(Value::CreateStringValue( | |
| 276 constants::kEventTypeError)); | |
| 277 result_voice->Set(constants::kEventTypesKey, event_types); | |
| 278 result_voices->Append(result_voice); | |
| 279 } | |
| 280 | |
| 281 GetExtensionVoices(profile, result_voices); | |
| 282 | |
| 283 return result_voices; | |
| 284 } | |
| 285 | |
| 286 bool ExtensionTtsController::IsSpeaking() const { | |
| 287 return current_utterance_ != NULL; | |
| 288 } | |
| 289 | |
| 290 void ExtensionTtsController::FinishCurrentUtterance() { | |
| 291 if (current_utterance_) { | |
| 292 if (!current_utterance_->finished()) | |
| 293 current_utterance_->OnTtsEvent(TTS_EVENT_INTERRUPTED, kInvalidCharIndex, | |
| 294 std::string()); | |
| 295 delete current_utterance_; | |
| 296 current_utterance_ = NULL; | |
| 297 } | |
| 298 } | |
| 299 | |
| 300 void ExtensionTtsController::SpeakNextUtterance() { | |
| 301 // Start speaking the next utterance in the queue. Keep trying in case | |
| 302 // one fails but there are still more in the queue to try. | |
| 303 while (!utterance_queue_.empty() && !current_utterance_) { | |
| 304 Utterance* utterance = utterance_queue_.front(); | |
| 305 utterance_queue_.pop(); | |
| 306 SpeakNow(utterance); | |
| 307 } | |
| 308 } | |
| 309 | |
| 310 void ExtensionTtsController::ClearUtteranceQueue(bool send_events) { | |
| 311 while (!utterance_queue_.empty()) { | |
| 312 Utterance* utterance = utterance_queue_.front(); | |
| 313 utterance_queue_.pop(); | |
| 314 if (send_events) | |
| 315 utterance->OnTtsEvent(TTS_EVENT_CANCELLED, kInvalidCharIndex, | |
| 316 std::string()); | |
| 317 else | |
| 318 utterance->Finish(); | |
| 319 delete utterance; | |
| 320 } | |
| 321 } | |
| 322 | |
| 323 void ExtensionTtsController::SetPlatformImpl( | |
| 324 ExtensionTtsPlatformImpl* platform_impl) { | |
| 325 platform_impl_ = platform_impl; | |
| 326 } | |
| 327 | |
| 328 int ExtensionTtsController::QueueSize() { | |
| 329 return static_cast<int>(utterance_queue_.size()); | |
| 330 } | |
| 331 | |
| 332 ExtensionTtsPlatformImpl* ExtensionTtsController::GetPlatformImpl() { | |
| 333 if (!platform_impl_) | |
| 334 platform_impl_ = ExtensionTtsPlatformImpl::GetInstance(); | |
| 335 return platform_impl_; | |
| 336 } | |
| OLD | NEW |