はじめに
VOICEVOXという「無料で使える中品質なテキスト読み上げ・歌声合成ソフトウェア」があります。これで「中品質かー」と思うくらいよくできていて、ちょっと自作アプリに組み込んだり、Web 上で喋らせたり、コマンドラインから起動できたら楽しそうです。
配布するのもライセンス的にかなり緩いので問題ないのですが、環境(CPU/OS)やファイルサイズ(合計 1GB くらいある)的に苦労するところがあります。ちょっと遊びに使いたいだけなんだけどなー、というときにはやはり Lambda みたいなところで WebAPI として使えると便利です。
そこでこの VOICEVOX(Core) を Lambda で動くようにした
https://github.com/mokemokechicken/lambda-vvcore
というのを作ったので共有します。
作ったもの
何ができるのか
Lambda としてデプロイすると、HTTP リクエストを受け取って VOICEVOX で音声を生成し、その Wav データを base64 エンコードして返す API ができます。
「テキスト」「speakerId」を指定すると、そのテキストを指定した話者の声で読み上げた音声データを返します。
使い方例 1: ブラウザの JavaScript から
synthesizeAndPlayVoice("今日は台風ですね", 0);
synthesizeAndPlayVoiceの中身
const LAMBDA_ENDPOINT = "https://YOUR-LAMBDA-URL.lambda-url.ap-northeast-1.on.aws/";
const LAMBDA_APIKEY = "YOUR-LAMBDA-APIKEY";
async function synthesizeAndPlayVoice(text, speakerId) {
const requestBody = JSON.stringify({
text: text,
speaker_id: speakerId,
});
try {
// Lambda関数を呼び出し
const response = await fetch(LAMBDA_ENDPOINT, {
method: "POST",
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${LAMBDA_APIKEY}`,
},
body: requestBody,
});
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const data = await response.json();
// Base64エンコードされたWAVデータをデコード
const wavData = atob(data.wav);
// WAVデータをUint8Arrayに変換
const wavArray = new Uint8Array(wavData.length);
for (let i = 0; i < wavData.length; i++) {
wavArray[i] = wavData.charCodeAt(i);
}
// AudioContextを作成
const audioContext = new (window.AudioContext || window.webkitAudioContext)();
// WAVデータをデコード
const audioBuffer = await audioContext.decodeAudioData(wavArray.buffer);
// 音源を作成
const source = audioContext.createBufferSource();
source.buffer = audioBuffer;
// 出力に接続
source.connect(audioContext.destination);
// 再生開始
source.start();
console.log("音声の再生を開始しました。");
} catch (error) {
console.error("エラーが発生しました:", error);
}
}
使い方例 2: コマンドラインから
こういう感じで使えます。
export LAMBDA_ENDPOINT="https://YOUR-LAMBDA-URL.lambda-url.ap-northeast-1.on.aws/";
export LAMBDA_APIKEY="YOUR-LAMBDA-APIKEY";
TEXT="ボイスボックスへ、ようこそ!"
SPEAKER=0
JSON='{"text":"'$TEXT'","speaker_id":'$SPEAKER'}'
curl -s -XPOST "$LAMBDA_ENDPOINT" -H "Content-Type: application/json" -d "$JSON" -H "Authorization: Bearer $LAMBDA_APIKEY" | jq -r '.wav' | base64 -d > voice.wav
# play voice.wav
デプロイ方法
https://github.com/mokemokechicken/lambda-vvcore に書いてある通りですが、ざっくり以下の手順です。
- AWSCDKv2 が使える環境を用意します。AWS のアカウントや認証情報は設定済みであることを前提とします。
- このリポジトリを clone して、
lambda-vvcore
ディレクトリに移動します。 -
lambda-vvcore
ディレクトリでnpm install
します。 - 環境変数を設定して、
npx cdk deploy
します。 - デプロイが完了したら、出力された URL と API キーを使って API を呼び出します。
処理速度 や 料金 について
処理速度
- 話者Model の Load: 約 3 秒
- Lambda は再利用されるので、Speaker 毎の最初のリクエストのみ
- でも、Lambda はリクエストがない場合数分で終了するので、再度リクエストが来たときには再度 Load が走る
- Lambda は再利用されるので、Speaker 毎の最初のリクエストのみ
- 音声合成: 「ボイスボックスをエーダブリューエス ラムダで動かす」 で 約 2.2 秒
つまり およそ 初回5.2秒、2回目なら2.2秒 という感じ。
料金
この Lambda の場合、メモリサイズを 10240MB(最大) に設定しています。これが 1 秒動くと約 0.020 円(1 ドル 150 円換算)かかります。
※ 東京リージョン、Arm、10240MB の 1 ミリ秒あたりの料金: 0.0000001333USD
(2024 年 8 月現在) ※
※ リクエスト料金は相当低いので無視します。
AWS Lambda は メモリサイズを上げると処理速度が上がるらしいです(ついでに料金も上がります)。
メモリサイズを落とすと、顕著に処理速度が遅くなります。
あまり細かく計測はしていませんが、同じくらいの価格になるような感じがあるので、処理速度が速くなる 10240MB に設定しています(が好みで変えてください)。
実際のメモリ消費量は 1 モデルだと 300MB ~ 600MB くらいです。
その他
中身の構成
- Rust で vvcore というライブラリを経由して VOICEVOX(Core) を呼び出しています
- Docker コンテナ内に VOICEVOX(Core) の 共有ライブラリや モデルデータ等を入れて、それを Lambda で動かしています
- VOICEVOX CORE は 0.15.4 を使っています。0.14系だと「初期化にとても時間がかかる(
load_all_models=false
にしても全ModelをLoadしている?)」
メモリサイズとスレッド数
VVCore の方で Lambda の CPU 数を動的に取得できないようなので(まあそうか)、固定値で thread 数を設定しています。
10240MB では vCPU が 6 らしいので、6 スレッドにしています。
(よく見るとこの話では 8896MB から 6 スレッドらしいので、8896MB にしてもいいかもしれないな...)
さいごに
新しいおもちゃができて楽しいです。開発者の方々、ありがとうございます。
結局 1 リクエストで 0.05 ~ 0.1 円くらいかかるので、まあちょっとした用途に使う感じかなと思います。
英語などの読み上げは得意ではないので、gpt-4o-mini とかで 平仮名に変換してから読み上げるオプションを追加するとか、そういうのも面白いかもしれませんね。