Voice instructions

Назначение голосовых инструкций

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

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

Типичная архитектура выглядит следующим образом:

  1. Получение маршрута через Directions API.
  2. Извлечение голосовых инструкций из ответа.
  3. Отслеживание текущего положения пользователя.
  4. Определение момента воспроизведения инструкции.
  5. Озвучивание текста через синтезатор речи.

Структура голосовых инструкций в Directions API

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

Пример запроса:

const url =
  'https://api.mapbox.com/directions/v5/mapbox/driving/' +
  `${startLng},${startLat};${endLng},${endLat}` +
  '?steps=true' +
  '&voice_instructions=true' +
  '&access_token=' + mapboxgl.accessToken;

Параметр:

voice_instructions=true

активирует генерацию голосовых подсказок.

Фрагмент ответа может выглядеть следующим образом:

{
  "voiceInstructions": [
    {
      "distanceAlongGeometry": 250,
      "announcement": "Через 250 метров поверните направо",
      "ssmlAnnouncement": "<speak>Через 250 метров поверните направо</speak>"
    }
  ]
}

Каждая инструкция содержит несколько важных полей.


Поле announcement

Наиболее часто используемое поле:

{
  "announcement": "Поверните налево"
}

Содержит обычный текст, предназначенный для отображения или озвучивания.

Использование:

const text =
  voiceInstruction.announcement;

Полученный текст может быть напрямую передан синтезатору речи.


Поле ssmlAnnouncement

SSML (Speech Synthesis Markup Language) позволяет более точно управлять произношением.

Пример:

<speak>
  Через
  <say-as interpret-as="cardinal">
    300
  </say-as>
  метров поверните налево
</speak>

Поле:

voiceInstruction.ssmlAnnouncement

может использоваться при интеграции с облачными голосовыми движками:

  • Amazon Polly;
  • Google Cloud Text-to-Speech;
  • Azure Speech Services;
  • другими SSML-совместимыми системами.

Для встроенного Web Speech API обычно применяется обычное поле announcement.


Поле distanceAlongGeometry

Показывает расстояние до точки выполнения манёвра.

Пример:

{
  "distanceAlongGeometry": 500
}

Это означает, что инструкция должна быть воспроизведена за 500 метров до манёвра.

Использование:

const distance =
  voiceInstruction.distanceAlongGeometry;

Значение применяется для определения момента воспроизведения подсказки.


Получение голосовых инструкций из маршрута

После получения маршрута необходимо пройти по его шагам.

Пример:

const route =
  data.routes[0];

route.legs.forEach(leg => {
  leg.steps.forEach(step => {

    if (step.voiceInstructions) {
      console.log(
        step.voiceInstructions
      );
    }

  });
});

Результат:

[
  {
    distanceAlongGeometry: 500,
    announcement:
      "Через 500 метров поверните направо"
  },
  {
    distanceAlongGeometry: 100,
    announcement:
      "Поверните направо"
  }
]

Для одного манёвра может существовать несколько голосовых предупреждений.


Формирование общего списка инструкций

Удобно собрать все инструкции в единый массив.

const instructions = [];

route.legs.forEach(leg => {
  leg.steps.forEach(step => {

    if (!step.voiceInstructions) {
      return;
    }

    step.voiceInstructions.forEach(
      instruction => {
        instructions.push(
          instruction
        );
      }
    );

  });
});

После обработки:

console.log(instructions);

можно получить полный список голосовых сообщений маршрута.


Использование Web Speech API

Большинство браузеров поддерживает встроенный механизм синтеза речи.

Простейшее воспроизведение:

function speak(text) {

  const utterance =
    new SpeechSynthesisUtterance(
      text
    );

  speechSynthesis.speak(
    utterance
  );
}

Вызов:

speak(
  "Через 300 метров поверните налево"
);

озвучит текст через системный голос устройства.


Настройка языка озвучивания

Для корректного произношения необходимо задавать язык.

function speak(text) {

  const utterance =
    new SpeechSynthesisUtterance(
      text
    );

  utterance.lang = 'ru-RU';

  speechSynthesis.speak(
    utterance
  );
}

Популярные значения:

'ru-RU'
'en-US'
'de-DE'
'fr-FR'
'es-ES'
'it-IT'

Выбор конкретного голоса

Получение списка доступных голосов:

const voices =
  speechSynthesis.getVoices();

console.log(voices);

Выбор:

const utterance =
  new SpeechSynthesisUtterance(
    text
  );

utterance.voice =
  voices.find(
    voice =>
      voice.lang === 'ru-RU'
  );

Это позволяет использовать наиболее качественный голос на устройстве пользователя.


Управление скоростью речи

Параметр:

utterance.rate

контролирует скорость произношения.

Пример:

utterance.rate = 0.9;

Варианты:

0.5
0.8
1.0
1.2
1.5

Для автомобильной навигации чаще используется диапазон:

0.9 – 1.1

Управление высотой голоса

Настройка тона:

utterance.pitch = 1;

Примеры:

0.8
1.0
1.2

Повышенный тон иногда используется для предупреждающих сообщений.


Управление громкостью

utterance.volume = 1;

Диапазон:

0.0 - 1.0

Пример:

utterance.volume = 0.8;

Отслеживание позиции пользователя

Для своевременного воспроизведения инструкций требуется получать текущие координаты.

navigator.geolocation.watchPosition(
  position => {

    const lng =
      position.coords.longitude;

    const lat =
      position.coords.latitude;

    updateLocation(
      lng,
      lat
    );

  }
);

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


Определение ближайшего шага маршрута

Каждый шаг содержит геометрию движения.

Пример:

const currentStep =
  route.legs[0].steps[
    activeStepIndex
  ];

Далее вычисляется расстояние от текущей позиции до конца шага.

const distanceToTurn =
  calculateDistance(
    userLocation,
    turnLocation
  );

Если расстояние становится меньше порогового значения, запускается голосовая подсказка.


Предотвращение повторного воспроизведения

Каждую инструкцию необходимо озвучивать только один раз.

Пример:

const spokenInstructions =
  new Set();

Проверка:

if (
  !spokenInstructions.has(
    instruction.id
  )
) {

  speak(
    instruction.announcement
  );

  spokenInstructions.add(
    instruction.id
  );
}

Без такой защиты голосовые сообщения будут постоянно повторяться.


Работа с несколькими предупреждениями

Для одного манёвра часто существует несколько сообщений:

Через 1000 метров поверните направо
Через 300 метров поверните направо
Поверните направо

Каждое предупреждение имеет собственное значение:

distanceAlongGeometry

Логика может выглядеть следующим образом:

if (
  distanceToTurn <=
  instruction.distanceAlongGeometry
) {
  speak(
    instruction.announcement
  );
}

Очередь голосовых сообщений

Если инструкции поступают слишком часто, полезно организовать очередь.

const queue = [];

Добавление:

queue.push(
  instruction.announcement
);

Воспроизведение:

function processQueue() {

  if (
    speechSynthesis.speaking
  ) {
    return;
  }

  const text =
    queue.shift();

  if (!text) {
    return;
  }

  speak(text);
}

Запуск:

setInterval(
  processQueue,
  500
);

Очередь предотвращает наложение сообщений друг на друга.


Отмена текущего сообщения

Иногда требуется немедленно остановить речь.

speechSynthesis.cancel();

Пример использования:

if (
  routeRecalculated
) {
  speechSynthesis.cancel();
}

После перестроения маршрута устаревшие инструкции становятся неактуальными.


Перестроение маршрута

При сходе пользователя с маршрута необходимо:

  1. Получить новый маршрут.
  2. Очистить список инструкций.
  3. Сбросить историю озвучивания.
  4. Начать отслеживание заново.

Пример:

spokenInstructions.clear();

instructions.length = 0;

После получения нового маршрута инструкции формируются повторно.


Локализация голосовых инструкций

Directions API поддерживает различные языки.

Пример:

const url =
  'https://api.mapbox.com/directions/v5/mapbox/driving/' +
  coordinates +
  '?steps=true' +
  '&voice_instructions=true' +
  '&language=ru' +
  '&access_token=' +
  mapboxgl.accessToken;

Параметр:

language=ru

заставляет сервис возвращать инструкции на русском языке.

Примеры:

language=en
language=de
language=fr
language=es
language=it

Совместное отображение текста и озвучивания

Голосовые инструкции часто сопровождаются визуальными подсказками.

Пример отображения:

instructionPanel.textContent =
  instruction.announcement;

Одновременно запускается речь:

speak(
  instruction.announcement
);

Такой подход улучшает восприятие навигации в шумной среде или при отключённом звуке.


Интеграция с Mapbox Navigation SDK

Хотя Mapbox GL JS предназначен главным образом для веб-картографии, его можно использовать как основу навигационного интерфейса.

Типичная схема:

Mapbox GL JS
      +
Directions API
      +
Geolocation API
      +
Web Speech API

Компоненты выполняют разные функции:

Компонент Назначение
Mapbox GL JS Отображение карты
Directions API Построение маршрута
Geolocation API Получение координат
Web Speech API Озвучивание инструкций

Подобная архитектура позволяет реализовать полноценную браузерную навигацию с визуальными и голосовыми подсказками без использования нативных мобильных SDK.