Commit f58f9dd5 authored by David Tseng's avatar David Tseng Committed by Commit Bot

Change text-to-speech slider tick scale

Formerly, the rate, pitch, and volume used a hybrid linear/exponential scale. Convert to linear, with a smaller step-size.

Fixed: 987509
Change-Id: I6eaae207e6fffac08f098750e96d131d08d4217c
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2388342Reviewed-by: default avatarKyle Horimoto <khorimoto@chromium.org>
Commit-Queue: David Tseng <dtseng@chromium.org>
Cr-Commit-Position: refs/heads/master@{#804482}
parent e6d0b22e
...@@ -461,6 +461,7 @@ if (is_chromeos) { ...@@ -461,6 +461,7 @@ if (is_chromeos) {
"background/output_test.js", "background/output_test.js",
"background/portals_test.js", "background/portals_test.js",
"background/recovery_strategy_test.js", "background/recovery_strategy_test.js",
"background/settings_test.js",
"background/smart_sticky_mode_test.js", "background/smart_sticky_mode_test.js",
"background/user_action_monitor_test.js", "background/user_action_monitor_test.js",
"braille/braille_table_test.js", "braille/braille_table_test.js",
......
// Copyright 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Include test fixture.
GEN_INCLUDE(['../testing/chromevox_next_e2e_test_base.js']);
GEN_INCLUDE(['../testing/mock_feedback.js', '../testing/fake_objects.js']);
/**
* Test fixture involving settings pages.
*/
ChromeVoxSettingsPagesTest = class extends ChromeVoxNextE2ETest {
/** @override */
setUp() {
window.doCmd = (command) => {
return () => {
CommandHandler.onCommand(command);
};
};
}
/** @override */
testGenCppIncludes() {
super.testGenCppIncludes();
GEN(`
#include "chrome/browser/ui/web_applications/system_web_app_ui_utils.h"
#include "chrome/browser/web_applications/web_app_provider.h"
`);
}
/** @override */
testGenPreamble() {
GEN(`
web_app::WebAppProvider::Get(browser()->profile())
->system_web_app_manager()
.InstallSystemAppsForTesting();
`);
super.testGenPreamble();
}
createMockFeedback() {
const mockFeedback =
new MockFeedback(this.newCallback(), this.newCallback.bind(this));
mockFeedback.install();
return mockFeedback;
}
};
TEST_F(
'ChromeVoxSettingsPagesTest', 'TtsRateCommandOnSettingsPage', function() {
const realTts = ChromeVox.tts;
const mockFeedback = this.createMockFeedback();
this.runWithLoadedTree(`unused`, function() {
const increaseRate = realTts.increaseOrDecreaseProperty.bind(
realTts, AbstractTts.RATE, true);
const decreaseRate = realTts.increaseOrDecreaseProperty.bind(
realTts, AbstractTts.RATE, false);
mockFeedback.call(doCmd('showTtsSettings'))
.expectSpeech('Text-to-Speech voice settings subpage back button')
// ChromeVox presents a 0% to 100% scale.
// Ensure we have the default rate.
.call(
() => chrome.settingsPrivate.setPref(
'settings.tts.speech_rate', 1.0))
.call(increaseRate)
.expectSpeech('Rate 19 percent')
.call(increaseRate)
.expectSpeech('Rate 21 percent')
// Speed things up...
.call(
() => chrome.settingsPrivate.setPref(
'settings.tts.speech_rate', 4.9))
.expectSpeech('Rate 98 percent')
.call(increaseRate)
.expectSpeech('Rate 100 percent')
.call(decreaseRate)
.expectSpeech('Rate 98 percent')
.call(decreaseRate)
.expectSpeech('Rate 96 percent')
// Slow things down...
.call(
() => chrome.settingsPrivate.setPref(
'settings.tts.speech_rate', 0.3))
.expectSpeech('Rate 2 percent')
.call(decreaseRate)
.expectSpeech('Rate 0 percent')
.call(increaseRate)
.expectSpeech('Rate 2 percent')
.call(increaseRate)
.expectSpeech('Rate 4 percent')
.replay();
});
});
...@@ -23,7 +23,7 @@ Polymer({ ...@@ -23,7 +23,7 @@ Polymer({
/** /**
* Available languages. * Available languages.
* @type {Array<{language: string, code: string, preferred: boolean, * @type {!Array<!{language: string, code: string, preferred: boolean,
* voice: TtsHandlerVoice}>} * voice: TtsHandlerVoice}>}
*/ */
languagesToVoices: { languagesToVoices: {
...@@ -33,7 +33,7 @@ Polymer({ ...@@ -33,7 +33,7 @@ Polymer({
/** /**
* All voices. * All voices.
* @type {Array<TtsHandlerVoice>} * @type {!Array<!TtsHandlerVoice>}
*/ */
allVoices: { allVoices: {
type: Array, type: Array,
...@@ -93,50 +93,58 @@ Polymer({ ...@@ -93,50 +93,58 @@ Polymer({
}, },
/** /**
* Ticks for the Speech Rate slider. Non-linear as we expect people * Ticks for the Speech Rate slider. Valid rates are between 0.1 and 5.
* to want more control near 1.0. * @return {!Array<!cr_slider.SliderTick>}
* @return Array<cr_slider.SliderTick>
* @private * @private
*/ */
speechRateTicks_() { speechRateTicks_() {
return Array.from(Array(16).keys()).map(x => { return this.buildLinearTicks_(0.1, 5);
return x <= 4 ?
// Linear from rates 0.6 to 1.0
this.initTick_(x / 10 + .6) :
// Power function above 1.0 gives more control at lower values.
this.initTick_(Math.pow(x - 3, 2) / 20 + 1);
});
}, },
/** /**
* Ticks for the Speech Pitch slider. Valid pitches are between 0 and 2, * Ticks for the Speech Pitch slider. Valid pitches are between 0.2 and 2.
* exclusive of 0. * @return {!Array<!cr_slider.SliderTick>}
* @return Array<cr_slider.SliderTick>
* @private * @private
*/ */
speechPitchTicks_() { speechPitchTicks_() {
return Array.from(Array(10).keys()).map(x => { return this.buildLinearTicks_(0.2, 2);
return this.initTick_(x * .2 + .2);
});
}, },
/** /**
* Ticks for the Speech Volume slider. Valid volumes are between 0 and * Ticks for the Speech Volume slider. Valid volumes are between 0.2 and
* 1 (100%), but volumes lower than .2 are excluded as being too quiet. * 1 (100%), but volumes lower than .2 are excluded as being too quiet.
* The values are linear between .2 and 1.0. * @return {!Array<!cr_slider.SliderTick>}
* @return Array<cr_slider.SliderTick>
* @private * @private
*/ */
speechVolumeTicks_() { speechVolumeTicks_() {
return Array.from(Array(9).keys()).map(x => { return this.buildLinearTicks_(0.2, 1);
return this.initTick_(x * .1 + .2); },
});
/**
* A helper to build a set of ticks between |min| and |max| (inclusive) spaced
* evenly by 0.1.
* @param {number} min
* @param {number} max
* @return {!Array<!cr_slider.SliderTick>}
* @private
*/
buildLinearTicks_(min, max) {
const ticks = [];
// Avoid floating point addition errors by scaling everything by 10.
min *= 10;
max *= 10;
const step = 1;
for (let tickValue = min; tickValue <= max; tickValue += step) {
ticks.push(this.initTick_(tickValue / 10));
}
return ticks;
}, },
/** /**
* Initializes i18n labels for ticks arrays. * Initializes i18n labels for ticks arrays.
* @param {number} tick The value to make a tick for. * @param {number} tick The value to make a tick for.
* @return {cr_slider.SliderTick} * @return {!cr_slider.SliderTick}
* @private * @private
*/ */
initTick_(tick) { initTick_(tick) {
...@@ -150,7 +158,7 @@ Polymer({ ...@@ -150,7 +158,7 @@ Polymer({
/** /**
* Returns true if any voices are loaded. * Returns true if any voices are loaded.
* @param {!Array<TtsHandlerVoice>} voices * @param {!Array<!TtsHandlerVoice>} voices
* @return {boolean} * @return {boolean}
* @private * @private
*/ */
...@@ -161,7 +169,7 @@ Polymer({ ...@@ -161,7 +169,7 @@ Polymer({
/** /**
* Returns true if voices are loaded and preview is not currently speaking and * Returns true if voices are loaded and preview is not currently speaking and
* there is text to preview. * there is text to preview.
* @param {!Array<TtsHandlerVoice>} voices * @param {!Array<!TtsHandlerVoice>} voices
* @param {boolean} isPreviewing * @param {boolean} isPreviewing
* @param {boolean} previewText * @param {boolean} previewText
* @return {boolean} * @return {boolean}
...@@ -175,7 +183,7 @@ Polymer({ ...@@ -175,7 +183,7 @@ Polymer({
/** /**
* Populates the list of languages and voices for the UI to use in display. * Populates the list of languages and voices for the UI to use in display.
* @param {Array<TtsHandlerVoice>} voices * @param {!Array<!TtsHandlerVoice>} voices
* @private * @private
*/ */
populateVoiceList_(voices) { populateVoiceList_(voices) {
...@@ -216,9 +224,10 @@ Polymer({ ...@@ -216,9 +224,10 @@ Polymer({
/** /**
* Returns true if the language is a primary language and should be shown by * Returns true if the language is a primary language and should be shown by
* default, false if it should be hidden by default. * default, false if it should be hidden by default.
* @param {{language: string, code: string, preferred: boolean, * @param {!{language: string, code: string, preferred: boolean,
* voice: TtsHandlerVoice}} language * voice: TtsHandlerVoice}} language
* @return {boolean} true if it's a primary language. * @return {boolean} true if it's a primary language.
* @private
*/ */
isPrimaryLanguage_(language) { isPrimaryLanguage_(language) {
return language.preferred; return language.preferred;
...@@ -227,9 +236,10 @@ Polymer({ ...@@ -227,9 +236,10 @@ Polymer({
/** /**
* Returns true if the language is a secondary language and should be hidden * Returns true if the language is a secondary language and should be hidden
* by default, true if it should be shown by default. * by default, true if it should be shown by default.
* @param {{language: string, code: string, preferred: boolean, * @param {!{language: string, code: string, preferred: boolean,
* voice: TtsHandlerVoice}} language * voice: TtsHandlerVoice}} language
* @return {boolean} true if it's a secondary language. * @return {boolean} true if it's a secondary language.
* @private
*/ */
isSecondaryLanguage_(language) { isSecondaryLanguage_(language) {
return !language.preferred; return !language.preferred;
...@@ -237,7 +247,7 @@ Polymer({ ...@@ -237,7 +247,7 @@ Polymer({
/** /**
* Sets the list of Text-to-Speech extensions for the UI. * Sets the list of Text-to-Speech extensions for the UI.
* @param {Array<TtsHandlerExtension>} extensions * @param {!Array<!TtsHandlerExtension>} extensions
* @private * @private
*/ */
populateExtensionList_(extensions) { populateExtensionList_(extensions) {
...@@ -256,8 +266,8 @@ Polymer({ ...@@ -256,8 +266,8 @@ Polymer({
/** /**
* A function used for sorting languages alphabetically. * A function used for sorting languages alphabetically.
* @param {Object} first A languageToVoices array item. * @param {!Object} first A languageToVoices array item.
* @param {Object} second A languageToVoices array item. * @param {!Object} second A languageToVoices array item.
* @return {number} The result of the comparison. * @return {number} The result of the comparison.
* @private * @private
*/ */
...@@ -267,7 +277,7 @@ Polymer({ ...@@ -267,7 +277,7 @@ Polymer({
/** /**
* Tests whether a language has just once voice. * Tests whether a language has just once voice.
* @param {Object} lang A languageToVoices array item. * @param {!Object} lang A languageToVoices array item.
* @return {boolean} True if the item has only one voice. * @return {boolean} True if the item has only one voice.
* @private * @private
*/ */
...@@ -278,8 +288,8 @@ Polymer({ ...@@ -278,8 +288,8 @@ Polymer({
/** /**
* Returns a list of objects that can be used as drop-down menu options for a * Returns a list of objects that can be used as drop-down menu options for a
* language. This is a list of voices in that language. * language. This is a list of voices in that language.
* @param {Object} lang A languageToVoices array item. * @param {!Object} lang A languageToVoices array item.
* @return {Array<Object>} An array of menu options with a value and name. * @return {!Array<!Object>} An array of menu options with a value and name.
* @private * @private
*/ */
menuOptionsForLang_(lang) { menuOptionsForLang_(lang) {
...@@ -290,8 +300,10 @@ Polymer({ ...@@ -290,8 +300,10 @@ Polymer({
/** /**
* Updates the preferences given the current list of voices. * Updates the preferences given the current list of voices.
* @param {Object<string, {language: string, code: string, preferred: boolean, * @param {!Object<string, !{language: string,
* voices: Array<TtsHandlerVoice>}>} langToVoices * code: string,
* preferred: boolean,
* voices: !Array<!TtsHandlerVoice>}>} langToVoices
* @private * @private
*/ */
updateLangToVoicePrefs_(langToVoices) { updateLangToVoicePrefs_(langToVoices) {
...@@ -334,8 +346,8 @@ Polymer({ ...@@ -334,8 +346,8 @@ Polymer({
/** /**
* Sets the voice to show in the preview drop-down as default, based on the * Sets the voice to show in the preview drop-down as default, based on the
* current locale and voice preferences. * current locale and voice preferences.
* @param {Array<TtsHandlerVoice>} allVoices * @param {!Array<!TtsHandlerVoice>} allVoices
* @param {Object<string, string>} languageCodeMap Mapping from language code * @param {!Object<string, string>} languageCodeMap Mapping from language code
* to simple language code without locale. * to simple language code without locale.
* @private * @private
*/ */
...@@ -374,7 +386,7 @@ Polymer({ ...@@ -374,7 +386,7 @@ Polymer({
/** /**
* Gets the best voice for the app locale. * Gets the best voice for the app locale.
* @param {Array<TtsHandlerVoice>} voices Voices to search through. * @param {!Array<!TtsHandlerVoice>} voices Voices to search through.
* @return {string} The ID of the best matching voice in the array. * @return {string} The ID of the best matching voice in the array.
* @private * @private
*/ */
...@@ -397,7 +409,7 @@ Polymer({ ...@@ -397,7 +409,7 @@ Polymer({
}, },
/** /**
* @param {{model:Object}} event * @param {!{model:Object}} event
* @private * @private
*/ */
onEngineSettingsTap_(event) { onEngineSettingsTap_(event) {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment