Статьи

Экспериментируя с Web Speech API

Несколько дней назад я выступал на WebTech Conference 2014 с презентацией под названием « Говоря и слушая веб-страницы», где я обсуждал API веб-речи и что может сделать с ним разработчик, чтобы улучшить взаимодействие с пользователем. Этот доклад был вдохновлен двумя статьями, которые я написал для SitePoint под названием « Представление Web Speech API и Talking Web Pages и API синтеза речи» .

В этом уроке мы будем опираться на полученные знания и разработаем демонстрационную версию, в которой будут использоваться оба интерфейса, определенных этим API. Если вам нужно введение в API Web Speech, я рекомендую прочитать две ранее упомянутые статьи, потому что эта предполагает, что вы хорошо разбираетесь в ней. Веселиться!

Разработка интерактивной формы

Цель этой статьи — создать интерактивную форму, которую наши пользователи могут заполнить своим голосом. Ради этого примера мы разработаем регистрационную форму, но вы можете применить те же понятия к любой форме, которую вы хотите. Важно помнить, что использование голоса никогда не должно быть единственным источником ввода, потому что независимо от того, насколько точен распознаватель речи, он никогда не будет идеальным. Таким образом, пользователь всегда должен иметь возможность изменять любое поле, чтобы исправить любую ошибку, допущенную распознавателем.

В этой демонстрации мы предоставим кнопку, которая после нажатия начнет задавать вопрос пользователю, а затем взаимодействие продолжится, когда пользователь говорит ответ. Распознаватель преобразует речь в текст, который помещается в текстовое поле. После завершения взаимодействия, что означает, что все поля нашей формы заполнены, наше приложение будет вежливым и поблагодарит пользователя.

В заключение отметим, что на момент написания этой статьи Web Speech API был очень экспериментальным и полностью поддерживался только Chrome. Поэтому наш эксперимент будет работать только в этом браузере. Без дальнейших церемоний, давайте начнем создавать разметку регистрационной формы.

HTML-код регистрационной формы

Чтобы было как можно проще, наша форма будет содержать только три поля, но вы можете добавить столько, сколько вам нужно. В частности, мы будем требовать от нашего пользователя указать имя, фамилию и национальность. Если у вас есть базовые знания HTML, выполнение этой задачи должно быть довольно простым. Я предлагаю вам попробовать реализовать это, прежде чем взглянуть на код ниже (моя реализация):

<form> <label for="form-demo-name">Name:</label> <input id="form-demo-name" /> <label for="form-demo-surname">Surname:</label> <input id="form-demo-surname" /> <label for="form-demo-nationality">Nationality:</label> <input id="form-demo-nationality" /> <input id="form-demo-voice" type="submit" value="Start" /> </form> 

Предыдущий код показывает только классическую форму, которую можно заполнить только с помощью клавиатуры или подобных устройств ввода. Итак, нам нужно найти способ указать вопрос, который мы хотим задать для каждого из полей, определенных в форме. Хорошее и простое решение — использовать атрибут data-* в HTML5. В частности, мы будем указывать атрибут data-question для каждой labelinput пара. Я решил установить для атрибута label связанную с input но вы можете легко изменить демонстрационную версию, чтобы определить атрибут для элемента input .

Полученный код показан ниже:

 <form> <label for="form-demo-name" data-question="What's your name?">Name:</label> <input id="form-demo-name" /> <label for="form-demo-surname" data-question="What's your surname?">Surname:</label> <input id="form-demo-surname" /> <label for="form-demo-nationality" data-question="What's your nationality?">Nationality:</label> <input id="form-demo-nationality" /> <input id="form-demo-voice" type="submit" value="Start" /> </form> 

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

Добавление бизнес-логики

Для разработки бизнес-логики нашей формы нам нужны три компонента: синтезатор речи, распознаватель речи и обещания . Нам нужен синтезатор речи, чтобы издавать звук, который задает пользователю вопрос, который мы определили с помощью атрибута data-question . Распознаватель речи используется для преобразования ответа пользователя в текст, который будет установлен в качестве значения каждого поля. Наконец, нам нужны обещания избежать ада обратного вызова! ,

WebSpeech API управляется асинхронными операциями, поэтому нам нужен способ синхронизации всех операций. Нам нужно начать распознавать речь пользователя после того, как вопрос задан, и мы должны задать новый вопрос после того, как пользователь произнес свой ответ и распознаватель завершил свою работу. Таким образом, нам необходимо синхронизировать набор переменных последовательных (последовательных) асинхронных операций. Мы можем легко решить эту проблему, приняв обещания в нашем коде. Если вам нужен учебник по обещаниям, SitePoint расскажет вам статью «Обзор обещаний JavaScript» . Еще одна очень хорошая статья была написана Джейком Арчибальдом и называется « Обещания JavaScript»: туда и обратно .

Наш код будет логически разделен на две части: библиотеку поддержки, которая работает с Web Speech API и будет выступать в роли производителя обещаний, и код, который будет использовать обещания. В следующих двух разделах этой статьи мы поговорим о них.

Разработка библиотеки поддержки

Если у вас есть практические знания о том, как работает Web Speech API, понять библиотеку поддержки не составит особого труда.

Мы определим литерал объекта, который мы назначим переменной с именем Speech . Этот объект имеет два метода: speak и recognize . Первый принимает текст, чтобы говорить и будет нести ответственность за передачу звука, а также создать обещание, связанное с этой операцией. Обещание будет выполнено в случае, если ошибка не возникнет (событие error ), или будет отклонено, если событие error инициировано. Обещание также будет отклонено, если браузер не поддерживает API. Метод recognize используется для распознавания речи пользователя. Он не принимает никаких аргументов и возвращает распознанный текст, передав его методу разрешения созданного обещания. Как вы увидите, recognize немного сложнее по сравнению с speak потому что оно имеет дело с большим количеством ситуаций. Обещание, созданное recognize будет разрешено, когда окончательные результаты будут доступны или отклонены в случае возникновения ошибки. Обратите внимание, что код также позаботится о решении проблемы, обнаруженной мной несколько дней назад в Windows 8.1 (# 428873) .

Полный код нашей библиотеки поддержки показан ниже:

 var Speech = { speak: function(text) { return new Promise(function(resolve, reject) { if (!SpeechSynthesisUtterance) { reject('API not supported'); } var utterance = new SpeechSynthesisUtterance(text); utterance.addEventListener('end', function() { console.log('Synthesizing completed'); resolve(); }); utterance.addEventListener('error', function (event) { console.log('Synthesizing error'); reject('An error has occurred while speaking: ' + event.error); }); console.log('Synthesizing the text: ' + text); speechSynthesis.speak(utterance); }); }, recognize: function() { return new Promise(function(resolve, reject) { var SpeechRecognition = SpeechRecognition || webkitSpeechRecognition || null; if (SpeechRecognition === null) { reject('API not supported'); } var recognizer = new SpeechRecognition(); recognizer.addEventListener('result', function (event) { console.log('Recognition completed'); for (var i = event.resultIndex; i < event.results.length; i++) { if (event.results[i].isFinal) { resolve(event.results[i][0].transcript); } } }); recognizer.addEventListener('error', function (event) { console.log('Recognition error'); reject('An error has occurred while recognizing: ' + event.error); }); recognizer.addEventListener('nomatch', function (event) { console.log('Recognition ended because of nomatch'); reject('Error: sorry but I could not find a match'); }); recognizer.addEventListener('end', function (event) { console.log('Recognition ended'); // If the Promise isn't resolved or rejected at this point // the demo is running on Chrome and Windows 8.1 (issue #428873). reject('Error: sorry but I could not recognize your speech'); }); console.log('Recognition started'); recognizer.start(); }); } }; 

Собираем все части вместе

Имея нашу библиотеку поддержки, нам нужно написать код, который будет извлекать вопросы, которые мы задали, и взаимодействовать с библиотекой поддержки для создания интерактивной формы.

Первое, что нам нужно сделать, — это получить все label нашей формы, потому что мы будем использовать их атрибут for для получения input data-question атрибут data-question чтобы задать вопросы. Эта операция выполняется с помощью инструкции ниже:

 var fieldLabels = [].slice.call(document.querySelectorAll('label')); 

Вспоминая, как мы написали разметку, мы можем сократить необходимый код, сохранив пары label input , что означает, что пары вопрос-ответ связаны. Мы можем сделать это с помощью функции поддержки, которую мы будем называть formData . Его цель — вернуть новое обещание, сгенерированное каждой парой label input . Обработка каждой label и input в нашей форме как уникального компонента, а не разных сущностей, позволяет нам сократить необходимый код, поскольку мы можем извлечь более абстрактный код и выполнить цикл по ним.

Код функции formData и способ ее formData показан ниже:

 function formData(i) { return promise.then(function() { return Speech.speak(fieldLabels[i].dataset.question); }) .then(function() { return Speech.recognize().then(function(text) { document.getElementById(fieldLabels[i].getAttribute('for')).value = text; }); }); } for(var i = 0; i < fieldLabels.length; i++) { promise = formData(i); } 

Поскольку мы formData обещания, как показано в функции formData нам нужно первоначальное обещание, которое разрешается, чтобы другие могли запускаться. Эта задача достигается созданием обещания, немедленно решаемого перед циклом предыдущего фрагмента:

 var promise = new Promise(function(resolve) { resolve(); }); 

В качестве последнего штриха мы хотим поблагодарить вас, наши пользователи, но также отловить любую возможную ошибку, сгенерированную нашим процессом

 promise.then(function() { return Speech.speak('Thank you for filling the form!'); }) .catch(function(error) { alert(error); }); 

На данный момент наш код почти завершен. Последний шаг — поместить весь код этого раздела в функцию, выполняемую, когда пользователь нажимает кнопку.

Результат

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

Результат разработанного кода показан ниже, но он также доступен в виде JSBin :

Вывод

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

Надеюсь, вам понравился этот урок, и вы узнали что-то новое и интересное.