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) {
"background/output_test.js",
"background/portals_test.js",
"background/recovery_strategy_test.js",
"background/settings_test.js",
"background/smart_sticky_mode_test.js",
"background/user_action_monitor_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({
/**
* Available languages.
* @type {Array<{language: string, code: string, preferred: boolean,
* @type {!Array<!{language: string, code: string, preferred: boolean,
* voice: TtsHandlerVoice}>}
*/
languagesToVoices: {
......@@ -33,7 +33,7 @@ Polymer({
/**
* All voices.
* @type {Array<TtsHandlerVoice>}
* @type {!Array<!TtsHandlerVoice>}
*/
allVoices: {
type: Array,
......@@ -93,50 +93,58 @@ Polymer({
},
/**
* Ticks for the Speech Rate slider. Non-linear as we expect people
* to want more control near 1.0.
* @return Array<cr_slider.SliderTick>
* Ticks for the Speech Rate slider. Valid rates are between 0.1 and 5.
* @return {!Array<!cr_slider.SliderTick>}
* @private
*/
speechRateTicks_() {
return Array.from(Array(16).keys()).map(x => {
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);
});
return this.buildLinearTicks_(0.1, 5);
},
/**
* Ticks for the Speech Pitch slider. Valid pitches are between 0 and 2,
* exclusive of 0.
* @return Array<cr_slider.SliderTick>
* Ticks for the Speech Pitch slider. Valid pitches are between 0.2 and 2.
* @return {!Array<!cr_slider.SliderTick>}
* @private
*/
speechPitchTicks_() {
return Array.from(Array(10).keys()).map(x => {
return this.initTick_(x * .2 + .2);
});
return this.buildLinearTicks_(0.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.
* The values are linear between .2 and 1.0.
* @return Array<cr_slider.SliderTick>
* @return {!Array<!cr_slider.SliderTick>}
* @private
*/
speechVolumeTicks_() {
return Array.from(Array(9).keys()).map(x => {
return this.initTick_(x * .1 + .2);
});
return this.buildLinearTicks_(0.2, 1);
},
/**
* 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.
* @param {number} tick The value to make a tick for.
* @return {cr_slider.SliderTick}
* @return {!cr_slider.SliderTick}
* @private
*/
initTick_(tick) {
......@@ -150,7 +158,7 @@ Polymer({
/**
* Returns true if any voices are loaded.
* @param {!Array<TtsHandlerVoice>} voices
* @param {!Array<!TtsHandlerVoice>} voices
* @return {boolean}
* @private
*/
......@@ -161,7 +169,7 @@ Polymer({
/**
* Returns true if voices are loaded and preview is not currently speaking and
* there is text to preview.
* @param {!Array<TtsHandlerVoice>} voices
* @param {!Array<!TtsHandlerVoice>} voices
* @param {boolean} isPreviewing
* @param {boolean} previewText
* @return {boolean}
......@@ -175,7 +183,7 @@ Polymer({
/**
* Populates the list of languages and voices for the UI to use in display.
* @param {Array<TtsHandlerVoice>} voices
* @param {!Array<!TtsHandlerVoice>} voices
* @private
*/
populateVoiceList_(voices) {
......@@ -216,9 +224,10 @@ Polymer({
/**
* Returns true if the language is a primary language and should be shown by
* 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
* @return {boolean} true if it's a primary language.
* @private
*/
isPrimaryLanguage_(language) {
return language.preferred;
......@@ -227,9 +236,10 @@ Polymer({
/**
* Returns true if the language is a secondary language and should be hidden
* 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
* @return {boolean} true if it's a secondary language.
* @private
*/
isSecondaryLanguage_(language) {
return !language.preferred;
......@@ -237,7 +247,7 @@ Polymer({
/**
* Sets the list of Text-to-Speech extensions for the UI.
* @param {Array<TtsHandlerExtension>} extensions
* @param {!Array<!TtsHandlerExtension>} extensions
* @private
*/
populateExtensionList_(extensions) {
......@@ -256,8 +266,8 @@ Polymer({
/**
* A function used for sorting languages alphabetically.
* @param {Object} first A languageToVoices array item.
* @param {Object} second A languageToVoices array item.
* @param {!Object} first A languageToVoices array item.
* @param {!Object} second A languageToVoices array item.
* @return {number} The result of the comparison.
* @private
*/
......@@ -267,7 +277,7 @@ Polymer({
/**
* 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.
* @private
*/
......@@ -278,8 +288,8 @@ Polymer({
/**
* 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.
* @param {Object} lang A languageToVoices array item.
* @return {Array<Object>} An array of menu options with a value and name.
* @param {!Object} lang A languageToVoices array item.
* @return {!Array<!Object>} An array of menu options with a value and name.
* @private
*/
menuOptionsForLang_(lang) {
......@@ -290,8 +300,10 @@ Polymer({
/**
* Updates the preferences given the current list of voices.
* @param {Object<string, {language: string, code: string, preferred: boolean,
* voices: Array<TtsHandlerVoice>}>} langToVoices
* @param {!Object<string, !{language: string,
* code: string,
* preferred: boolean,
* voices: !Array<!TtsHandlerVoice>}>} langToVoices
* @private
*/
updateLangToVoicePrefs_(langToVoices) {
......@@ -334,8 +346,8 @@ Polymer({
/**
* Sets the voice to show in the preview drop-down as default, based on the
* current locale and voice preferences.
* @param {Array<TtsHandlerVoice>} allVoices
* @param {Object<string, string>} languageCodeMap Mapping from language code
* @param {!Array<!TtsHandlerVoice>} allVoices
* @param {!Object<string, string>} languageCodeMap Mapping from language code
* to simple language code without locale.
* @private
*/
......@@ -374,7 +386,7 @@ Polymer({
/**
* 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.
* @private
*/
......@@ -397,7 +409,7 @@ Polymer({
},
/**
* @param {{model:Object}} event
* @param {!{model:Object}} event
* @private
*/
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