0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

VOICEVOX と BOCCO emo の API(+ FFmpeg)で音声合成・音声メッセージ送信【Node.js:3】

Posted at

(この記事は Node.js の Advent Calendar 2025 の記事【3つ目】にする予定です)

はじめに

この記事は、Node.js で VOICEVOX と BOCCO emo の API を組み合わせて使ってみた話です(それと FFmpeg も組み合わせています)。

以下の動画のように「VOICEVOX で合成した音声で BOCCO emo がしゃべる」ということができます。

実際に試していく

はじめに、全体の処理の概要を書きます。

主な処理の流れは、
「VOICEVOX での音声合成」⇒「WAVファイルを MP3ファイルに変換(FFmpegを利用)」⇒「BOCCO emo の API で MP3ファイルの再生」
となります。

処理の流れに関する補足

少し、処理の流れについて補足します。

VOICEVOX での音声合成と FFmpeg を用いた変換

VOICEVOX で音声合成をすると、その結果は WAVファイルで得られます。そのデータは、直接 BOCCO emo の API では使えません。以下の BOCCO emo の API の公式ドキュメントに書かれているとおり、利用可能なフォーマットが MP3・M4A であるためです。

2025-11-16_12-35-09.jpg

そのため、FFmpeg を Node.js の処理の中で呼び出して、WAVファイルを MP3ファイルに変換します。その部分の処理については、直近で書いた以下の記事のとおりです。

●Node.js を使った「VOICEVOX での音声合成 + FFmpeg による MP3変換」の処理 - Qiita
 https://qiita.com/youtoy/items/3d4eef42ac48c16dc251

BOCCO emo の API を使った音声メッセージ送信

その後は、変換後の MP3ファイルを、BOCCO emo の API を使って BOCCO emo に送ります。前に、以下の記事に書いた「音声メッセージ送信」を使います。

●BOCCO emo の SDK(Node.js版)で API を使った「音声メッセージ送信」を試す - Qiita
 https://qiita.com/youtoy/items/77f2545cb6cb9c49b9a8

今回用いたコードなど

実際に試してみます。下準備として、以下のコマンドでパッケージを 1つインストールします。

npm install @ux-xu/emo-platform-api-nodejs

コードの内容

また、今回用いたコードは以下のとおりです。

上で過去に書いた 2つの記事を紹介しましたが、その内容を組み合わせたようなものになります。

import fs from "node:fs";
import { exec } from "node:child_process";
import { promisify } from "node:util";
import { EmoApiClient } from "@ux-xu/emo-platform-api-nodejs";

process.loadEnvFile("./development.env");

const BASE_URL = "http://127.0.0.1:50021";
const execAsync = promisify(exec);

async function synthesizeSpeechToMp3(
  text,
  { speaker = 3, wavPath = "output.wav", mp3Path = "output.mp3" } = {}
) {
  console.log("クエリを作成中");
  const queryResponse = await fetch(
    `${BASE_URL}/audio_query?text=${encodeURIComponent(
      text
    )}&speaker=${speaker}`,
    { method: "POST" }
  );

  if (!queryResponse.ok) {
    throw new Error(`クエリ作成失敗: ${queryResponse.status}`);
  }

  const audioQuery = await queryResponse.json();

  console.log("音声合成を実行中");
  const synthesisResponse = await fetch(
    `${BASE_URL}/synthesis?speaker=${speaker}`,
    {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify(audioQuery),
    }
  );

  if (!synthesisResponse.ok) {
    throw new Error(`音声合成失敗: ${synthesisResponse.status}`);
  }

  const audioBuffer = await synthesisResponse.arrayBuffer();
  fs.writeFileSync(wavPath, Buffer.from(audioBuffer));
  console.log(`WAV を保存しました: ${wavPath}`);

  console.log("mp3 に変換中");
  const cmd = `ffmpeg -y -i "${wavPath}" "${mp3Path}"`;
  await execAsync(cmd);
  console.log(`MP3 を保存しました: ${mp3Path}`);

  return mp3Path;
}

async function sendMp3ToBoccoEmo(mp3Path) {
  const ACCESS_TOKEN = process.env.EMO_ACCESS_TOKEN;
  const REFRESH_TOKEN = process.env.EMO_REFRESH_TOKEN;
  const ROOM_UUID = process.env.EMO_ROOM_UUID;

  if (!ACCESS_TOKEN || !REFRESH_TOKEN || !ROOM_UUID) {
    throw new Error(
      "EMO_ACCESS_TOKEN / EMO_REFRESH_TOKEN / EMO_ROOM_UUID が設定されていません"
    );
  }

  const api = new EmoApiClient({
    accessToken: ACCESS_TOKEN,
    refreshToken: REFRESH_TOKEN,
  });

  console.log(`Bocco emo に送信中: ${mp3Path}`);
  const audio = fs.readFileSync(mp3Path);
  const res = await api.postAudioMessage(ROOM_UUID, { audio });

  console.log("送信結果:", res);
}

async function main() {
  const text = "これはVOICEVOXで作った音声です。";
  const wavPath = "bocco_emo.wav";
  const mp3Path = "bocco_emo.mp3";

  const createdMp3Path = await synthesizeSpeechToMp3(text, {
    speaker: 3,
    wavPath,
    mp3Path,
  });

  await sendMp3ToBoccoEmo(createdMp3Path);
}

main().catch((err) => {
  console.error("エラーが発生しました:", err?.message || err);
  console.error("詳細:", err?.response?.status, err?.response?.data);
});

この処理を実行することで、冒頭の動画のような内容を実現できました。

0
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?