作業環境・自動化

AivisSpeechをMacにインストールしてPythonから呼び出すまでに詰まった話

AivisSpeechをMacにインストールしてPythonから呼び出すまでに詰まった話

この記事は約 12 分で読めます

📖 目次
  1. 📌 動くまでに3時間かかった話
  2. 📌 AivisSpeechとは何か
  3. 📌 Macへのインストール手順
  4. 📌 Pythonから呼び出す
  5. 📌 Discord BotからVCに流す
  6. 📌 launchdで常駐させる
  7. 📌 結局どこで時間が溶けたか
  8. 📌 launchdはまだ試行錯誤中
  9. 📌 関連記事

最終更新: 2026-04-28

動くまでに3時間かかった話

AivisSpeechをMacに入れたのは4月の頭、Discord BotにVC読み上げを付けたかったからだ。テキストをAPIに投げて音声データを返してもらうだけ——構造はシンプルなのに、動くまでに3時間かかった。

理由が3つ重なった。Gatekeeperに弾かれる、エンジンが起動しない、Pythonのhttpxのタイムアウトが短すぎる。どれも単独なら10分で解決できる話だが、3つ同時に踏むと何が原因かわからなくなる。エラーメッセージも「Connection refused」しか出ないので、どこで詰まっているのかすら最初は判断できなかった。

この記事はその詰まりを発生した順番で書いた。完成形から言うと今はlaunchdで常駐させてPythonのHTTP越しに叩いている。1〜2秒でwavが返ってきて、そのままDiscordのVCに流れる——安定はしている。でも「安定するまで」が長かった。


AivisSpeechとは何か

VOICEVOXと同じアーキテクチャのローカルTTSエンジンだ。APIサーバーがlocalhost:10101で起動して、HTTPでテキストを投げると音声データが返ってくる。クラウドに出ない。完全ローカル。

OpenAI TTSは1000文字あたり0.015ドルかかる。Googleも月1万文字を超えると課金が始まる。私の用途——Botが1日数十回喋る、テキスト量は数百〜数千文字——だとローカルのほうが圧倒的にコストが低い。レイテンシも実測で1〜2秒以内に収まっていて、会話のテンポを壊さない。

商用利用の条件はキャラクターごとに違うので確認は必要だが、個人の自動化用途であれば基本的に問題ない。


Macへのインストール手順

ダウンロードと起動、そしてGatekeeperの壁

公式サイト(aivisspeech.jp)からMac版のdmgを落とす。Apple Silicon対応ビルドが別にある場合はそちらを選ぶ。インストール自体は普通のMacアプリと変わらない。

問題は起動した瞬間に来た。「開発元を確認できません」というダイアログ。Gatekeeperだ。ここで10分溶かした。ダイアログの「キャンセル」を押しては開こうとして、Finderの右クリックから「開く」を試して、それでも弾かれた。結局ターミナルから叩くのが確実だった。

# Gatekeeperの隔離フラグを外す
xattr -dr com.apple.quarantine /Applications/AivisSpeech.app

これを実行してから普通にダブルクリックすると起動する。GUIで起動すると右上に「エンジン起動中…」と出る。そこから30秒〜1分待つ。焦ってcurlを叩きに行くと当然 Connection refused が返ってくるので、少し待つ。確認は:

curl http://localhost:10101/version

JSONが返ってきたら成功だ。

それでもエンジンが起動しない場合——マイク設定という盲点

Gatekeeperを突破しても、私のMacBook Pro M5ではエンジンが上がらないことがあった。症状は「アプリは動いているのに localhost:10101 に接続できない」——curlを打っても Connection refused しか返ってこない。

原因を探って20分かかった。アプリのログを見ても有益なエラーは出ていない。ふと「プライバシー設定かもしれない」と思ってシステム設定を開き、「プライバシーとセキュリティ → マイク」を見たら、AivisSpeechの項目があった。オフになっていた。許可したら即座にエンジンが起動した。

TTS(音声出力)なのになぜマイクのアクセス許可が要るのか、私もよくわかっていない。でもこれが原因だったのは確かで、同じ症状が出たら真っ先に確認する場所だ。要求ダイアログが画面に出ないパターンもあるので、手動で確認しに行くほうが早い。


Pythonから呼び出す

2ステップのAPI構造

AivisSpeechのAPIはVOICEVOXと構造が同じで、音声生成は2回のHTTPリクエストで完結する。/audio_queryにテキストとspeaker_idをPOSTして合成クエリを取得し、そのクエリを/synthesisに投げてwavデータを受け取る——それだけだ。

import httpx
import json

AIVISSPEECH_URL = "http://localhost:10101"

def synthesize(text: str, speaker_id: int = 888753760) -> bytes:
    query_resp = httpx.post(
        f"{AIVISSPEECH_URL}/audio_query",
        params={"text": text, "speaker": speaker_id},
        timeout=30,
    )
    query_resp.raise_for_status()
    query = query_resp.json()

    audio_resp = httpx.post(
        f"{AIVISSPEECH_URL}/synthesis",
        params={"speaker": speaker_id},
        content=json.dumps(query),
        headers={"Content-Type": "application/json"},
        timeout=60,
    )
    audio_resp.raise_for_status()
    return audio_resp.content

if __name__ == "__main__":
    wav = synthesize("テスト音声です")
    with open("/tmp/test.wav", "wb") as f:
        f.write(wav)
    print("生成完了: /tmp/test.wav")

最初 timeout を 10 秒にしていたら synthesis でタイムアウトが頻発した。文字数が多いと合成に数秒かかることがあるので、synthesis 側は余裕を持って 60 秒にしている。

speaker_idの確認方法

speaker_id はAivisSpeechのGUIに表示されている番号で、キャラクターによって値が違う。コマンドで一覧を出すなら:

curl http://localhost:10101/speakers | python3 -m json.tool | grep -A2 '"name"'

これで全キャラクターとIDが並ぶ。GUIを開くのが面倒なときはこちらのほうが早い。


Discord BotからVCに流す

単体でwavが生成できたら、次はDiscord Botとつなぐ。discord.pyのFFmpegAudioSourceを使ってVCに流す構成だ。

import discord
import asyncio
import httpx
import json
import tempfile
import os

AIVISSPEECH_URL = "http://localhost:10101"
SPEAKER_ID = 888753760

async def speak_in_vc(vc: discord.VoiceClient, text: str):
    async with httpx.AsyncClient() as client:
        query_resp = await client.post(
            f"{AIVISSPEECH_URL}/audio_query",
            params={"text": text, "speaker": SPEAKER_ID},
            timeout=30,
        )
        query = query_resp.json()

        audio_resp = await client.post(
            f"{AIVISSPEECH_URL}/synthesis",
            params={"speaker": SPEAKER_ID},
            content=json.dumps(query),
            headers={"Content-Type": "application/json"},
            timeout=60,
        )

    with tempfile.NamedTemporaryFile(suffix=".wav", delete=False) as f:
        f.write(audio_resp.content)
        tmp_path = f.name

    try:
        source = discord.FFmpegPCMAudio(tmp_path)
        vc.play(source)
        while vc.is_playing():
            await asyncio.sleep(0.1)
    finally:
        os.unlink(tmp_path)

FFmpegが入っていないとここで詰まる。brew install ffmpeg で入れておく。私はこれを忘れていて「FileNotFoundError: [Errno 2] No such file or directory: ‘ffmpeg’」が出た——見ればわかるエラーだが、音声が出ない原因がAivisSpeech側だと思い込んでいたので15分無駄にした。


launchdで常駐させる

GUIアプリをlaunchdで自動起動させるのは少し癖がある。/Applications/AivisSpeech.appをそのまま指定しても動かないことがある。実体のバイナリをフルパスで指定する。

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN"
  "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>Label</key>
    <string>com.taito.aivisspeech</string>
    <key>ProgramArguments</key>
    <array>
        <string>/Applications/AivisSpeech.app/Contents/MacOS/AivisSpeech</string>
    </array>
    <key>RunAtLoad</key>
    <true/>
    <key>KeepAlive</key>
    <true/>
    <key>StandardOutPath</key>
    <string>/tmp/aivisspeech.log</string>
    <key>StandardErrorPath</key>
    <string>/tmp/aivisspeech_err.log</string>
</dict>
</plist>

これを ~/Library/LaunchAgents/com.taito.aivisspeech.plist に置いて:

launchctl load ~/Library/LaunchAgents/com.taito.aivisspeech.plist

KeepAlive: true にしたら落ちた後に再起動ループに入った。/tmp/aivisspeech_err.log が5分で100MB超えていた。今は KeepAlive: false にして、エンジンが落ちていたらPythonスクリプト側から起動チェックをかける構成にしている。GUIアプリをlaunchdに乗せるのはそれ自体が一仕事だと思っておいたほうがいい。


結局どこで時間が溶けたか

一番時間がかかったのはエンジンが起動しない問題で、原因はマイクのプライバシー設定だった。コードを書くのは10分で済んだのに、環境の問題で2時間半以上溶かした——これが実態だ。

Gatekeeperのほうはエラーメッセージが明確なのでまだいい。プライバシー設定はダイアログが出ないパターンがあるので発見が遅れた。「curlでバージョンが返ってきた瞬間」にやっと動いていることがわかる——あそこまでが長い。

API自体はシンプルで、VOICEVOXの記事がそのまま参考になる。speaker_idさえ合っていれば、コードは素直に動く✨


launchdはまだ試行錯誤中

AivisSpeechのPython連携は「dmgインストール → Gatekeeper回避 → プライバシー設定確認 → curlでバージョン確認 → audio_query+synthesisの2リクエスト」で動く。詰まりやすいのはコードより環境側の話がほとんどだ。

launchdで常駐させるのは今も試行錯誤中で、GUIアプリ特有の挙動がある。無理にlaunchdに乗せるより、Botの起動スクリプト内でサブプロセスとして立ち上げるほうがシンプルかもしれない。私は今のところGUI手動起動+Bot自動起動で運用している——launchdはもう少し詰めてから改めて書く。


一ノ瀬泰斗のアバター
一ノ瀬泰斗
AI自動化エンジニア / Python個人開発者

Claude Code × Ollama × launchd で SNS・ブログ・KDPを全自動化。実測データと失敗談を軸に、月5万円収益化のリアルな記録を発信中。

💬 自動化の相談・小規模受託も受付中:「launchd で毎朝 AI が動く仕組みを作りたい」「KDP の自動出版を組みたい」など、X (@taito_automate) の DM からお気軽にどうぞ。


関連記事

✨ AUTHOR'S KDP BOOKS

かかる人向ケ、10分でわかるAI自動化入門

Claude Code / Ollama / launchd の実践テクニックをコンパクトにまとめたシリーズ。非エンジニアの会社員向けに書いてます。

Amazonで見る ›

✨ FOLLOW ME

AI自動化の実験・失敗・実測データを毎日発信中

𝕏 フォローする