OLD | NEW |
---|---|
1 // Copyright 2014 The Chromium Authors. All rights reserved. | 1 // Copyright 2014 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/speech/tts_controller_impl.h" | 5 #include "chrome/browser/speech/tts_controller_impl.h" |
6 | 6 |
7 #include <stddef.h> | 7 #include <stddef.h> |
8 | 8 |
9 #include <string> | 9 #include <string> |
10 #include <vector> | 10 #include <vector> |
(...skipping 145 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
156 | 156 |
157 void TtsControllerImpl::SpeakNow(Utterance* utterance) { | 157 void TtsControllerImpl::SpeakNow(Utterance* utterance) { |
158 // Ensure we have all built-in voices loaded. This is a no-op if already | 158 // Ensure we have all built-in voices loaded. This is a no-op if already |
159 // loaded. | 159 // loaded. |
160 bool loaded_built_in = | 160 bool loaded_built_in = |
161 GetPlatformImpl()->LoadBuiltInTtsExtension(utterance->browser_context()); | 161 GetPlatformImpl()->LoadBuiltInTtsExtension(utterance->browser_context()); |
162 | 162 |
163 // Get all available voices and try to find a matching voice. | 163 // Get all available voices and try to find a matching voice. |
164 std::vector<VoiceData> voices; | 164 std::vector<VoiceData> voices; |
165 GetVoices(utterance->browser_context(), &voices); | 165 GetVoices(utterance->browser_context(), &voices); |
166 | |
167 // Get the best matching voice. If nothing matches, just set "native" | |
168 // to true because that might trigger deferred loading of native voices. | |
166 int index = GetMatchingVoice(utterance, voices); | 169 int index = GetMatchingVoice(utterance, voices); |
167 | |
168 VoiceData voice; | 170 VoiceData voice; |
169 if (index != -1) { | 171 if (index >= 0) |
170 // Select the matching voice. | |
171 voice = voices[index]; | 172 voice = voices[index]; |
172 } else { | 173 else |
173 // However, if no match was found on a platform without native tts voices, | 174 voice.native = true; // Try to let |
174 // attempt to get a voice based only on the current locale without respect | |
175 // to any supplied voice names. | |
176 std::vector<VoiceData> native_voices; | |
177 | |
178 if (GetPlatformImpl()->PlatformImplAvailable()) | |
179 GetPlatformImpl()->GetVoices(&native_voices); | |
180 | |
181 if (native_voices.empty() && !voices.empty()) { | |
182 // TODO(dtseng): Notify extension caller of an error. | |
183 utterance->set_voice_name(""); | |
184 // TODO(gaochun): Replace the global variable g_browser_process with | |
185 // GetContentClient()->browser() to eliminate the dependency of browser | |
186 // once TTS implementation was moved to content. | |
187 utterance->set_lang(g_browser_process->GetApplicationLocale()); | |
188 index = GetMatchingVoice(utterance, voices); | |
189 | |
190 // If even that fails, just take the first available voice. | |
191 if (index == -1) | |
192 index = 0; | |
193 voice = voices[index]; | |
194 } else { | |
195 // Otherwise, simply give native voices a chance to handle this utterance. | |
196 voice.native = true; | |
197 } | |
198 } | |
199 | 175 |
200 GetPlatformImpl()->WillSpeakUtteranceWithVoice(utterance, voice); | 176 GetPlatformImpl()->WillSpeakUtteranceWithVoice(utterance, voice); |
201 | 177 |
202 if (!voice.native) { | 178 if (!voice.native) { |
203 #if !defined(OS_ANDROID) | 179 #if !defined(OS_ANDROID) |
204 DCHECK(!voice.extension_id.empty()); | 180 DCHECK(!voice.extension_id.empty()); |
205 current_utterance_ = utterance; | 181 current_utterance_ = utterance; |
206 utterance->set_extension_id(voice.extension_id); | 182 utterance->set_extension_id(voice.extension_id); |
207 if (tts_engine_delegate_) | 183 if (tts_engine_delegate_) |
208 tts_engine_delegate_->Speak(utterance, voice); | 184 tts_engine_delegate_->Speak(utterance, voice); |
(...skipping 160 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
369 } | 345 } |
370 | 346 |
371 TtsPlatformImpl* TtsControllerImpl::GetPlatformImpl() { | 347 TtsPlatformImpl* TtsControllerImpl::GetPlatformImpl() { |
372 if (!platform_impl_) | 348 if (!platform_impl_) |
373 platform_impl_ = TtsPlatformImpl::GetInstance(); | 349 platform_impl_ = TtsPlatformImpl::GetInstance(); |
374 return platform_impl_; | 350 return platform_impl_; |
375 } | 351 } |
376 | 352 |
377 int TtsControllerImpl::GetMatchingVoice( | 353 int TtsControllerImpl::GetMatchingVoice( |
378 const Utterance* utterance, std::vector<VoiceData>& voices) { | 354 const Utterance* utterance, std::vector<VoiceData>& voices) { |
379 // Make two passes: the first time, do strict language matching | 355 // Return the index of the voice that best match the utterance parameters. |
380 // ('fr-FR' does not match 'fr-CA'). The second time, do prefix | 356 // |
381 // language matching ('fr-FR' matches 'fr' and 'fr-CA') | 357 // These criteria are considered mandatory - if they're specified, any voice |
382 for (int pass = 0; pass < 2; ++pass) { | 358 // that doesn't match is rejected. |
383 for (size_t i = 0; i < voices.size(); ++i) { | 359 // |
384 const VoiceData& voice = voices[i]; | 360 // Extension ID |
361 // Voice name | |
362 // | |
363 // The other criteria are scored based on how well they match, in | |
364 // this order of precedence: | |
365 // | |
366 // Utterange language (exact region preferred, then general language) | |
367 // App/system language (exact region preferred, then general language) | |
368 // Required event types | |
369 // Gender | |
385 | 370 |
386 if (!utterance->extension_id().empty() && | 371 // TODO(gaochun): Replace the global variable g_browser_process with |
387 utterance->extension_id() != voice.extension_id) { | 372 // GetContentClient()->browser() to eliminate the dependency of browser |
388 continue; | 373 // once TTS implementation was moved to content. |
389 } | 374 std::string app_lang = g_browser_process->GetApplicationLocale(); |
390 | 375 |
391 if (!voice.name.empty() && | 376 // Start with a best score of -1, that way even if none of the criteria |
392 !utterance->voice_name().empty() && | 377 // match, something will be returned if there are any voices. |
393 voice.name != utterance->voice_name()) { | 378 int best_score = -1; |
394 continue; | 379 int best_score_index = -1; |
395 } | 380 for (size_t i = 0; i < voices.size(); ++i) { |
396 if (!voice.lang.empty() && !utterance->lang().empty()) { | 381 const VoiceData& voice = voices[i]; |
397 std::string voice_lang = voice.lang; | 382 int score = 0; |
398 std::string utterance_lang = utterance->lang(); | 383 |
399 if (pass == 1) { | 384 // If the extension ID is specified, check for an exact match. |
400 voice_lang = TrimLanguageCode(voice_lang); | 385 if (!utterance->extension_id().empty() && |
401 utterance_lang = TrimLanguageCode(utterance_lang); | 386 utterance->extension_id() != voice.extension_id) |
402 } | 387 continue; |
403 if (voice_lang != utterance_lang) { | 388 |
404 continue; | 389 // If the voice name is specified, check for an exact match. |
390 if (!utterance->voice_name().empty() && | |
391 voice.name != utterance->voice_name()) | |
392 continue; | |
393 | |
394 // Prefer the utterance language. | |
395 if (!voice.lang.empty() && !utterance->lang().empty()) { | |
396 // An exact language match is worth more | |
397 if (voice.lang == utterance->lang()) | |
398 score += 32; | |
399 | |
400 // A partial language match is worth less | |
401 if (TrimLanguageCode(voice.lang) == TrimLanguageCode(utterance->lang())) | |
David Tseng
2016/09/23 22:10:50
else if?
dmazzoni
2016/09/26 19:26:12
Done
| |
402 score += 16; | |
403 } | |
404 | |
405 // Prefer the system language after that. | |
406 if (!voice.lang.empty()) { | |
407 if (voice.lang == app_lang) | |
408 score += 8; | |
409 | |
410 if (TrimLanguageCode(voice.lang) == TrimLanguageCode(app_lang)) | |
David Tseng
2016/09/23 22:10:50
else if?
dmazzoni
2016/09/26 19:26:12
Done
| |
411 score += 4; | |
412 } | |
413 | |
414 // Next, prefer required event types. | |
415 if (utterance->required_event_types().size() > 0) { | |
416 bool has_all_required_event_types = true; | |
417 for (std::set<TtsEventType>::const_iterator iter = | |
418 utterance->required_event_types().begin(); | |
419 iter != utterance->required_event_types().end(); | |
420 ++iter) { | |
421 if (voice.events.find(*iter) == voice.events.end()) { | |
422 has_all_required_event_types = false; | |
423 break; | |
405 } | 424 } |
406 } | 425 } |
407 if (voice.gender != TTS_GENDER_NONE && | 426 if (has_all_required_event_types) |
408 utterance->gender() != TTS_GENDER_NONE && | 427 score += 2; |
409 voice.gender != utterance->gender()) { | 428 } |
410 continue; | |
411 } | |
412 | 429 |
413 if (utterance->required_event_types().size() > 0) { | 430 // Finally prefer the requested gender last. |
414 bool has_all_required_event_types = true; | 431 if (voice.gender != TTS_GENDER_NONE && |
415 for (std::set<TtsEventType>::const_iterator iter = | 432 utterance->gender() != TTS_GENDER_NONE && |
416 utterance->required_event_types().begin(); | 433 voice.gender == utterance->gender()) { |
417 iter != utterance->required_event_types().end(); | 434 score += 1; |
418 ++iter) { | 435 } |
419 if (voice.events.find(*iter) == voice.events.end()) { | |
420 has_all_required_event_types = false; | |
421 break; | |
422 } | |
423 } | |
424 if (!has_all_required_event_types) | |
425 continue; | |
426 } | |
427 | 436 |
428 return static_cast<int>(i); | 437 if (score > best_score) { |
438 best_score = score; | |
439 best_score_index = i; | |
429 } | 440 } |
430 } | 441 } |
431 | 442 |
432 return -1; | 443 return best_score_index; |
433 } | 444 } |
434 | 445 |
435 void TtsControllerImpl::VoicesChanged() { | 446 void TtsControllerImpl::VoicesChanged() { |
436 // Existence of platform tts indicates explicit requests to tts. Since | 447 // Existence of platform tts indicates explicit requests to tts. Since |
437 // |VoicesChanged| can occur implicitly, only send if needed. | 448 // |VoicesChanged| can occur implicitly, only send if needed. |
438 if (!platform_impl_) | 449 if (!platform_impl_) |
439 return; | 450 return; |
440 | 451 |
441 for (std::set<VoicesChangedDelegate*>::iterator iter = | 452 for (std::set<VoicesChangedDelegate*>::iterator iter = |
442 voices_changed_delegates_.begin(); | 453 voices_changed_delegates_.begin(); |
(...skipping 43 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
486 } | 497 } |
487 | 498 |
488 void TtsControllerImpl::SetTtsEngineDelegate( | 499 void TtsControllerImpl::SetTtsEngineDelegate( |
489 TtsEngineDelegate* delegate) { | 500 TtsEngineDelegate* delegate) { |
490 tts_engine_delegate_ = delegate; | 501 tts_engine_delegate_ = delegate; |
491 } | 502 } |
492 | 503 |
493 TtsEngineDelegate* TtsControllerImpl::GetTtsEngineDelegate() { | 504 TtsEngineDelegate* TtsControllerImpl::GetTtsEngineDelegate() { |
494 return tts_engine_delegate_; | 505 return tts_engine_delegate_; |
495 } | 506 } |
OLD | NEW |