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

Unified Diff: chrome/browser/speech/tts_controller_impl.cc

Issue 2364753002: Improve TTS GetMatchingVoices algorithm (Closed)
Patch Set: Address feedback 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 side-by-side diff with in-line comments
Download patch
« no previous file with comments | « chrome/browser/speech/tts_controller_impl.h ('k') | chrome/browser/speech/tts_controller_unittest.cc » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: chrome/browser/speech/tts_controller_impl.cc
diff --git a/chrome/browser/speech/tts_controller_impl.cc b/chrome/browser/speech/tts_controller_impl.cc
index 7b57c351542233c7a927a8c92f2a9e0b02aebcdb..9cbcaf3c6df6f6d8589a133e15db910f977bf65f 100644
--- a/chrome/browser/speech/tts_controller_impl.cc
+++ b/chrome/browser/speech/tts_controller_impl.cc
@@ -81,7 +81,9 @@ Utterance::Utterance(content::BrowserContext* browser_context)
}
Utterance::~Utterance() {
- DCHECK(finished_);
+ // It's an error if an Utterance is destructed without being finished,
+ // unless |browser_context_| is nullptr because it's a unit test.
+ DCHECK(finished_ || !browser_context_);
}
void Utterance::OnTtsEvent(TtsEventType event_type,
@@ -163,39 +165,15 @@ void TtsControllerImpl::SpeakNow(Utterance* utterance) {
// Get all available voices and try to find a matching voice.
std::vector<VoiceData> voices;
GetVoices(utterance->browser_context(), &voices);
- int index = GetMatchingVoice(utterance, voices);
+ // Get the best matching voice. If nothing matches, just set "native"
+ // to true because that might trigger deferred loading of native voices.
+ int index = GetMatchingVoice(utterance, voices);
VoiceData voice;
- if (index != -1) {
- // Select the matching voice.
+ if (index >= 0)
voice = voices[index];
- } else {
- // However, if no match was found on a platform without native tts voices,
- // attempt to get a voice based only on the current locale without respect
- // to any supplied voice names.
- std::vector<VoiceData> native_voices;
-
- if (GetPlatformImpl()->PlatformImplAvailable())
- GetPlatformImpl()->GetVoices(&native_voices);
-
- if (native_voices.empty() && !voices.empty()) {
- // TODO(dtseng): Notify extension caller of an error.
- utterance->set_voice_name("");
- // TODO(gaochun): Replace the global variable g_browser_process with
- // GetContentClient()->browser() to eliminate the dependency of browser
- // once TTS implementation was moved to content.
- utterance->set_lang(g_browser_process->GetApplicationLocale());
- index = GetMatchingVoice(utterance, voices);
-
- // If even that fails, just take the first available voice.
- if (index == -1)
- index = 0;
- voice = voices[index];
- } else {
- // Otherwise, simply give native voices a chance to handle this utterance.
- voice.native = true;
- }
- }
+ else
+ voice.native = true; // Try to let
GetPlatformImpl()->WillSpeakUtteranceWithVoice(utterance, voice);
@@ -376,60 +354,94 @@ TtsPlatformImpl* TtsControllerImpl::GetPlatformImpl() {
int TtsControllerImpl::GetMatchingVoice(
const Utterance* utterance, std::vector<VoiceData>& voices) {
- // Make two passes: the first time, do strict language matching
- // ('fr-FR' does not match 'fr-CA'). The second time, do prefix
- // language matching ('fr-FR' matches 'fr' and 'fr-CA')
- for (int pass = 0; pass < 2; ++pass) {
- for (size_t i = 0; i < voices.size(); ++i) {
- const VoiceData& voice = voices[i];
-
- if (!utterance->extension_id().empty() &&
- utterance->extension_id() != voice.extension_id) {
- continue;
+ // Return the index of the voice that best match the utterance parameters.
+ //
+ // These criteria are considered mandatory - if they're specified, any voice
+ // that doesn't match is rejected.
+ //
+ // Extension ID
+ // Voice name
+ //
+ // The other criteria are scored based on how well they match, in
+ // this order of precedence:
+ //
+ // Utterange language (exact region preferred, then general language)
+ // App/system language (exact region preferred, then general language)
+ // Required event types
+ // Gender
+
+ // TODO(gaochun): Replace the global variable g_browser_process with
+ // GetContentClient()->browser() to eliminate the dependency of browser
+ // once TTS implementation was moved to content.
+ std::string app_lang = g_browser_process->GetApplicationLocale();
+
+ // Start with a best score of -1, that way even if none of the criteria
+ // match, something will be returned if there are any voices.
+ int best_score = -1;
+ int best_score_index = -1;
+ for (size_t i = 0; i < voices.size(); ++i) {
+ const VoiceData& voice = voices[i];
+ int score = 0;
+
+ // If the extension ID is specified, check for an exact match.
+ if (!utterance->extension_id().empty() &&
+ utterance->extension_id() != voice.extension_id)
+ continue;
+
+ // If the voice name is specified, check for an exact match.
+ if (!utterance->voice_name().empty() &&
+ voice.name != utterance->voice_name())
+ continue;
+
+ // Prefer the utterance language.
+ if (!voice.lang.empty() && !utterance->lang().empty()) {
+ // An exact language match is worth more than a partial match.
+ if (voice.lang == utterance->lang()) {
+ score += 32;
+ } else if (TrimLanguageCode(voice.lang) ==
+ TrimLanguageCode(utterance->lang())) {
+ score += 16;
}
+ }
- if (!voice.name.empty() &&
- !utterance->voice_name().empty() &&
- voice.name != utterance->voice_name()) {
- continue;
- }
- if (!voice.lang.empty() && !utterance->lang().empty()) {
- std::string voice_lang = voice.lang;
- std::string utterance_lang = utterance->lang();
- if (pass == 1) {
- voice_lang = TrimLanguageCode(voice_lang);
- utterance_lang = TrimLanguageCode(utterance_lang);
- }
- if (voice_lang != utterance_lang) {
- continue;
- }
- }
- if (voice.gender != TTS_GENDER_NONE &&
- utterance->gender() != TTS_GENDER_NONE &&
- voice.gender != utterance->gender()) {
- continue;
- }
+ // Prefer the system language after that.
+ if (!voice.lang.empty()) {
+ if (voice.lang == app_lang)
+ score += 8;
+ else if (TrimLanguageCode(voice.lang) == TrimLanguageCode(app_lang))
+ score += 4;
+ }
- if (utterance->required_event_types().size() > 0) {
- bool has_all_required_event_types = true;
- for (std::set<TtsEventType>::const_iterator iter =
- utterance->required_event_types().begin();
- iter != utterance->required_event_types().end();
- ++iter) {
- if (voice.events.find(*iter) == voice.events.end()) {
- has_all_required_event_types = false;
- break;
- }
+ // Next, prefer required event types.
+ if (utterance->required_event_types().size() > 0) {
+ bool has_all_required_event_types = true;
+ for (std::set<TtsEventType>::const_iterator iter =
+ utterance->required_event_types().begin();
+ iter != utterance->required_event_types().end();
+ ++iter) {
+ if (voice.events.find(*iter) == voice.events.end()) {
+ has_all_required_event_types = false;
+ break;
}
- if (!has_all_required_event_types)
- continue;
}
+ if (has_all_required_event_types)
+ score += 2;
+ }
+
+ // Finally prefer the requested gender last.
+ if (voice.gender != TTS_GENDER_NONE &&
+ utterance->gender() != TTS_GENDER_NONE &&
+ voice.gender == utterance->gender()) {
+ score += 1;
+ }
- return static_cast<int>(i);
+ if (score > best_score) {
+ best_score = score;
+ best_score_index = i;
}
}
- return -1;
+ return best_score_index;
}
void TtsControllerImpl::VoicesChanged() {
« no previous file with comments | « chrome/browser/speech/tts_controller_impl.h ('k') | chrome/browser/speech/tts_controller_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698