Open WebUIでVOICEVOXのTTS使いたくないか？

作りました。どうぞ。というかこの記事自体が作ったでっていう報告みたいなもんです。

https://github.com/NP-F/openaitts_voicevox_bridge

## TL;DR

- Open WebUIでVOICEVOXのTTS使うためのブリッジ作ったで
- ~~報告だけやと短いから~~ついでに学んだことを書いてくで

## Open WebUIとは

Ollamaとそのモデルをブラウザ上で動かせるやつです。
デフォルトならOllamaってCLI上で動かすものなんですが、Open WebUIを使ったらChatGPTライクな画面で動かせます。

詳しくはこちらから。

https://docs.openwebui.com/

## VOICEVOXで喋ってもらおう

皆さんご存知であろうVOICEVOX。AIを用いた音声合成を行ってくれる無料ソフトです。仕組み等々はオープンソースになってます。また、APIもあって、他のソフトなどとの連携もしやすいようになっています。

https://voicevox.hiroshiba.jp/

で、今回はそんなVOICEVOXの音声合成をOpen WebUIから使えるようにするためのブリッジを作ったので、その宣伝と、ついでにどうやって作ったかをちょっとだけ紹介します。

## 使用

コードはここです。
あと、使用には`Python`, `ffmpeg`が必要です。パスを通しておいてください。

https://github.com/NP-F/openaitts_voicevox_bridge

### 1. ブリッジを起動

`openaitts2voicevox_bridge.py`を起動してください。
また、VOICEVOXも起動してください。

### 2.Open WebUIでの設定

TTSの設定からOpenAIを選択して、URLに`http://localhost:8000/v1`を入れます。ほかは何かしら書いておくといいかも。

### 3.多分動く

音声再生をすれば多分動きます。
詳しい設定はGithubのreadme.mdに書いてあるのでそっち見てください。

## 作りながら得た知識

### 1.PythonのFastAPIは超便利

当方APIブリッジどころか、FastAPIを使ったのも初めてですし、curlのPOSTすら使い方が怪しいのですが、そういう自分から見ても、FastAPIはメチャクチャわかりやすかったです。

特に、`Swagger UI`は感動しました。自分で立てたサーバーの動作をGUI付きで、しかもcurlせずにテストできるのは感動モノです。


### 2.configparserも捨てたもんじゃない

設定ファイル言語って言うんでしょうか？`json`とか`toml`とか。最新のPythonにはどうやら`toml`のパーサが追加されたようですが、ウチのPythonは3.10なもので、設定ファイルが`json`か`configparser`しか扱えそうにありません。

わざわざパッケージを使いたくもないし、人間が書くということを考えると`configparser`か...。と思い、渋々`configparser`を使ってみましたが...。これが案外悪くない。
言語仕様が不明だったり、そもそもiniファイル自体が古すぎて意味不明だったり...。問題点も多いですが、小規模なプロジェクトには十分でした。
まあ、大規模プロジェクトなら`toml`とか`yaml`とかにしてほしいところです。

### 3.クソコードを止める + ChatGPTで知らないことを知る

こんなコードを書くシーンが有りました。

```py
def voicevox_api(input, voice):
    # switch-case文が恋しい
    if is_int(voice):
        return voicevox_api_runner(input, voice)
    elif voice == "alloy":
        return voicevox_api_runner(input, config["ALTID"]["ALLOY"])
    elif voice == "echo":
        return voicevox_api_runner(input, config["ALTID"]["ECHO"])
    elif voice == "fable":
        return voicevox_api_runner(input, config["ALTID"]["FABLE"])
    elif voice == "onyx":
        return voicevox_api_runner(input, config["ALTID"]["ONYX"])
    elif voice == "nova":
        return voicevox_api_runner(input, config["ALTID"]["NOVA"])
    elif voice == "shimmer":
        return voicevox_api_runner(input, config["ALTID"]["SHIMMER"])
    else:
        # IDでも上記のでもないときはこっち
        return voicevox_api_runner(input, config["ALTID"]["DEFAULT_SPEAKER"])
```

`voicevox_api_runner`関数でVOICEVOXのAPIと通信します。`is_int`関数は名前のとおりです...。
ただしこれ、まあまあな**クソコード**ですよね。

だから、ChatGPTに聞いて以下のように修正しました。

```py
def voicevox_api(input, voice):
    voice_map = {
        "alloy": config["ALTID"]["ALLOY"],
        "echo": config["ALTID"]["ECHO"],
        "fable": config["ALTID"]["FABLE"],
        "onyx": config["ALTID"]["ONYX"],
        "nova": config["ALTID"]["NOVA"],
        "shimmer": config["ALTID"]["SHIMMER"]
    }
    
    return voicevox_api_runner(input, voice_map.get(voice, config["ALTID"]["DEFAULT_SPEAKER"]) if not is_int(voice) else voice)
```

`.get()`で変数内に要素がない場合に代わりの要素を指定する方法とか初めて知りました。なんとも知識不足。そんな業があったのかって感じです。
ChatGPTって、ハルシネーションとかもあって良くないとは言われますが、「**知らないことすら知らないことを知るためのツール**」としてはすごく優秀だと思っています。

特に、日曜プログラマの自分なんかは人のコードを読む機会が現職のエンジニアさんたちよりも少ないので、ChatGPTは結構貴重な話し相手です。

## まとめ？

MITで公開してます。これで何も気にせず使えます。
ぜひウチのブリッジ使ってな！