📑

VOICEVOX エンジンを Docker で立ち上げて、音声をダウンロードする

2024/12/29に公開

はじめに

VOICEVOX は、商用・非商用問わず無料で利用できるテキスト読み上げソフトです。VOICEVOX エンジンを Docker で簡単に立ち上げ、TypeScript を使って音声ファイルをダウンロードする方法を紹介します。

Docker Compose で VOICEVOX を起動する

まず、以下の docker-compose.yml ファイルを準備します。

docker-compose.yml
version: '3.8'
services:
  voicevox_engine:
    image: voicevox/voicevox_engine:cpu-ubuntu20.04-latest
    ports:
      - '50021:50021'
    restart: unless-stopped

次に、以下のコマンドを実行して VOICEVOX エンジンを起動します。

docker compose up

これで、VOICEVOX エンジンが localhost:50021 で起動します。

TypeScript で音声をダウンロードする

bun init で作成したプロジェクトで動作させることを想定しています。以下の TypeScript コードを使用して、指定したテキストを音声ファイルとしてダウンロードします。

import * as fs from 'node:fs/promises';
import { resolve } from 'node:path';

const VOICEVOX_URL = 'http://localhost:50021';

const logSuccess = (message: string) => console.log(`${message}`);
const logError = (message: string, error: unknown) => console.error(`${message}:`, error);

const createAudioQuery = async (text: string, speaker: number) => {
  const queryResponse = await fetch(
    `${VOICEVOX_URL}/audio_query?speaker=${speaker}&text=${encodeURIComponent(text)}`,
    {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
      },
    }
  );

  if (!queryResponse.ok) {
    throw new Error(`Failed to create audio query: ${queryResponse.statusText}`);
  }

  return queryResponse.json();
};

const synthesizeAudio = async (query: any, speaker: number) => {
  const synthesisResponse = await fetch(
    `${VOICEVOX_URL}/synthesis?speaker=${speaker}`,
    {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
      },
      body: JSON.stringify(query),
    }
  );

  if (!synthesisResponse.ok) {
    throw new Error(`Failed to synthesize audio: ${synthesisResponse.statusText}`);
  }

  return synthesisResponse.arrayBuffer();
};

const saveAudioToFile = async (arrayBuffer: ArrayBuffer, outputPath: string) => {
  const buffer = Buffer.from(arrayBuffer);
  const absoluteOutputPath = resolve(outputPath);
  await fs.writeFile(absoluteOutputPath, new Uint8Array(buffer));
  return absoluteOutputPath;
};

const downloadAudio = async (text: string, speaker: number, outputPath: string) => {
  try {
    const query = await createAudioQuery(text, speaker);
    const arrayBuffer = await synthesizeAudio(query, speaker);
    const absoluteOutputPath = await saveAudioToFile(arrayBuffer, outputPath);
    logSuccess(`音声ファイルをダウンロードしました: ${absoluteOutputPath}`);
  } catch (e) {
    logError('音声ファイルのダウンロード中にエラーが発生しました', e);
  }
};

const ensureOutputDirExists = async (outputDir: string) => {
  try {
    await fs.mkdir(outputDir, { recursive: true });
  } catch (e) {
    logError(`出力フォルダ (${outputDir}) の作成に失敗しました`, e);
    process.exit(1);
  }
};

const main = async () => {
  const outputDir = 'output';
  const filename = 'long_voice.wav';
  const outputPath = resolve(outputDir, filename);

  await ensureOutputDirExists(outputDir);

  const longText = `
    皆さん、こんにちは!今日も元気に動画を見てくれてありがとうございます。
    今日はですね、最近私がハマっている趣味について、ちょっとお話してみようかなと思っています。
    それは何かと言いますと、実は最近、自宅でコーヒーを焙煎することに挑戦しているんです。
  `;

  await downloadAudio(longText, 1, outputPath); // スピーカーID 1 で音声をダウンロード
};

main();

このコードを実行すると、指定したテキストが音声ファイルとして output/long_voice.wav に保存されます。

スピーカーIDについて

VOICEVOX では、複数のキャラクター(スピーカー)が用意されています。スピーカーIDを変更することで、異なるキャラクターの音声を生成できます。例えば、スピーカーID 1 は「ずんだもん」に対応しています。
キャラクターの id は API ドキュメントの /speakers を実行すると取得することができます。

http://localhost:50021/docs#/%E3%81%9D%E3%81%AE%E4%BB%96/speakers_speakers_get

公式ドキュメント

詳細な情報や最新のアップデートについては、以下の公式ドキュメントを参照してください。

VOICEVOX ENGINE

その他の音声読み上げ

今後検証したい音声読み上げツールは以下です。

Discussion