TwilioにGoogle Could Text-to-Speechで話してもらうには?(Node.js)

Twilioが提供するProgrammable VoiceとGoogle Cloud Text to Speech APIを使用し着信応答を任意の音声で行う方法をご紹介します。
2019.12.17

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

Twilioが提供するProgrammable Voiceを利用すると、電話の自動応答や発信をアプリケーションから制御することができます。 着信に応答する場合は、Twimlと呼ばれるマークアップ言語を用い、言語や音声を設定することができます。 このProgrammable Voiceで、2018年から標準の音声合成エンジン以外にもAmazon Pollyが使えるようになりました。

【Amazon Polly】 Twilio さんに Mizuki さんの声で話してもらいました!

Amazon Pollyは標準の音声に比べてとても滑らかなのですが、他の音声をリアルタイムで利用したいという声もよく聞きます。 今回は、このエントリではGoogleが提供するCloud Text-to-Speechを音声合成エンジンとして利用する方法をご紹介します。基本的には他のエンジンにも適用可能です。

準備しておくもの

  • Node.jsおよびExpress.js、body-parser
  • Twilioアカウント
  • Twilioから提供される電話番号
  • Google Cloud Text-to-Speech APIの有効化、Node.js用のクライアントライブラリのインストール

プロジェクトはNode.jsならびにExpress.js、body-parserを利用します。セットアップがめんどくさい!という場合は、 こちらのリポジトリを利用できます。

Github - Neri78: Twilio-Voice-GCP-tts

app.js

const express = require('express');
const bodyParser = require('body-parser');
const fs = require('fs');
const util = require('util');
const path = require('path');

// Google Cloud client library
const textToSpeech = require('@google-cloud/text-to-speech');
// Twilio VoiceResponse
const VoiceResponse = require('twilio').twiml.VoiceResponse;

const app = express();
app.use(bodyParser.urlencoded({ extended: true }));
// 生成したファイルを保存しておくフォルダ
app.use(express.static(
    path.join(__dirname, 'mp3を保存するフォルダ')));

Twilioアカウントのサインアップと電話番号の取得方法についてはこちらで確認できます。

日本の番号を取得する場合は、アカウントを作成後、下記手順の 1-4.日本の番号を新しく取得 に沿って取得できます。

Google Cloud Text-to-Speech APIを有効化させる場合は、クイックスタート: クライアント ライブラリの使用始める前に の手順に沿ってサービスの設定を終えておきます。サービスアカウントの情報を記載したjsonファイル (service-account-file.json)はこの後で使用するため、ローカルに保存しておき、ファイルのパスを控えておきます。(例: /Users/dikehara/Downloads/service-account-file.json) さらに準備したNode.jsプロジェクトにクライアントライブラリをインストールします。

着信時の応答を返すAPIを実装

Twilioで取得した電話に着信があった際に呼び出す関数を実装します。GCPのクライアントで非同期メソッドを使用するためasyncキーワードを指定します。

// Twilioから着信リクエストを受け取るAPI
app.post('/incoming', async (req, res) => {
 
});

この関数の内部でTextToSpeechClientクライアントを初期化し、音声合成を行うテキストとリクエストを作成します。 着信ごとにカスタマイズする感じを出すために着信番号をテキストに追加しています。

    // クライアントを初期化
    const client = new textToSpeech.TextToSpeechClient();

    // 音声合成するテキストを設定
    const text = 'お電話ありがとうございます。あなたの電話番号は、' +
        req.body.From + 'です。';

    // 音声合成のリクエストを作成
    const request = {
        input: {text: text},
        // 言語およびSSMLを設定
        voice: {languageCode: 'ja-JP', ssmlGender: 'FEMALE'},
        // 生成した音声のエンコーディングをmp3に設定
        audioConfig: {audioEncoding: 'MP3'},
    };

作成したリクエストを送信し、返ってきたオーディオデータをファイルとして保存します。

    // 音声合成を開始
    const [response] = await client.synthesizeSpeech(request);

    //現在のCallSidから出力ファイル名を生成
    let outputFileName = req.body.CallSid + '.mp3';

    // mp3ファイルを保存
    const writeFile = util.promisify(fs.writeFile);
    await writeFile('mp3を保存するフォルダ/' + outputFileName, 
                    response.audioContent, 'binary');

着信に応答する TwiML を生成しレスポンスとして返します。ここでは Play句を使用し、mp3のファイルの場所を指定します。もし、音声合成APIにUrlだけでリクエストを送れる場合はファイルとして保存する必要はなく、直接APIのUrlを指定できます。

    // TwiMLを初期化
    const twiml = new VoiceResponse();
    twiml.say({
        language: 'ja-JP',
        voice: 'Polly.Mizuki'
    }, "テストです。");
    // 生成した音声ファイルを再生させる。
    // ローカルホストで試す場合は、ngrok()などのツールを使用し、生成されたurlを指定してください。
    twiml.play('このプロジェクトをホストするドメイン名(https://example.com/)' + outputFileName);

    //ヘッダを設定
    res.writeHead(200, {'Content-Type': 'text/xml'});
    res.end(twiml.toString());

コンソールで着信応答のWebhookとして設定

Twilioコンソールで取得した電話番号の設定画面を表示し、作成したNode.jsアプリケーションのUrlを指定します。

Twilioコンソール

環境変数を設定しターミナルからアプリケーションを起動

ターミナルからアプリケーションを起動させる前に、先ほどダウンロードしたGoogle Cloud Text to Speech APIを使用する際に必要な情報が保存されているサービスアカウントのファイルパスをGOOGLE_APPLICATION_CREDENTIALSという変数名で通しておきます。

Macの場合

export GOOGLE_APPLICATION_CREDENTIALS="[PATH]"

Windowsの場合

set GOOGLE_APPLICATION_CREDENTIALS=[PATH]

この設定はターミナルを閉じてしまうと失われるため、システム・ユーザーの環境変数として登録しない場合は、毎回実行する必要があります。

後はアプリを起動し、Twilioの電話番号にかけてみましょう!

ここからはGithubサンプルの実行方法です。アプリケーションを外部からアクセスできるドメインでホスティングする場合は、TwiML.PlayのURLをホストしているドメインに変更します。

https://example.comにホストする場合

    twiml.play('https://example.com/' + outputFileName);

サンプルをローカルホストで実行する場合は、ローカルホストのポート3000番に外部からアクセスできるようにします。 その場合は、ngrokなどのツールが利用できます。 ngrokをインストール後、別のターミナルを開き、下記のコマンドを実行します。

ngrok http 3000

ngrok

この例ではhttps://2c2a973d.ngrok.ioが表示されるため、twiml.playのurlを次のように設定します。

ngrokを利用する場合

    twiml.play('https://2c2a973d.ngrok.io/' + outputFileName);

最後にapp.jsを起動します。

node app.js

実行してみると...電話番号を数字として解釈してしまっているので少し変ですが、動的にリアルタイムで 合成した音声を応答として利用することができました。

まとめ

今回は、Twilioに標準で提供されている音声エンジン以外を利用しリアルタイムで応答オーディオを作成・使用する方法を試してみました。いろいろな妄想可能性が広がりますね! さて、このままでは作成した音声ファイルが残り続けるため、セキュリティ的にも、ストレージ的にも良くありません。 こちらについてはまた次回にでも。

Github - Neri78: Twilio-Voice-GCP-tts