chromium/chrome/browser/resources/chromeos/accessibility/chromevox/background/tts_background.ts

// Copyright 2014 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

/**
 * @fileoverview Sends Text-To-Speech commands to Chrome's native TTS
 * extension API.
 */
import {BridgeHelper} from '/common/bridge_helper.js';
import {constants} from '/common/constants.js';
import {TestImportManager} from '/common/testing/test_import_manager.js';

import {BridgeConstants} from '../common/bridge_constants.js';
import {Msgs} from '../common/msgs.js';
import {SettingsManager} from '../common/settings_manager.js';
import {Personality, QueueMode, TtsSpeechProperties} from '../common/tts_types.js';

import {ChromeVox} from './chromevox.js';
import {CompositeTts} from './composite_tts.js';
import {ConsoleTts} from './console_tts.js';
import {Output} from './output/output.js';
import {PrimaryTts} from './primary_tts.js';

const Action = BridgeConstants.TtsBackground.Action;
const TARGET = BridgeConstants.TtsBackground.TARGET;

/** This class broadly handles TTS within the background context. */
export class TtsBackground {
  static instance: TtsBackground;

  private compositeTts_: CompositeTts;
  private consoleTts_: ConsoleTts;
  private primaryTts_: PrimaryTts;

  private constructor() {
    this.consoleTts_ = new ConsoleTts();
    this.primaryTts_ = new PrimaryTts();
    this.compositeTts_ =
        new CompositeTts().add(this.primaryTts_).add(this.consoleTts_);
  }
  static init(): void {
    TtsBackground.instance = new TtsBackground();
    ChromeVox.tts = TtsBackground.composite;

    BridgeHelper.registerHandler(
        TARGET, Action.UPDATE_PUNCTUATION_ECHO,
        (echo: number) => TtsBackground.primary.updatePunctuationEcho(echo));
    BridgeHelper.registerHandler(
        TARGET, Action.GET_CURRENT_VOICE,
        () => TtsBackground.primary.currentVoice);
  }

  static get composite(): CompositeTts {
    if (!TtsBackground.instance) {
      throw new Error(
          'Cannot access composite TTS before TtsBackground has been ' +
          'initialized.');
    }
    return TtsBackground.instance.compositeTts_;
  }

  static get console(): ConsoleTts {
    if (!TtsBackground.instance) {
      throw new Error(
          'Cannot access console TTS before TtsBackground has been ' +
          'initialized.');
    }
    return TtsBackground.instance.consoleTts_;
  }

  static get primary(): PrimaryTts {
    if (!TtsBackground.instance) {
      throw new Error(
          'Cannot access primary TTS before TtsBackground has been ' +
          'initialized.');
    }
    return TtsBackground.instance.primaryTts_;
  }

  static resetTextToSpeechSettings(): void {
    const rate = ChromeVox.tts.getDefaultProperty('rate');
    const pitch = ChromeVox.tts.getDefaultProperty('pitch');
    const volume = ChromeVox.tts.getDefaultProperty('volume');
    ChromeVox.tts.setProperty('rate', rate ? rate : 1);
    ChromeVox.tts.setProperty('pitch', pitch ? pitch : 1);
    ChromeVox.tts.setProperty('volume', volume ? volume : 1);
    SettingsManager.set('voiceName', constants.SYSTEM_VOICE);
    TtsBackground.primary.updateVoice('', () => {
      // Ensure this announcement doesn't get cut off by speech triggered by
      // updateFromPrefs_().
      const speechProperties = {...Personality.ANNOTATION};
      speechProperties.doNotInterrupt = true;

      ChromeVox.tts.speak(
          Msgs.getMsg('announce_tts_default_settings'), QueueMode.FLUSH,
          new TtsSpeechProperties(speechProperties));
    });
  }

  /** Toggles speech on or off and announces the change. */
  static toggleSpeechWithAnnouncement(): void {
    const state = ChromeVox.tts.toggleSpeechOnOrOff();
    new Output().format(state ? '@speech_on' : '@speech_off').go();
  }
}

TestImportManager.exportForTesting(TtsBackground);