生成したオーディオをTwilio経由で送信する

Twilioを使って生成した音声を電話に組み込む方法を学びます。

ハウツーガイド · ElevenAPI クイックスタートを完了し、Twilioアカウントをお持ちであることを前提としています。

このガイドでは、TwilioとElevenLabsを使って、AI生成メッセージを電話で送信する方法を説明します。このプロセスにより、高品質な音声メッセージを発信者に直接届けられます。

Twilioとngrokのアカウントを作成する

このガイドではTwilioとngrokを使用するため、それぞれのアカウントを作成してください。

Expressでサーバーを作成する

プロジェクトを初期化する

プロジェクト用の新しいフォルダを作成します。

mkdir elevenlabs-twilio
cd elevenlabs-twilio
npm init -y

依存関係をインストールする

npm install @elevenlabs/elevenlabs-js express express-ws twilio

開発用依存関係をインストールする

npm i @types/node @types/express @types/express-ws @types/ws dotenv tsx typescript

ファイルを作成する

// src/app.ts
import { ElevenLabsClient } from "@elevenlabs/elevenlabs-js";
import "dotenv/config";
import express, { Response } from "express";
import ExpressWs from "express-ws";
import { Readable } from "stream";
import VoiceResponse from "twilio/lib/twiml/VoiceResponse";
import { type WebSocket } from "ws";
const app = ExpressWs(express()).app;
const PORT: number = parseInt(process.env.PORT || "5000");
const elevenlabs = new ElevenLabsClient();
const voiceId = "aMSt68OGf4xUZAnLpTU8";
const outputFormat = "ulaw_8000";
const text = "This is a test. You can now hang up. Thank you.";
function startApp() {
app.post("/call/incoming", (_, res: Response) => {
const twiml = new VoiceResponse();
twiml.connect().stream({
url: `wss://${process.env.SERVER_DOMAIN}/call/connection`,
});
res.writeHead(200, { "Content-Type": "text/xml" });
res.end(twiml.toString());
});
app.ws("/call/connection", (ws: WebSocket) => {
ws.on("message", async (data: string) => {
const message: {
event: string;
start?: { streamSid: string; callSid: string };
} = JSON.parse(data);
if (message.event === "start" && message.start) {
const streamSid = message.start.streamSid;
const response = await elevenlabs.textToSpeech.convert(voiceId, {
modelId: "eleven_flash_v2_5",
outputFormat: outputFormat,
text,
});
const readableStream = Readable.from(response);
const audioArrayBuffer = await streamToArrayBuffer(readableStream);
ws.send(
JSON.stringify({
streamSid,
event: "media",
media: {
payload: Buffer.from(audioArrayBuffer as any).toString("base64"),
},
})
);
}
});
ws.on("error", console.error);
});
app.listen(PORT, () => {
console.log(`Local: http://localhost:${PORT}`);
console.log(`Remote: https://${process.env.SERVER_DOMAIN}`);
});
}
function streamToArrayBuffer(readableStream: Readable) {
return new Promise((resolve, reject) => {
const chunks: Buffer[] = [];
readableStream.on("data", (chunk) => {
chunks.push(chunk);
});
readableStream.on("end", () => {
resolve(Buffer.concat(chunks).buffer);
});
readableStream.on("error", reject);
});
}
startApp();
# .env
SERVER_DOMAIN=
ELEVENLABS_API_KEY=

コードを理解する

着信を処理する

番号に電話がかかると、Twilioは/call/incomingのエンドポイントにPOSTリクエストを送信します。 その後、urlに/call/connectionエンドポイントを設定して、WebSocket経由で通話を処理することをtwiml.connectでTwilioに伝えます。

function startApp() {
app.post('/call/incoming', (_, res: Response) => {
const twiml = new VoiceResponse();
twiml.connect().stream({
url: `wss://${process.env.SERVER_DOMAIN}/call/connection`,
});
res.writeHead(200, { 'Content-Type': 'text/xml' });
res.end(twiml.toString());
});

テキスト読み上げを作成する

ここでは、TwilioがWebSocketエンドポイントに送信するメッセージを監視します。startメッセージイベントを受信すると、ElevenLabsのTypeScript SDKを使用してオーディオを生成します。

app.ws('/call/connection', (ws: WebSocket) => {
ws.on('message', async (data: string) => {
const message: {
event: string;
start?: { streamSid: string; callSid: string };
} = JSON.parse(data);
if (message.event === 'start' && message.start) {
const streamSid = message.start.streamSid;
const response = await elevenlabs.textToSpeech.convert(voiceId, {
modelId: 'eleven_flash_v2_5',
outputFormat: outputFormat,
text,
});

メッセージを送信する

ElevenLabsからオーディオを受信したら、配列バッファに変換し、WebSocket経由でTwilioに送信します。

const readableStream = Readable.from(response);
const audioArrayBuffer = await streamToArrayBuffer(readableStream);
ws.send(
JSON.stringify({
streamSid,
event: "media",
media: {
payload: Buffer.from(audioArrayBuffer as any).toString("base64"),
},
})
);

ngrokをアプリケーションに接続する

Twilioには公開アクセス可能なURLが必要です。ngrokを使ってアプリケーションのローカルポートを転送し、公開URLとして公開します。

ターミナルで次のコマンドを実行します。

ngrok http 5000

ngrokドメイン(https://を除く)をコピーして、環境変数で使用します。

環境変数を更新する

.envファイルにngrokドメインとElevenLabs APIキーを設定します。

# .env
SERVER_DOMAIN=*******.ngrok.app
ELEVENLABS_API_KEY=*************************

アプリケーションを起動する

次のコマンドを実行してアプリを起動します。

npm run dev

Twilioを設定する

Twilioのガイドに従って新しい番号を作成します。番号を作成したら、Phone Numbers -> Manage -> Active numbersの「Configure」タブに移動します。

「A call comes in」セクションで、アプリケーションの完全なURLを入力します(/call/incomingパスを必ず追加してください)。

例:https://***ngrok.app/call/incoming

電話をかける

作成した番号に電話をかけます。ElevenLabsの音声によるメッセージが聞こえるはずです。

本番環境へのデプロイのヒント

本番環境でアプリケーションを実行する場合は、SERVER_DOMAIN環境変数をサーバーのドメインに設定してください。TwilioのURLも本番サーバーを指すように更新してください。

まとめ

これで、TwilioとElevenLabs音声の統合について基本的な理解が得られました。さらに質問がある場合や、このブログ記事の改善案がある場合は、下の「Suggest edits」または「Raise issue」ボタンからお知らせください。