COEIROINKエンジンをDocker＋CLIで起動して音声合成APIを叩いてみた

## 概要

お疲れさまです、ツーナです！  
最近、個人利用のAIと会話できるアプリを作っているのですが、  
その過程で「つくよみちゃん」の声を使いたいなと思い、  
調べたところ **COEIROINK** に辿り着きました。

> COEIROINKのダウンロードページ  
> https://coeiroink.com/download

当初は、以下のエンジンリポジトリがあることを知り、
これを利用して起動する予定でした。
https://github.com/shirowanisan/voicevox_engine

しかしながら、pyopenjtalkのビルド周りや依存関係に
関するエラー修正難航してしまい、一旦断念しました。

そこで一旦GUIの方がどうなっているのか見てみようかなと
COEIROINKエディタ版の  中身をざっと見ると
engineというバイナリがあり、  
これを起動することでAPIサーバが立ち上がる
仕様になっていました。

なので、これをうまく使えばAWSやGCP（FargateやCloud Run）
で利用するなどにも応用できそうです。

今回は、

- COEIROINKのLinux版をDocker上で動かす  
- CLIでAPIサーバを立ち上げる  
- PostmanでAPIを叩き、音声を合成する  

という流れをまとめました！

---

## 結論

最終的に出来上がったDockerfileは以下に
なりますので、結論だけ欲しい方は以下よりお願いします。
https://github.com/devtoona/coe-cloud-run

---

## ディレクトリ構成

プロジェクトルートに以下を配置しています。

```
.
├── Dockerfile
├── start.sh
└── COEIROINK_LINUX_CPU_v.2.11.2/
    ├── engine/
    │   └── engine (実行バイナリ)
    └── 他フォルダ・ファイル
```

---

## Dockerfileの作成

Dockerfileを最小構成に仕上げるため
engineが依存している共有ライブラリを確認してみました。

```
ldd engine/engine
```

出力は以下のとおりでした。

```
libdl.so.2 => /lib/x86_64-linux-gnu/libdl.so.2 (0x00007fffff7b8000)
libz.so.1 => /lib/x86_64-linux-gnu/libz.so.1 (0x00007fffff79c000)
libpthread.so.0 => /lib/x86_64-linux-gnu/libpthread.so.0 (0x00007fffff797000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007fffff56e000)
/lib64/ld-linux-x86-64.so.2 (0x00007ffffffc4000)
```

で、ライブラリ名とubuntuイメージに含まれているかを確認したところ、
全て標準で含まれているのでCOEIROINKエンジンを動かすのに追加で
インストールは必要なさそうです。

| ライブラリ名              | 内容                           | Ubuntuに含まれるか |
|---------------------------|--------------------------------|--------------------|
| `libdl.so.2`              | 動的リンク用ライブラリ         | ✅ 標準（glibc）    |
| `libz.so.1`               | 圧縮・解凍ライブラリ（zlib）     | ✅ 標準             |
| `libpthread.so.0`         | POSIXスレッドライブラリ         | ✅ 標準（glibc）    |
| `libc.so.6`               | C言語標準ライブラリ             | ✅ 標準（glibc）    |
| `ld-linux-x86-64.so.2`    | ELFローダ（実行時リンクローダ） | ✅ 標準（glibc）    |

---

なので、

1. COEIROINKのダウンロード  
2. ダウンロードしたCOEIROINKをDocker内にコピー  
3. Ubuntu 22.04ベースのDockerfileを用意し、`engine` を実行  

という手順でAPIが使える！と思いきや、**APIに接続できない** という
壁にぶち当たりました。

---

## 接続できなかった原因
### `127.0.0.1` にしかバインドされていない

```bash
./engine/engine
```

このコマンドでAPIサーバは `127.0.0.1:50032` にバインドされます。  
そのため、Dockerで以下のようにポートを開けても：

```bash
docker run -p 50032:50032 ...
```

**ホストからはアクセスできません。**

理由は「127.0.0.1」は**コンテナ内のローカル**を指しており、  
ホスト側から見たときにそのポートは見えないからです。

---

## 解決策
### `socat` でポートフォワーディング

`socat` はネットワークプロキシのように動きます。

```bash
socat TCP-LISTEN:50033,fork TCP:127.0.0.1:50032
```

このコマンドにより：

- `0.0.0.0:50033` で受けた通信を  
- `127.0.0.1:50032` に中継してくれます  

つまり、ホストからは `localhost:50033` にアクセスするだけで  
Docker内の `127.0.0.1:50032` に繋がる、という仕組みになります。

Dockerでは `-p 50033:50033` でポートを開けておけばOK！

---

## 最終的に出来上がったDockerfile

```dockerfile
FROM ubuntu:22.04

# 非対話モードにしておく
ENV DEBIAN_FRONTEND=noninteractive
# ポート中継に必要なsocatをインストール
RUN apt update && apt install -y \
    socat \
    && apt clean && rm -rf /var/lib/apt/lists/*

ARG ENGINE_NAME="COEIROINK_LINUX_CPU_v.2.11.2"
COPY ./${ENGINE_NAME} /opt/${ENGINE_NAME}
COPY ./start.sh /opt/${ENGINE_NAME}/start.sh
WORKDIR /opt/${ENGINE_NAME}

# バイナリやシェルスクリプトに実行権限を付与
RUN chmod +x ./engine/engine
RUN chmod +x ./start.sh
CMD ["./start.sh"]
```

```start.sh
#!/bin/bash

# バイナリの起動をログファイルを使って監視
./engine/engine > engine.log 2>&1 &

# "Uvicorn running" が出るまで（エンジン起動まで）ループで待機
while ! grep -q "Uvicorn running on" engine.log; do
    sleep 0.2
done

color="\033[1;32m"
color_default="\033[0m"


# エンジン起動後にカスタムメッセージを表示
echo -e "${color}INFO${color_default}    running on http://127.0.0.1:50033 (Press CTRL+C to quit)"

# socatでポート中継
socat TCP-LISTEN:50033,fork TCP:127.0.0.1:50032

```

---

## ビルドと実行

以下に示すコマンドでビルドと実行をします。  
私の環境では、M1macを利用していたため、platformを指定しています。


```bash
# ビルド
docker buildx build --platform linux/amd64 --load -t coeiroink-engine-x86

# 実行
docker run -p 50033:50033 -it --platform linux/amd64 coeiroink-engine-x86.
```

### 補足
platformをlinux/amd64に指定しないと

```
rosetta error: failed to open elf at /lib64/ld-linux-x86-64.so.2
```

というエラーが出ます。  
これは、Apple Silicon（M1/M2などのarm64）Mac上で、linux/amd64 向けのバイナリを実行しようとして失敗するためです。  
（言い換えれば、COEIROINKを実行にはlinux/amd64である必要があるということです。）

---

## APIを呼び出し音声合成できるかの確認

上記が終わったら、127.0.0.1:50033にアクセスして
APIを呼びだせると思います。

APIの仕様は公式サイト（https://coeiroink.com/help/002）
が参考になると思います。

私の環境では、APIドキュメントを参考にPostmanを使って
話者リストを取得し、その値を使いつつ
/v1/synthesisを呼び出すと音声合成できました。

そのときの設定値は以下ですが、色々変えてみるのも面白いと思います。
```json
{
  "volumeScale": 1.0,
  "pitchScale": 0.1,
  "intonationScale": 1.0,
  "prePhonemeLength": 1.0,
  "postPhonemeLength":1.0,
  "outputSamplingRate": 48000,
  "speakerUuid": "3c37646f-3881-5374-2a83-149267990abc",
  "styleId": 6,
  "text": "こんにちは！つくよみちゃんです！",
  "speedScale": 1.0
}
```
---

## まとめ

COEIROINKのランタイムAPIを、Docker + CLI + Postmanで
動かすことに成功しました！  

調べてもGUIで立ち上げるみたいな記事ばかりでCLIで起動している
方法がほとんどなかったので、同じように
「DockerからAPI叩きたいけどアクセスできねぇ！」
と悩んでいる方の役に立てば嬉しいです！

---