Web Speech API в JavaScript: от текста к речи

Я провожу много времени на Medium: иногда пишу, но по большей части читаю. Изучаю опыт разработчиков, которым они делятся с сообществом. 

Недавно заметил, что для каждой статьи Medium предлагает кнопку воспроизведения. Сначала подумал, что такая привилегированная функция предоставляется только избранным статьям или авторам. Но оказывается, она доступна всем! Это значит, что на Medium можно слушать материал публикаций.

И, как любой разработчик, я сразу же заинтересовался решением этой задачи. Для такого случая JavaScript предоставляет веб-API под названием Web Speech API. Однако сам я с ним еще не работал. 

Поэтому предлагаю совместно познакомиться с этим инструментом и применить его на практике. 

Голосовые данные встраиваются в онлайн-приложения с помощью Web Speech API. Мы создадим простую веб-страницу, которая реализует преобразование текста в речь. 

Сначала создаем новую директорию и 2 файла: index.html и text-to-speech.js.

Файл HTML

Файл HTML включает следующие элементы.

  • Меню select без предлагаемых вариантов. С помощью JavaScript пустое меню select заполняется списком возможных голосов. 
  • Слайдеры диапазона для громкости, высоты и скорости звука.
  • textarea для ввода текста.
  • Кнопки управления речью. 

В демопримере для стилевого оформления кода используется Bootstrap 5. Ниже представлен код: 

<html lang="en">

<head>
<link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" rel="stylesheet" />
<title>Text to Speech</title>

<style>
.card {
padding: 1rem 1.5rem;
border-radius: 7px;
box-shadow: rgb(45 46 46 / 35%) 0px 4px 32px 0px, rgb(45 46 46 / 8%) 0px 4px 16px 0px, rgb(45 46 46 / 10%) 0px 0px 4px 0px;
}
</style>
</head>

<body class="container mt-5 bg-light">
<div class="card">
<h2>Convert Text to Speech</h2>
<h5>Listen to those words!</h5>
<p class="lead mt-4">Select Voice</p>

<!-- Select Menu for Voice -->
<select id="voices" class="form-select"></select>

<!-- Range Slliders for Volume, Rate & Pitch -->
<div class="d-flex mt-4">
<div>
<p class="lead">Volume</p>
<input type="range" min="0" max="1" value="1" step="0.1" id="volume" />
<span id="volume-label" class="ms-2">1</span>
</div>
<div class="mx-5">
<p class="lead">Rate</p>
<input type="range" min="0.1" max="10" value="1" id="rate" step="0.1" />
<span id="rate-label" class="ms-2">1</span>
</div>
<div>
<p class="lead">Pitch</p>
<input type="range" min="0" max="1" value="1" step="0.1" id="pitch" />
<span id="pitch-label" class="ms-2">1</span>
</div>
</div>

<!-- Text Area for the User to Type -->
<textarea class="form-control mt-5" cols="30" rows="10" placeholder="Type here..."></textarea>

<!-- Control Buttons -->
<div class="mb-5">
<button id="start" class="btn btn-success mt-5 me-3">Start</button>
<button id="pause" class="btn btn-warning mt-5 me-3">Pause</button>
<button id="resume" class="btn btn-info mt-5 me-3">Resume</button>
<button id="cancel" class="btn btn-danger mt-5 me-3">Cancel</button>
</div>
</div>
</body>
<script src="./text-to-speech.js"></script>

</html>

Так код выглядит в браузере: 

Файл JavaScript

В файле JavaScript используются в основном 3 интерфейса: SpeechSynthesis , window.speechSynthesis и SpeechSynthesisUtterance. Кратко рассмотрим каждый из них. 

Интерфейс JavaScript SpeechSynthesis

Представляет собой главный интерфейс сервиса синтеза речи, который управляет синтезом или воспроизведением речи на основе текстового ввода. Он применяется для запуска, остановки, паузы и перезапуска речи, а также для доступа к голосам, поддерживаемых устройством. 

Интерфейс SpeechSynthesis предоставляет следующие методы.

  • speak() добавляет высказывание (объект SpeechSynthesisUtterance) в очередь. Оно озвучивается в том случае, если перед ним нет ожидающего высказывания. 
  • pause() приостанавливает текущую речь. 
  • resume() возобновляет приостановленную речь. 
  • cancel() отменяет все ожидающие высказывания или созданные фрагменты речи, которые еще не были воспроизведены. 
  • getVoices() получает список всех голосов, поддерживаемых устройством. 

Свойство JavaScript window.speechSynthesis 

Метод speak() вызывается в интерфейсе контроллера синтеза голоса, на который ссылается данное свойство объекта JavaScript window.

Когда мы дойдем до кода, эти теоретические моменты станут более понятными. 

Интерфейс JavaScript SpeechSynthesisUtterance 

В SpeechSynthesisUtterance мы фактически воспроизводим речь или высказывание на основе предлагаемого текста, включая язык, громкость, высоту голоса, скорость речи и т. д. Создав объект для данного интерфейса, мы предоставляем его методу speak() объекта SpeechSynthesis для воспроизведения речи.

Интерфейс SpeechSynthesisUtterance включает 6 настраиваемых свойств.

Language (язык)

Свойство language получает и устанавливает язык высказывания. Если оно не задано, то используется значение <html lang=”en”>. Если же <html lang=”en”> недоступно, то задействуется предустановленное значение user-agent.

speech.lang = "en";

Text (текст)

При озвучивании высказывания свойство text получает и задает текст для последующего синтеза. Допускается отправка текста в обычном текстовом формате. В рассматриваемом примере свойство text устанавливается при нажатии кнопки запуска Start.

Назначаем кнопке слушателя click. Мы должны извлечь значение text из textarea и установить его для данного свойства при нажатии кнопки: 

document.querySelector("#start").addEventListener("click", () => {
speech.text = document.querySelector("textarea").value;
});

Volume (громкость)

Свойство volume получает и устанавливает громкость высказывания. Это число с плавающей точкой, которое указывает значение volume и варьируется от наименьшей величины 0 до наибольшей 1. Если свойство не задано, то значение по умолчанию  —  1. 

Добавляем слушателя input к слайдеру диапазона громкости и меняем свойство volume при обновлении значения слайдера. Минимальные, максимальные и предустановленные значения уже указаны в теге HTML. 

Далее к слайдеру диапазона добавляем <span>, который отображает значение volume на веб-странице:

document.querySelector("#volume").addEventListener("input", () => {
  // Получение значения volume из input
  const volume = document.querySelector("#volume").value;

  // Установка свойства volume экземпляра SpeechSynthesisUtterance 
  speech.volume = volume;

  // Обновление метки volume
  document.querySelector("#volume-label").innerHTML = volume;
});

Rate (скорость)

Свойство rate возвращает и устанавливает скорость высказывания. Это число с плавающей точкой, представляющее значение rate. Оно варьируется от наименьшей величины 0,1 до наибольшей 10. Если свойство не задано, значение по умолчанию  —  1.

Все действия, выполненные для volume, повторяем применительно к rate

document.querySelector("#rate").addEventListener("input", () => {
  // Получение значения rate из input
  const rate = document.querySelector("#rate").value;

  // Установка свойства rate экземпляра SpeechSynthesisUtterance 
  speech.rate = rate;

  // Обновление метки rate 
  document.querySelector("#rate-label").innerHTML = rate;
});

Pitch (высота звука)

Свойство pitch возвращает и устанавливает высоту звука высказывания. Значение также представлено в виде числа с плавающей точкой в диапазоне от 0 до 1. 

По аналогии с rate и volume выполняем те же действия по отношению к pitch:

document.querySelector("#pitch").addEventListener("input", () => {
  // Получение значения pitch из input
  const pitch = document.querySelector("#pitch").value;

  // Установка свойства pitch экземпляра SpeechSynthesisUtterance 
  speech.pitch = pitch;

  // Обновление метки pitch
  document.querySelector("#pitch-label").innerHTML = pitch;
});

Voice (голос)

Свойство voice извлекает и преобразует голос, предназначенный для произнесения речи. Мы используем один из объектов SpeechSynthesisVoice. Если он не настроен, то применяется наиболее подходящий предустановленный голос для языковой настройки высказывания. 

Мы должны извлечь список имеющихся голосов в объекте window, чтобы установить голоса высказывания. Они не будут доступны сразу же при загрузке window. Это асинхронная операция. Как только голоса загружаются, вызывается событие. Мы можем задать функцию, которая будет выполняться в момент загрузки голосов. 

window.speechSynthesis.onvoiceschanged = () => {
// По факту загрузки голосов
};

Метод window.speechSynthesis.getVoices() позволяет получить список голосов. Он возвращает массив доступных объектов SpeechSynthesisVoice. Сохраним список в глобальном массиве и воспользуемся им для обновления меню select предлагаемыми голосами:

let voices = []; // global array

window.speechSynthesis.onvoiceschanged = () => {
// Получение списка голосов
voices = window.speechSynthesis.getVoices();

// Первоначальная установка первого голоса в массиве.
speech.voice = voices[0];

// Установка списка выбора голоса (задаем индекс в качестве значения, который в дальнейшем потребуется при обновлении пользователем голоса посредством меню Select)
let voiceSelect = document.querySelector("#voices");
voices.forEach((voice, i) => (voiceSelect.options[i] = new Option(voice.name, i)));
};

В связи с изменением меню голосов мы можем добавить к нему слушателя события change и обновить голос экземпляра SpeechSynthesisUtterance. Воспользуемся номером индекса, установленного в качестве значения для каждого варианта, и глобальным массивом голосов, чтобы обновить голос при его изменении пользователем: 

document.querySelector("#voices").addEventListener("change", () => {
speech.voice = voices[document.querySelector("#voices").value];
});

Кнопки управления

Как вы помните, файл index.html содержит кнопки управления, такие как start (запуск), resume (возобновление), pause (пауза) и cancel (отмена). Приведем их в рабочее состояние с помощью интерфейса SpeechSynthesis и его методов. 

Кнопка Start

При нажатии кнопки start экземпляр SpeechSynthesisUtterance передается в window.speechSynthesis.speak(). Это действие начинает процесс преобразования текста в речь. 

Перед вызовом данной функции необходимо установить свойство text.

Если во время работы одного экземпляра text-to-speech вы запускаете еще один, то новый будет поставлен в очередь вслед за текущим.

document.querySelector("#start").addEventListener("click", () => {
speech.text = document.querySelector("textarea").value;
window.speechSynthesis.speak(speech);
});

Кнопка Pause

window.speechSynthesis.pause() приостанавливает текущее выполнение экземпляра SpeechSynthesisUtterance:

document.querySelector("#pause").addEventListener("click", () => {
window.speechSynthesis.pause();
});

Кнопка Resume

window.speechSynthesis.resume() возобновляет работу приостановленного текущего экземпляра SpeechSynthesisUtterance:

document.querySelector("#resume").addEventListener("click", () => {
window.speechSynthesis.resume();
});

Кнопка Cancel

window.speechSynthesis.cancel() отменяет выполнение текущего экземпляра SpeechSynthesisUtterance:

document.querySelector("#cancel").addEventListener("click", () => {
window.speechSynthesis.cancel();
});

Все кнопки управления готовы, и необходимые свойства установлены. Ниже представлен окончательный вариант text-to-speech.js:

// Инициализация нового объекта SpeechSynthesisUtterance
let speech = new SpeechSynthesisUtterance();

// Установка языка речи
speech.lang = "en";

let voices = []; // глобальный массив доступных голосов

window.speechSynthesis.onvoiceschanged = () => {
// Получение списка голосов
voices = window.speechSynthesis.getVoices();

// Первоначальная установка первого голоса в массиве
speech.voice = voices[0];

// Установка списка выбора голосов (задаем индекс в качестве значения, который в дальнейшем потребуется при обновлении пользователем голоса посредством меню Select)
let voiceSelect = document.querySelector("#voices");
voices.forEach((voice, i) => (voiceSelect.options[i] = new Option(voice.name, i)));
};

document.querySelector("#rate").addEventListener("input", () => {
// Получение значения rate из input
const rate = document.querySelector("#rate").value;

// Установка свойства rate экземпляра SpeechSynthesisUtterance
speech.rate = rate;

// Обновление метки rate
document.querySelector("#rate-label").innerHTML = rate;
});

document.querySelector("#volume").addEventListener("input", () => {
// Получение значения volume из input
const volume = document.querySelector("#volume").value;

// Установка свойства volume экземпляра SpeechSynthesisUtterance
speech.volume = volume;

// Обновление метки volume
document.querySelector("#volume-label").innerHTML = volume;
});

document.querySelector("#pitch").addEventListener("input", () => {
// Получение значения pitch из input
const pitch = document.querySelector("#pitch").value;

// Установка свойства pitch экземпляра SpeechSynthesisUtterance
speech.pitch = pitch;

// Обновление метки pitch
document.querySelector("#pitch-label").innerHTML = pitch;
});

document.querySelector("#voices").addEventListener("change", () => {
// При изменении голоса используется значение меню выбора (которое является индексом голоса в глобальном массиве голосов)
speech.voice = voices[document.querySelector("#voices").value];
});

document.querySelector("#start").addEventListener("click", () => {
// Установка свойства text со значением textarea
speech.text = document.querySelector("textarea").value;

// Запуск озвучки
window.speechSynthesis.speak(speech);
});

document.querySelector("#pause").addEventListener("click", () => {
// Приостановка экземпляра speechSynthesis
window.speechSynthesis.pause();
});

document.querySelector("#resume").addEventListener("click", () => {
// Возобновление приостановленного экземпляра speechSynthesis
window.speechSynthesis.resume();
});

document.querySelector("#cancel").addEventListener("click", () => {
// Отмена экземпляра speechSynthesis
window.speechSynthesis.cancel();
});

Полученный результат на экране браузера: 

Пользователь просто вводит текст в textarea, нажимает на кнопку Start и его прослушивает. 

Читайте также:

Читайте нас в TelegramVK и Яндекс.Дзен


Перевод статьи Gourav Kajal: Convert Text to Speech Using Web Speech API in JavaScript

Предыдущая статьяНовый шаг к будущему без языковых границ
Следующая статьяСобеседование в области науки о данных: 7 распространенных ошибок