TwilioにGoogle Could Text-to-Speechで話してもらうには?(Node.js)
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
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を指定します。
環境変数を設定しターミナルからアプリケーションを起動
ターミナルからアプリケーションを起動させる前に、先ほどダウンロードしたGoogle Cloud Text to Speech APIを使用する際に必要な情報が保存されているサービスアカウントのファイルパスをGOOGLE_APPLICATION_CREDENTIALS
という変数名で通しておきます。
export GOOGLE_APPLICATION_CREDENTIALS="[PATH]"
set GOOGLE_APPLICATION_CREDENTIALS=[PATH]
この設定はターミナルを閉じてしまうと失われるため、システム・ユーザーの環境変数として登録しない場合は、毎回実行する必要があります。
後はアプリを起動し、Twilioの電話番号にかけてみましょう!
ここからはGithubサンプルの実行方法です。アプリケーションを外部からアクセスできるドメインでホスティングする場合は、TwiML.PlayのURLをホストしているドメインに変更します。
twiml.play('https://example.com/' + outputFileName);
サンプルをローカルホストで実行する場合は、ローカルホストのポート3000番に外部からアクセスできるようにします。 その場合は、ngrokなどのツールが利用できます。 ngrokをインストール後、別のターミナルを開き、下記のコマンドを実行します。
ngrok http 3000
この例ではhttps://2c2a973d.ngrok.io
が表示されるため、twiml.playのurlを次のように設定します。
twiml.play('https://2c2a973d.ngrok.io/' + outputFileName);
最後にapp.jsを起動します。
node app.js
実行してみると...電話番号を数字として解釈してしまっているので少し変ですが、動的にリアルタイムで 合成した音声を応答として利用することができました。
まとめ
今回は、Twilioに標準で提供されている音声エンジン以外を利用しリアルタイムで応答オーディオを作成・使用する方法を試してみました。いろいろな妄想可能性が広がりますね!
さて、このままでは作成した音声ファイルが残り続けるため、セキュリティ的にも、ストレージ的にも良くありません。
こちらについてはまた次回にでも。