Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(284)

Side by Side Diff: chrome/browser/speech/tts_controller_impl.cc

Issue 2364753002: Improve TTS GetMatchingVoices algorithm (Closed)
Patch Set: Fix tests Created 4 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
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
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
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
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 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698