Приложение Express.js преобразует текст в речь с помощью службы "Речь" из Cognitive Services

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

В этом руководстве приведено три разных способа преобразования текста в речь с помощью службы "Речь" из Azure Cognitive Services:

  • Клиент JavaScript получает аудио напрямую.
  • Серверный код JavaScript получает аудио из файла (*.MP3).
  • Серверный код JavaScript получает аудио из arrayBuffer в памяти.

Архитектура приложения

В этом руководстве описано, как создать простое приложение Express.js и добавить функциональные возможности с помощью таких сочетаний:

  • новый маршрут для API сервера для преобразования текста в речь с результирующим потоком в формате MP3;
  • новый маршрут для HTML-формы, позволяющей ввести данные;
  • новая HTML-форма с JavaScript, обеспечивающая вызов к службе "Речь" на стороне клиента.

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

  • Первый вызов сервера создает файл на сервере и возвращает его клиенту. Обычно этот вызов используется для более длинного текста или текста, который нужно озвучить более одного раза.
  • Второй вызов сервера предназначен для краткосрочного текста и хранится в памяти, прежде чем вернуться клиенту.
  • Вызов клиента демонстрирует прямой вызов к службе "Речь" с использованием пакета SDK. Этот вызов подходит в тех случаях, если у вас есть клиентское приложение без сервера.

Необходимые компоненты

  • Node.js LTS — установлен на локальный компьютер.
  • Установите на локальный компьютер Visual Studio Code.
  • Расширение Службы приложений Azure для VS Code (установленное в VS Code).
  • Средство Git для отправки данных в GitHub, по которым активируется действие GitHub.
  • Использование Azure Cloud Shell с помощью bash Embed launch
  • При необходимости установите Azure CLI, чтобы выполнять справочные команды CLI.
    • Если вы используете локальную установку, выполните вход с помощью команды Azure CLI az login. Чтобы выполнить аутентификацию, следуйте инструкциям в окне терминала. Сведения о дополнительных вариантах входа см. в статье Вход с помощью Azure CLI.
    • Если появится запрос, установите расширения Azure CLI при первом использовании. Дополнительные сведения о расширениях см. в статье Использование расширений с Azure CLI.
    • Выполните команду az version, чтобы узнать установленную версию и зависимые библиотеки. Чтобы обновиться до последней версии, выполните команду az upgrade.

Скачивание примера репозитория Express.js

  1. С помощью Git клонируйте пример репозитория Express.js на локальный компьютер.

    git clone https://github.com/Azure-Samples/js-e2e-express-server
    
  2. Перейдите в новый каталог для примера.

    cd js-e2e-express-server
    
  3. Откройте проект в Visual Studio Code.

    code .
    
  4. Откройте новый терминал в Visual Studio Code и установите зависимости проекта.

    npm install
    

Установка пакета SDK службы "Речь" из Cognitive Services для JavaScript

В терминале Visual Studio Code установите пакет SDK службы "Речь" из Azure Cognitive Services.

npm install microsoft-cognitiveservices-speech-sdk

Создание модуля службы "Речь" для приложения Express.js

  1. Чтобы интегрировать пакет SDK службы "Речь" с приложением Express.js, создайте файл azure-cognitiveservices-speech.js в папке src.

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

    // azure-cognitiveservices-speech.js
    
    const sdk = require('microsoft-cognitiveservices-speech-sdk');
    const { Buffer } = require('buffer');
    const { PassThrough } = require('stream');
    const fs = require('fs');
    
    /**
     * Node.js server code to convert text to speech
     * @returns stream
     * @param {*} key your resource key
     * @param {*} region your resource region
     * @param {*} text text to convert to audio/speech
     * @param {*} filename optional - best for long text - temp file for converted speech/audio
     */
    const textToSpeech = async (key, region, text, filename)=> {
        
        // convert callback function to promise
        return new Promise((resolve, reject) => {
            
            const speechConfig = sdk.SpeechConfig.fromSubscription(key, region);
            speechConfig.speechSynthesisOutputFormat = 5; // mp3
            
            let audioConfig = null;
            
            if (filename) {
                audioConfig = sdk.AudioConfig.fromAudioFileOutput(filename);
            }
            
            const synthesizer = new sdk.SpeechSynthesizer(speechConfig, audioConfig);
    
            synthesizer.speakTextAsync(
                text,
                result => {
                    
                    const { audioData } = result;
    
                    synthesizer.close();
                    
                    if (filename) {
                        
                        // return stream from file
                        const audioFile = fs.createReadStream(filename);
                        resolve(audioFile);
                        
                    } else {
                        
                        // return stream from memory
                        const bufferStream = new PassThrough();
                        bufferStream.end(Buffer.from(audioData));
                        resolve(bufferStream);
                    }
                },
                error => {
                    synthesizer.close();
                    reject(error);
                }); 
        });
    };
    
    module.exports = {
        textToSpeech
    };
    
    • Параметры — файл извлекает зависимости для использования пакета SDK, потоков, буферов и файловой системы. Функция textToSpeech принимает четыре аргумента. Если отправляется имя файла с локальным путем, текст преобразуется в звуковой файл. Если имя файла не отправляется, создается аудиопоток в памяти.
    • Метод в пакете SDK службы "Речь" — метод synthesizer.speakTextAsync возвращает различные типы в зависимости от полученной конфигурации. Метод возвращает результат, который отличается в зависимости от того, какой метод был предложено сделать:
      • Создать файл
      • Создать поток в памяти в виде массива буферов.
    • Формат аудио — для аудио выбран формат MP3, но существуют и другие форматы, а также другие методы настройки аудио.

    Локальный метод textToSpeech упаковывает и преобразует функцию обратного вызова пакета SDK в обещание.

Создание нового маршрута для приложения Express.js

  1. Откройте файл src/server.js.

  2. Добавьте модуль azure-cognitiveservices-speech.js в качестве зависимости в начале файла:

    const { textToSpeech } = require('./azure-cognitiveservices-speech');
    
  3. Добавьте новый маршрут API для вызова метода textToSpeech, создание которого описано в предыдущем разделе руководства. Добавьте этот код после /api/hello маршрута.

    // creates a temp file on server, the streams to client
    /* eslint-disable no-unused-vars */
    app.get('/text-to-speech', async (req, res, next) => {
        
        const { key, region, phrase, file } = req.query;
        
        if (!key || !region || !phrase) res.status(404).send('Invalid query string');
        
        let fileName = null;
        
        // stream from file or memory
        if (file && file === true) {
            fileName = `./temp/stream-from-file-${timeStamp()}.mp3`;
        }
        
        const audioStream = await textToSpeech(key, region, phrase, fileName);
        res.set({
            'Content-Type': 'audio/mpeg',
            'Transfer-Encoding': 'chunked'
        });
        audioStream.pipe(res);
    });
    

    Этот метод принимает обязательные и необязательные параметры для метода textToSpeech из строки запроса. Если требуется создать файл, генерируется уникальное имя файла. Метод textToSpeech вызывается асинхронно и передает результат в объект ответа (res).

Обновление веб-страницы клиента с помощью формы

Обновите веб-страницу клиента в формате HTML с помощью формы, которая собирает обязательные параметры. Необязательный параметр передается в зависимости от того, какой элемент управления звуком выбрал пользователь. Поскольку в этом руководстве предоставляется механизм для вызова службы "Речь" Azure из клиента, также предоставляется код JavaScript.

Откройте файл /public/client.html и замените имеющееся содержимое следующим кодом:

<!DOCTYPE html>
<html lang="en">

<head>
  <title>Microsoft Cognitive Services Demo</title>
  <meta charset="utf-8" />
</head>

<body>

  <div id="content" style="display:none">
    <h1 style="font-weight:500;">Microsoft Cognitive Services Speech </h1>
    <h2>npm: microsoft-cognitiveservices-speech-sdk</h2>
    <table width="100%">
      <tr>
        <td></td>
        <td>
          <a href="https://docs.microsoft.com/azure/cognitive-services/speech-service/get-started" target="_blank">Azure
            Cognitive Services Speech Documentation</a>
        </td>
      </tr>
      <tr>
        <td align="right">Your Speech Resource Key</td>
        <td>

          <input id="resourceKey" type="text" size="40" placeholder="Your resource key (32 characters)" value=""
            onblur="updateSrc()">

      </tr>
      <tr>
        <td align="right">Your Speech Resource region</td>
        <td>
          <input id="resourceRegion" type="text" size="40" placeholder="Your resource region" value="eastus"
            onblur="updateSrc()">

        </td>
      </tr>
      <tr>
        <td align="right" valign="top">Input Text (max 255 char)</td>
        <td><textarea id="phraseDiv" style="display: inline-block;width:500px;height:50px" maxlength="255"
            onblur="updateSrc()">all good men must come to the aid</textarea></td>
      </tr>
      <tr>
        <td align="right">
          Stream directly from Azure Cognitive Services
        </td>
        <td>
          <div>
            <button id="clientAudioAzure" onclick="getSpeechFromAzure()">Get directly from Azure</button>
          </div>
        </td>
      </tr>

      <tr>
        <td align="right">
          Stream audio from file on server</td>
        <td>
          <audio id="serverAudioFile" controls preload="none" onerror="DisplayError()">
          </audio>
        </td>
      </tr>

      <tr>
        <td align="right">Stream audio from buffer on server</td>
        <td>
          <audio id="serverAudioStream" controls preload="none" onerror="DisplayError()">
          </audio>
        </td>
      </tr>
    </table>
  </div>

  <!-- Speech SDK reference sdk. -->
  <script
    src="https://cdn.jsdelivr.net/npm/microsoft-cognitiveservices-speech-sdk@latest/distrib/browser/microsoft.cognitiveservices.speech.sdk.bundle-min.js">
    </script>

  <!-- Speech SDK USAGE -->
  <script>
    // status fields and start button in UI
    var phraseDiv;
    var resultDiv;

    // subscription key and region for speech services.
    var resourceKey = null;
    var resourceRegion = "eastus";
    var authorizationToken;
    var SpeechSDK;
    var synthesizer;

    var phrase = "all good men must come to the aid"
    var queryString = null;

    var audioType = "audio/mpeg";
    var serverSrc = "/text-to-speech";

    document.getElementById('serverAudioStream').disabled = true;
    document.getElementById('serverAudioFile').disabled = true;
    document.getElementById('clientAudioAzure').disabled = true;

    // update src URL query string for Express.js server
    function updateSrc() {

      // input values
      resourceKey = document.getElementById('resourceKey').value.trim();
      resourceRegion = document.getElementById('resourceRegion').value.trim();
      phrase = document.getElementById('phraseDiv').value.trim();

      // server control - by file
      var serverAudioFileControl = document.getElementById('serverAudioFile');
      queryString += `%file=true`;
      const fileQueryString = `file=true&region=${resourceRegion}&key=${resourceKey}&phrase=${phrase}`;
      serverAudioFileControl.src = `${serverSrc}?${fileQueryString}`;
      console.log(serverAudioFileControl.src)
      serverAudioFileControl.type = "audio/mpeg";
      serverAudioFileControl.disabled = false;

      // server control - by stream
      var serverAudioStreamControl = document.getElementById('serverAudioStream');
      const streamQueryString = `region=${resourceRegion}&key=${resourceKey}&phrase=${phrase}`;
      serverAudioStreamControl.src = `${serverSrc}?${streamQueryString}`;
      console.log(serverAudioStreamControl.src)
      serverAudioStreamControl.type = "audio/mpeg";
      serverAudioStreamControl.disabled = false;

      // client control
      var clientAudioAzureControl = document.getElementById('clientAudioAzure');
      clientAudioAzureControl.disabled = false;

    }

    function DisplayError(error) {
      window.alert(JSON.stringify(error));
    }

    // Client-side request directly to Azure Cognitive Services
    function getSpeechFromAzure() {

      // authorization for Speech service
      var speechConfig = SpeechSDK.SpeechConfig.fromSubscription(resourceKey, resourceRegion);

      // new Speech object
      synthesizer = new SpeechSDK.SpeechSynthesizer(speechConfig);

      synthesizer.speakTextAsync(
        phrase,
        function (result) {

          // Success function

          // display status
          if (result.reason === SpeechSDK.ResultReason.SynthesizingAudioCompleted) {

            // load client-side audio control from Azure response
            audioElement = document.getElementById("clientAudioAzure");
            const blob = new Blob([result.audioData], { type: "audio/mpeg" });
            const url = window.URL.createObjectURL(blob);

          } else if (result.reason === SpeechSDK.ResultReason.Canceled) {
            // display Error
            throw (result.errorDetails);
          }

          // clean up
          synthesizer.close();
          synthesizer = undefined;
        },
        function (err) {

          // Error function
          throw (err);
          audioElement = document.getElementById("audioControl");
          audioElement.disabled = true;

          // clean up
          synthesizer.close();
          synthesizer = undefined;
        });

    }

    // Initialization
    document.addEventListener("DOMContentLoaded", function () {

      var clientAudioAzureControl = document.getElementById("clientAudioAzure");
      var resultDiv = document.getElementById("resultDiv");

      resourceKey = document.getElementById('resourceKey').value;
      resourceRegion = document.getElementById('resourceRegion').value;
      phrase = document.getElementById('phraseDiv').value;
      if (!!window.SpeechSDK) {
        SpeechSDK = window.SpeechSDK;
        clientAudioAzure.disabled = false;

        document.getElementById('content').style.display = 'block';
      }
    });

  </script>
</body>

</html>

Выделенные строки в файле:

  • Строка 74. Пакет SDK службы "Речь Azure" извлекается в клиентную библиотеку, используя cdn.jsdelivr.net сайт для доставки пакета NPM.
  • Строка 102. Метод updateSrc обновляет URL-адрес элементов управления src звуком с помощью строки запроса, включая ключ, регион и текст.
  • Строка 137. Если пользователь выбирает Get directly from Azure кнопку, веб-страница вызывается непосредственно в Azure с клиентской страницы и обрабатывает результат.

Создание ресурса службы "Речь" из Cognitive Services

Создайте ресурс службы "Речь" с помощью команд Azure CLI в Azure Cloud Shell.

  1. Войдите в Azure Cloud Shell. Для этого необходимо пройти аутентификацию в браузере с учетной записью, имеющей разрешение на действительную подписку Azure.

  2. Создайте группу ресурсов для ресурса службы "Речь".

    az group create \
        --location eastus \
        --name tutorial-resource-group-eastus
    
  3. Создайте ресурс службы "Речь" в группе ресурсов.

    az cognitiveservices account create \
        --kind SpeechServices \
        --location eastus \
        --name tutorial-speech \
        --resource-group tutorial-resource-group-eastus \
        --sku F0
    

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

  4. Используйте команду, чтобы получить значения ключей для нового ресурса службы "Речь".

    az cognitiveservices account keys list \
        --name tutorial-speech \
        --resource-group tutorial-resource-group-eastus \
        --output table
    
  5. Скопируйте один из ключей.

    Ключ можно использовать, вставив его в веб-форму приложения Express для аутентификации в службе Azure "Речь".

Запуск приложения Express.js для преобразования текста в речь

  1. Запустите приложение с помощью следующей команды Bash.

    npm start
    
  2. Откройте веб-приложение в браузере.

    http://localhost:3000    
    
  3. Вставьте свой ключ службы "Речь" в выделенное текстовое поле.

    Browser screenshot of web form with Speech key input field highlighted.

  4. При необходимости измените текст на другой.

  5. Чтобы начать преобразование в аудиоформат, нажмите одну из трех кнопок:

    • Получить напрямую из Azure — используется вызов на стороне клиента в Azure.
    • Элемент управления аудио для получения аудио из файла.
    • Элемент управления аудио для получения аудио из буфера.

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

Создание Службы приложений Azure в Visual Studio Code

  1. В палитре команд (CTRL+SHIFT+P) введите "Создать интернет" и выберите службу приложение Azure: создать веб-приложение... Дополнительно. Используйте расширенные параметры для полного управления развертыванием, в том числе группой ресурсов, планом Службы приложений и операционной системой, чтобы не использовать значения по умолчанию для Linux.

  2. Ответьте на запросы следующим образом:

    • Выберите учетную запись своей подписки.
    • В поле Введите глобально уникальное имя (например, my-text-to-speech-app):
      • введите имя, уникальное в пределах Azure. Используйте только буквенно-цифровые символы (A–Z, a–z и 0–9) и дефисы (-).
    • Выберите tutorial-resource-group-eastus для группы ресурсов.
    • Выберите стек среды выполнения для версии, которая включает Node и LTS.
    • Выберите операционную систему Linux.
    • Выберите Создать план службы приложений и укажите имя, например my-text-to-speech-app-plan.
    • Выберите F1 в бесплатной ценовой категории. Если в вашей подписке уже есть бесплатное веб-приложение, выберите уровень Basic.
    • Выберите Пропустить для ресурса Application Insights.
    • Выберите расположение eastus.
  3. Через некоторое время в Visual Studio Code появится уведомление о завершении создания. Закройте уведомление с помощью кнопки X.

Развертывание локального приложения Express.js в удаленной Службе приложений в Visual Studio Code

  1. Так как веб-приложение уже готово, разверните код с локального компьютера. Щелкните значок Azure, чтобы открыть обозреватель Службы приложений Azure, разверните узел подписки, щелкните правой кнопкой мыши имя только что созданного веб-приложения и выберите Развернуть в веб-приложении.

  2. При появлении запросов при разработке выберите корневую папку приложения Express.js, снова выберите свою учетную запись в подписке, а затем выберите имя созданного веб-приложения (my-text-to-speech-app).

  3. При появлении запроса на выполнение npm install нажмите Да, когда появится запрос на обновление конфигурации, чтобы выполнить npm install на целевом сервере.

    Prompt to update configuration on the target Linux server

  4. По завершении развертывания щелкните Обзор веб-сайта в диалоговом окне с предложением просмотреть только что развернутое веб-приложение.

  5. (Необязательно): вы можете внести изменения в файлы кода, а затем использовать развертывание в веб-приложении в расширении службы приложение Azure для обновления веб-приложения.

Потоковая передача журналов удаленной службы в Visual Studio Code

Просматривайте любые выходные данные или выводите последний фрагмент данных, которые выполняющееся приложение создает в результате вызовов к console.log. Эти выходные данные отображаются в окне Вывод в Visual Studio Code.

  1. В обозревателе Службы приложений Azure щелкните узел нового приложения правой кнопкой мыши и выберите Начать потоковую передачу журналов.

     Starting Live Log Stream ---
     
  2. Несколько раз обновите веб-страницу в браузере и убедитесь, что отображаются дополнительные выходные данные журнала.

Удаление ресурсов путем удаления группы ресурсов

Завершив работу с этим руководством, удалите группу ресурсов с ресурсом, чтобы с вас не взималась плата за потребление.

Чтобы удалить группу ресурсов, в Azure Cloud Shell выполните команду Azure CLI:

az group delete --name tutorial-resource-group-eastus  -y

Эта команда может выполняться несколько минут.

Следующие шаги