Voicevox の読み上げ(メモリ不足のため失敗)

Voicevox の読み上げ

YouTubeで人気「ずんだもん」の音声を利用、LLMの返答をPythonで読み上げる
を参考にする

Pyttxsで読み上げの長文が可能だが
Windows Macでしかできない
ラズパイと ubuntu では音声エンジンが使えないので
Voicevox を使っているがdocker だと100文字以上だとエラーになるので
docker以外を使う

ollama run gemma2:2b

で使っているが欲しいのはvoicevox の導入と音声の生成部分

https://github.com/VOICEVOX/voicevox_core/releases
の中から
m1MacbookAir なので
macOS arm64

download-osx-arm64
を選択する

なお whlファイルも必要らしいので調べる

download-osx-arm64 と voicevox_core-0.16.0-cp310-abi3-macosx_11_0_arm64.whl について知りたい

項目 | 意味
voicevox_core | VOICEVOX(音声合成ソフト)の「コア機能ライブラリ」
0.16.0 | バージョン番号(この場合は 0.16.0)
cp310 | CPython 3.10向け(Python 3.10に対応)
abi3 | 複数のPythonバージョンに対応できるABI (バイナリ互換性)
macosx_11_0_arm64 | macOS 11.0 (Big Sur)以降、Apple Silicon (ARM64) 向け

つまりこのファイルは、
* Python 3.10 上で動かすために作られた
* Mac (Apple Silicon版) 用の
* VOICEVOXのコア部分のバイナリパッケージ(wheelファイル、.whl) ということです!
この .whl ファイルをインストールすると、Pythonプログラムから VOICEVOX のエンジン(音声合成)を直接呼び出して使えるようになります。

pip install voicevox_core-0.16.0-cp310-abi3-macosx_11_0_arm64.whl

でインストール

項目 | 内容
download-osx-arm64 | Apple Silicon Mac用の何かをダウンロードするもの(具体的な中身は文脈次第)
voicevox_core-0.16.0-…whl | Python 3.10対応のVOICEVOXエンジンをApple Silicon Mac用にビルドしたインストールファイル

とのこと

そのままdownload-osx-arm64 をクリックしても動かないので
https://github.com/VOICEVOX/voicevox_core/blob/main/docs/guide/user/usage.md
を参考に

mv ~/Downloads/download-osx-arm64 .

DirectML 版や CUDA 版があるらしいけど
m1macbookAir だとGPUは使えないらしい

https://github.com/VOICEVOX/voicevox_core/blob/main/docs/guide/user/usage.md
によれば

# CPU版を利用する場合

./download --exclude c-api # C APIを使う場合は`--exclude c-api`は無し

thread 'main' panicked at /Users/runner/.cargo/registry/src/index.crates.io-6f17d22bba15001f/minus-5.6.1/src/state.rs:322:24:
byte index 84 is not a char boundary; it is inside 'く' (bytes 82..85) of `上下キーとスペースでスクロールし、読み終えたらqを押してください`
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
ERROR something went wrong with the pager
ダウンロードには以下の利用規約への同意が必要です。
(矢印キーで移動、q で終了)
─┬─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
 │ # VOICEVOX 音声モデル 利用規約
 │ 
 │ ## 許諾内容
 │ 
 │ 1. 商用・非商用問わず利用することができます
 │ 2. アプリケーションに組み込んで再配布することができます
 │ 3. 作成された音声を利用する際は、各音声ライブラリの規約に従ってください
 │ 4. 作成された音声の利用を他者に許諾する際は、当該他者に対し本許諾内容の 3 及び 4 の遵守を義務付けてください
 │ 
 │ ## 禁止事項
 │ 
 │ - 逆コンパイル・リバースエンジニアリング及びこれらの方法の公開すること
 │ - 製作者または第三者に不利益をもたらす行為
 │ - 公序良俗に反する行為
 │ 
 │ ## 免責事項
 │ 
 │ 本ソフトウェアにより生じた損害・不利益について、製作者は一切の責任を負いません。
 │ 
 │ ## その他
 │ 
 │ ご利用の際は VOICEVOX を利用したことがわかるクレジット表記が必要です。
 │ 
 │ ---
 │ 
 │ # 音声ライブラリ利用規約
 │ 
 │ ## 四国めたん
 │ 
 │ 四国めたんの音声ライブラリを用いて生成した音声は、
 │ 「VOICEVOX:四国めたん」とクレジットを記載すれば、商用・非商用で利用可能です。
 │ 
 │ 利用規約の詳細は以下をご確認ください。
 │ https://zunko.jp/con_ongen_kiyaku.html
 │ 
 │ ## ずんだもん
 │ 
 │ ずんだもんの音声ライブラリを用いて生成した音声は、
 │ 「VOICEVOX:ずんだもん」とクレジットを記載すれば、商用・非商用で利用可能です。
 │ 
 │ 利用規約の詳細は以下をご確認ください。
 │ https://zunko.jp/con_ongen_kiyaku.html
 │ 
 │ ## 春日部つむぎ
 │ 
 │ 春日部つむぎの音声ライブラリを用いて生成した音声は、
 │ 「VOICEVOX:春日部つむぎ」とクレジットを記載すれば、商用・非商用で利用可能です。
 │ 
 │ 利用規約の詳細は以下をご確認ください。
 │ https://tsumugi-official.studio.site/rule
 │ 
 │ ## 波音リツ
 │ 
 │ 波音リツの音声ライブラリを用いて生成した音声は、
 │ 「VOICEVOX:波音リツ」とクレジットを記載すれば、商用・非商用で利用可能です。
 │ 
 │ 利用規約の詳細は以下をご確認ください。
 │ http://canon-voice.com/kiyaku.html
 │ 
 │ ## 玄野武宏
 │ 
 │ 玄野武宏の音声ライブラリを用いて生成した音声は、
 │ 「VOICEVOX:玄野武宏」とクレジットを記載すれば、商用・非商用で利用可能です。
 │ 
 │ 利用規約の詳細は以下をご確認ください。
 │ https://virvoxproject.wixsite.com/official/voicevoxの利用規約
 │ 
 │ ## 白上虎太郎
 │ 
 │ 白上虎太郎の音声ライブラリを用いて生成した音声は、
 │ 「VOICEVOX:白上虎太郎」とクレジットを記載すれば、商用・非商用で利用可能です。
 │ 
 │ 利用規約の詳細は以下をご確認ください。
 │ https://virvoxproject.wixsite.com/official/voicevoxの利用規約
 │ 
 │ ## 青山龍星
 │ 
 │ 個人が青山龍星の音声ライブラリを用いて生成した音声は、
 │ 「VOICEVOX:青山龍星」とクレジットを記載すれば、商用・非商用で利用可能です。
 │ ただし企業が携わる形で利用する場合は、「ななはぴ(https://v.seventhh.com/contact/)」に対し事前確認を取る必要があります。
 │ 
 │ 利用規約の詳細は以下をご確認ください。
 │ https://virvoxproject.wixsite.com/official/voicevoxの利用規約
 │ 
 │ ## 冥鳴ひまり
 │ 
 │ 冥鳴ひまりの音声ライブラリを用いて生成した音声は、
 │ 「VOICEVOX:冥鳴ひまり」とクレジットを記載すれば、商用・非商用で利用可能です。
 │ 
 │ 利用規約の詳細は以下をご確認ください。
 │ https://meimeihimari.wixsite.com/himari/terms-of-use
 │ 
 │ ## 九州そら
 │ 
 │ 九州そらの音声ライブラリを用いて生成した音声は、
 │ 「VOICEVOX:九州そら」とクレジットを記載すれば、商用・非商用で利用可能です。
 │ 
 │ 利用規約の詳細は以下をご確認ください。
 │ https://zunko.jp/con_ongen_kiyaku.html
 │ 
 │ ## もち子さん
 │ 
 │ 個人がもち子さんの音声ライブラリを用いて生成した音声は、
 │ 「VOICEVOX:もち子(cv 明日葉よもぎ)」とクレジットを記載すれば、
 │ 音声作品・音声素材・ゲーム作品等を除いて商用・非商用で利用可能です。
 │ ただし企業が携わる形で利用する場合は、「もちぞら模型店(https://vtubermochio.wixsite.com/mochizora/contact)」に対し事前確認を取る必要があります。
 │ 
 │ 利用規約の詳細は以下をご確認ください。
 │ https://vtubermochio.wixsite.com/mochizora/利用規約
 │ 
 │ ## 剣崎雌雄
 │ 
 │ 剣崎雌雄の音声ライブラリを用いて生成した音声は、
 │ 「VOICEVOX:剣崎雌雄」とクレジットを記載すれば、商用・非商用で利用可能です。
 │ 
 │ 利用規約の詳細は以下をご確認ください。
 │ https://frontier.creatia.cc/fanclubs/413/posts/4507
 │ 
 │ ## WhiteCUL
 │ 
 │ WhiteCUL の音声ライブラリを用いて生成した音声は、
 │ 「VOICEVOX:WhiteCUL」とそれぞれクレジットを記載すれば、商用・非商用で利用可能です。
 │ 
 │ 利用規約の詳細は以下をご確認ください。
 │ https://www.whitecul.com/guideline
 │ 
 │ ## 後鬼
 │ 
 │ 個人が後鬼の音声ライブラリを用いて生成した音声は、
 │ 「VOICEVOX:後鬼」とクレジットを記載すれば、商用・非商用で利用可能です。
 │ ただし企業が携わる形で利用する場合は、「【鬼っ子ハンターついなちゃん】プロジェクト(https://ついなちゃん.com/mail/)」に対し事前確認を取る必要があります。
 │ 
 │ 利用規約の詳細は以下をご確認ください。
 │ https://ついなちゃん.com/voicevox_terms/
 │ 
 │ ## No.7
 │ 
 │ 個人が No.7 の音声ライブラリを用いて生成した音声は、
 │ 「VOICEVOX:No.7」とクレジットを記載すれば、非商用(同人利用や配信による収入は OK)で利用可能です。
 │ その他商用利用の場合は、「No.7 製作委員会(https://voiceseven.com/)」に対し事前確認を取る必要があります。
 │ 
 │ 利用規約の詳細は以下をご確認ください。
 │ https://voiceseven.com/#j0200
 │ 
 │ ## ちび式じい
 │ 
 │ ちび式じいの音声ライブラリを用いて生成した音声は、
 │ 「VOICEVOX:ちび式じい」とクレジットを記載すれば、商用・非商用で利用可能です。
 │ 
 │ 利用規約の詳細は以下をご確認ください。
 │ https://docs.google.com/presentation/d/1AcD8zXkfzKFf2ertHwWRwJuQXjNnijMxhz7AJzEkaI4
 │ 
 │ ## 櫻歌ミコ
 │ 
 │ 櫻歌ミコの音声ライブラリを用いて生成した音声は、
 │ 「VOICEVOX:櫻歌ミコ」とクレジットを記載すれば、商用・非商用で利用可能です。
 │ 
 │ 利用規約の詳細は以下をご確認ください。
 │ https://voicevox35miko.studio.site/rule
 │ 
 │ ## 小夜/SAYO
 │ 
 │ 小夜/SAYO の音声ライブラリを用いて生成した音声は、
 │ 「VOICEVOX:小夜/SAYO」とクレジットを記載すれば、商用・非商用で利用可能です。
 │ 
 │ 利用規約の詳細は以下をご確認ください。
 │ https://316soramegu.wixsite.com/sayo-official/guideline
 │ 
 │ ## ナースロボ_タイプT
 │ 
 │ ナースロボ_タイプTの音声ライブラリを用いて生成した音声は、
 │ 「VOICEVOX:ナースロボ_タイプT」とクレジットを記載すれば、商用・非商用で利用可能です。
 │ 
 │ 利用規約の詳細は以下をご確認ください。
 │ https://www.krnr.top/rules
 │ 
 │ ## †聖騎士 紅桜†
 │ 
 │ †聖騎士 紅桜†の音声ライブラリを用いて生成した音声は、
 │ 「VOICEVOX:†聖騎士 紅桜†」とクレジットを記載すれば、商用・非商用で利用可能です。
 │ 
 │ 利用規約の詳細は以下をご確認ください。
 │ https://commons.nicovideo.jp/material/nc296132
 │ 
 │ ## 雀松朱司
 │ 
 │ 雀松朱司の音声ライブラリを用いて生成した音声は、
 │ 「VOICEVOX:雀松朱司」とクレジットを記載すれば、商用・非商用で利用可能です。
 │ 
 │ 利用規約の詳細は以下をご確認ください。
 │ https://virvoxproject.wixsite.com/official/voicevoxの利用規約
 │ 
 │ ## 麒ヶ島宗麟
 │ 
 │ 麒ヶ島宗麟の音声ライブラリを用いて生成した音声は、
 │ 「VOICEVOX:麒ヶ島宗麟」とクレジットを記載すれば、商用・非商用で利用可能です。
 │ 
 │ 利用規約の詳細は以下をご確認ください。
 │ https://virvoxproject.wixsite.com/official/voicevoxの利用規約
 │ 
 │ ## 春歌ナナ
 │ 
 │ 春歌ナナの音声ライブラリを用いて生成した音声は、
 │ 「VOICEVOX:春歌ナナ」とクレジットを記載すれば、商用・非商用で利用可能です。
 │ 
 │ 利用規約の詳細は以下をご確認ください。
 │ https://nanahira.jp/haruka_nana/guideline.html
 │ 
 │ ## 猫使アル
 │ 
 │ 猫使アルの音声ライブラリを用いて生成した音声は、
 │ 「VOICEVOX:猫使アル」とクレジットを記載すれば、商用・非商用で利用可能です。
 │ 
 │ 利用規約の詳細は以下をご確認ください。
 │ https://nekotukarb.wixsite.com/nekonohako/利用規約
 │ 
 │ ## 猫使ビィ
 │ 
 │ 猫使ビィの音声ライブラリを用いて生成した音声は、
 │ 「VOICEVOX:猫使ビィ」とクレジットを記載すれば、商用・非商用で利用可能です。
 │ 
 │ 利用規約の詳細は以下をご確認ください。
 │ https://nekotukarb.wixsite.com/nekonohako/利用規約
 │ 
 │ ## 中国うさぎ
 │ 
 │ 中国うさぎの音声ライブラリを用いて生成した音声は、
 │ 「VOICEVOX:中国うさぎ」とクレジットを記載すれば、商用・非商用で利用可能です。
 │ 
 │ 利用規約の詳細は以下をご確認ください。
 │ https://zunko.jp/con_ongen_kiyaku.html
 │ 
 │ ## 栗田まろん
 │ 
 │ 栗田まろんの音声ライブラリを用いて生成した音声は、
 │ 「VOICEVOX:栗田まろん」とクレジットを記載すれば、商用・非商用で利用可能です。
 │ 
 │ 利用規約の詳細は以下をご確認ください。
 │ https://aivoice.jp/character/maron/
 │ 
 │ ## あいえるたん
 │ 
 │ あいえるたんの音声ライブラリを用いて生成した音声は、
 │ 「VOICEVOX:あいえるたん」とクレジットを記載すれば、商用・非商用で利用可能です。
 │ 
 │ 利用規約の詳細は以下をご確認ください。
 │ https://www.infiniteloop.co.jp/special/iltan/terms/
 │ 
 │ ## 満別花丸
 │ 
 │ 満別花丸の音声ライブラリを用いて生成した音声は、
 │ 「VOICEVOX:満別花丸」とクレジットを記載すれば、商用・非商用で利用可能です。
 │ 
 │ 利用規約の詳細は以下をご確認ください。
 │ https://100hanamaru.wixsite.com/manbetsu-hanamaru/rule
 │ 
 │ ## 琴詠ニア
 │ 
 │ 琴詠ニアの音声ライブラリを用いて生成した音声は、
 │ 「VOICEVOX:琴詠ニア」とクレジットを記載すれば、商用・非商用で利用可能です。
 │ 
 │ 利用規約の詳細は以下をご確認ください。
 │ https://commons.nicovideo.jp/works/nc315435
 │ 
 │ ## Voidoll
 │ 
 │ 個人が Voidoll の音声ライブラリを用いて生成した音声は、
 │ 「VOICEVOX:Voidoll(CV:丹下桜)」とクレジットを記載すれば、商用・非商用で利用可能です。
 │ ただし法人による利用の場合は、個別に問い合わせ(https://qa.nicovideo.jp/)が必要です。
 │ 
 │ 利用規約の詳細は以下をご確認ください。
 │ https://blog.nicovideo.jp/niconews/224589.html
 │ 
 │ ## ぞん子
 │ 
 │ ぞん子の音声ライブラリを用いて生成した音声は、
 │ 「VOICEVOX:ぞん子」とクレジットを記載すれば、商用・非商用で利用可能です。
 │ ただし商用利用の場合は、個別に問い合わせ(https://zonko.zone-energy.jp/contact)が必要です。
 │ 
 │ 利用規約の詳細は以下をご確認ください。
 │ https://zonko.zone-energy.jp/guideline
 │ 
 │ ## 中部つるぎ
 │ 
 │ 中部つるぎの音声ライブラリを用いて生成した音声は、
 │ 「VOICEVOX:中部つるぎ」とクレジットを記載すれば、商用・非商用で利用可能です。
 │ 
 │ 利用規約の詳細は以下をご確認ください。
 │ https://zunko.jp/con_ongen_kiyaku.html
─┼─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
 │ # VOICEVOX ONNX Runtime 利用規約
 │ 
 │ ## 許諾内容
 │ 
 │ 1. 商用・非商用問わず利用することができます
 │ 2. アプリケーションに組み込んで再配布することができます
 │ 3. 作成された音声を利用する際は、各音声ライブラリの規約に従ってください
 │ 4. 作成された音声の利用を他者に許諾する際は、当該他者に対し本許諾内容の 3 及び 4 の遵守を義務付けてください
 │ 
 │ ## 禁止事項
 │ 
 │ - 逆コンパイル・リバースエンジニアリング及びこれらの方法の公開すること
 │ - 製作者または第三者に不利益をもたらす行為
 │ - 公序良俗に反する行為
 │ 
 │ ## 免責事項
 │ 
 │ 本ソフトウェアにより生じた損害・不利益について、製作者は一切の責任を負いません。
 │ 
 │ ## その他
 │ 
 │ ご利用の際は VOICEVOX を利用したことがわかるクレジット表記が必要です。
─┴─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
[Agreement Required]
「VOICEVOX 音声モデル 利用規約」と「VOICEVOX ONNX Runtime 利用規約」に同意しますか?
同意する場合は y を、同意しない場合は n を、再確認する場合は r を入力し、エンターキーを押してください。
[y,n,r] : 

となるので
Y

これでインストールが始まる

次にライブラリインストール

pip install voicevox_core-0.16.0-cp310-abi3-macosx_11_0_arm64.whl

音声ファイルの再生で必要になる、「soundfile」と「sounddevice」という2つのライブラリをインストール

pip install soundfile sounddevice

次に使用モデルを変更する
elyza:jp8bを使用するようにコード変更

python ollama_voicevox_test.py

として保存する

保存先はvoicevox_coreフォルダの中にする

cd voicevox_core 
touch ollama_voicevox_test.py

内容は

import ollama
from pathlib import Path
from voicevox_core import VoicevoxCore
import soundfile as sf
import sounddevice as sd

# Ollamaでelyza:jp8bにチャットする
response = ollama.chat(model="elyza:jp8b", messages=[
    {
        "role": "system",
        "content": "あなたは何でも丁寧に、短く答えてくれる日本語アシスタントです"
    },
    {
        "role": "user",
        "content": "生成AIの未来はどうなりますか?"
    }
])

# Ollamaからのレスポンス本文を取り出し
response_text = response["message"]["content"]
print(response_text)
print("クレジット表記 VOICEVOX:ずんだもん")

# VoicevoxCoreの初期化
vv_core = VoicevoxCore(open_jtalk_dict_dir=Path("open_jtalk_dic_utf_8-1.11"))
speaker_id = 1  # ずんだもん
vv_core.load_model(speaker_id)

# テキストを音声に変換
voice_data = vv_core.tts(response_text, speaker_id)

# 音声ファイルとして保存
with open("output.wav", "wb") as f:
    f.write(voice_data)

# 保存した音声ファイルを再生
data, samplerate = sf.read("output.wav")
sd.play(data, samplerate)
sd.wait()

としたけど

Traceback (most recent call last):
  File "/Users/snowpool/aw10s/gemini/voicevox_core/ollama_voicevox_test.py", line 3, in <module>
    from voicevox_core import VoicevoxCore
ImportError: cannot import name 'VoicevoxCore' from 'voicevox_core' (/Users/snowpool/.pyenv/versions/3.11.0/lib/python3.11/site-packages/voicevox_core/__init__.py)

となる

これは
いまの voicevox_core では
→ VoicevoxCore というクラス名では提供されていません!
とのこと

VoicevoxCore ではなく、
VoicevoxCoreClient を使う

インポートは

from voicevox_core import VoicevoxCoreClient

に変わり
初期化も

vv_core = VoicevoxCoreClient(open_jtalk_dict_dir=Path("open_jtalk_dic_utf_8-1.11"))

となる

しかしコードを

import ollama
from pathlib import Path
from voicevox_core import VoicevoxCoreClient

import soundfile as sf
import sounddevice as sd
import asyncio

async def main():
    # Ollamaで elyza:jp8b にチャットする
    response = ollama.chat(model="elyza:jp8b", messages=[
        {
            "role": "system",
            "content": "あなたは何でも丁寧に、短く答えてくれる日本語アシスタントです"
        },
        {
            "role": "user",
            "content": "生成AIの未来はどうなりますか?"
        }
    ])

    response_text = response["message"]["content"]
    print(response_text)
    print("クレジット表記 VOICEVOX:ずんだもん")

    # VoicevoxCoreClientの初期化
    vv_core = VoicevoxCoreClient(open_jtalk_dict_dir=Path("open_jtalk_dic_utf_8-1.11"))

    # クライアントの起動
    async with vv_core:
        speaker_id = 1  # ずんだもん
        await vv_core.load_model(speaker_id)

        # テキストを音声に変換(非同期)
        voice_data = await vv_core.tts(response_text, speaker_id)

    # 音声ファイルとして保存
    with open("output.wav", "wb") as f:
        f.write(voice_data)

    # 保存した音声ファイルを再生
    data, samplerate = sf.read("output.wav")
    sd.play(data, samplerate)
    sd.wait()

# 非同期実行
asyncio.run(main())

と変更しても変わらない

調べた結果、ver0.16以降は色々変わっているらしい

こうなった場合ChatGPTはほぼ機能しない
Ollama Index の時のように間違ったコードのループになるだけなので
別のサイトを参考にする

PythonからVOICEVOX Coreを使ってみる

これでやりたいのは
おまけ(大量の文章の読み上げサンプル)

ファイルの書き込み用に、aiofilesを使用

pip install aiofiles

でインストール

import asyncio
import aiofiles
from voicevox_core.asyncio import Onnxruntime, OpenJtalk, Synthesizer, VoiceModelFile

texts = [
    ...  # 文章一覧をここに
]

async def tts(synthesizer, text, style_id, index):
    # 音声合成してファイルに保存するコルーチン
    wav = await synthesizer.tts(text, style_id)
    async with aiofiles.open(f"output_{index}.wav", mode="wb") as f:
        await f.write(wav)

async def main(texts):
    # synthesizerの作成
    voicevox_onnxruntime_path = "onnxruntime/lib/" + Onnxruntime.LIB_VERSIONED_FILENAME
    open_jtalk_dict_dir = "dict/open_jtalk_dic_utf_8-1.11"
    ort = await Onnxruntime.load_once(filename=voicevox_onnxruntime_path)
    ojt = await OpenJtalk.new(open_jtalk_dict_dir)
    synthesizer = Synthesizer(ort, ojt)
    # VVMファイルの読み込み
    async with await VoiceModelFile.open("models/vvms/0.vvm") as model:
        await synthesizer.load_voice_model(model)
    # 音声合成の一括実行
    # ttsのコルーチンのリストを作成し、asyncio.gatherで実行する
    style_id = 5  # ずんだもんノーマル(スタイルID: 5)
    await asyncio.gather(*[
        tts(synthesizer, t, style_id, i)
        for i, t in enumerate(texts)
    ])

asyncio.run(main(texts))

がコード

まずはこれで動くかテストする

touch voice016test.py

でファイルを作成し

import asyncio
import aiofiles
from voicevox_core.asyncio import Onnxruntime, OpenJtalk, Synthesizer, VoiceModelFile

texts = [
    "これはテストです"
]

async def tts(synthesizer, text, style_id, index):
    # 音声合成してファイルに保存するコルーチン
    wav = await synthesizer.tts(text, style_id)
    async with aiofiles.open(f"output_{index}.wav", mode="wb") as f:
        await f.write(wav)

async def main(texts):
    # synthesizerの作成
    voicevox_onnxruntime_path = "onnxruntime/lib/" + Onnxruntime.LIB_VERSIONED_FILENAME
    open_jtalk_dict_dir = "dict/open_jtalk_dic_utf_8-1.11"
    ort = await Onnxruntime.load_once(filename=voicevox_onnxruntime_path)
    ojt = await OpenJtalk.new(open_jtalk_dict_dir)
    synthesizer = Synthesizer(ort, ojt)
    # VVMファイルの読み込み
    async with await VoiceModelFile.open("models/vvms/0.vvm") as model:
        await synthesizer.load_voice_model(model)
    # 音声合成の一括実行
    # ttsのコルーチンのリストを作成し、asyncio.gatherで実行する
    style_id = 5  # ずんだもんノーマル(スタイルID: 5)
    await asyncio.gather(*[
        tts(synthesizer, t, style_id, i)
        for i, t in enumerate(texts)
    ])

asyncio.run(main(texts))

を実行

なお voicevox のファイルパスを変えてなかったので

 mv voice016test.py voicevox_core 
cd voicevox_core 
python voice016test.py  

で実行

すると
output_0.wav
ができるので
これを再生すると音声ができているのが確認できる

短文はできるので100文字以上の音声を作成する

しかし文面を変更しても
これはテストです
が繰り返されるだけで他の音声にならない

なので別の方法を使う

Cd ../
touch voice016.py

で別のディレクトリで試す

from voicevox_core.blocking import Onnxruntime, OpenJtalk, Synthesizer, VoiceModelFile

voicevox_onnxruntime_path = "voicevox_core/onnxruntime/lib/" + Onnxruntime.LIB_VERSIONED_FILENAME
open_jtalk_dict_dir = "voicevox_core/dict/open_jtalk_dic_utf_8-1.11"
synthesizer = Synthesizer(Onnxruntime.load_once(filename=voicevox_onnxruntime_path), OpenJtalk(open_jtalk_dict_dir))

with VoiceModelFile.open("voicevox_core/models/vvms/0.vvm") as model:
    synthesizer.load_voice_model(model)

style_id = 5

text = "こんにちは、これはテストです。"
wav = synthesizer.tts(text, style_id)

# wavをファイルに書き出す
with open("output.wav", "wb") as f:
    f.write(wav)

これで実行すると音声が生成される

しかし
これを長文の

不要な情報(価格、電話番号、住所、営業時間など)を取り除き、食材に焦点を当ててレシピを提案します。
鶏むね肉と夏野菜の炒め物
材料:
鶏むね肉 (100g):一口大に切る
トウモロコシ:実を外す
ピーマン (1パック):種を取り、細切りにする
トマト (1パック):くし切りにする
キュウリ (1パック):薄切りにする
旬彩盛 (1パック):内容に応じて適当な大きさに切る (例:もやしならそのまま、ニラなら3cm程度に切る)
サラダ油:大さじ1
塩コショウ:少々
醤油:小さじ1
酒:小さじ1
鶏ガラスープの素:小さじ1/2
作り方:
フライパンにサラダ油を熱し、鶏むね肉を炒める。
鶏肉の色が変わったら、ピーマン、キュウリ、旬彩盛を加えて炒める。
野菜がしんなりしてきたら、トウモロコシ、トマトを加えて軽く炒める。
塩コショウ、醤油、酒、鶏ガラスープの素で調味する。
全体に味がなじんだら、火を止めて完成。
うなぎと野菜の丼
材料:
うなぎ (1串):温める
ピーマン (1パック):千切りにする
キュウリ (1パック):千切りにする
トマト (1パック):薄切りにする
温かいご飯:適量
刻み海苔:適量
作り方:
温かいご飯を丼によそう。
ピーマン、キュウリ、トマトを丼に盛り付ける。
うなぎを乗せる。
刻み海苔を散らして完成。
国産牛ロースのミニステーキ
材料:
国産牛ロース (100g):軽く塩コショウを振る
サラダ油:小さじ1
醤油:小さじ1/2
わさび:お好みで
作り方:
フライパンにサラダ油を熱し、牛ロースを焼く。
片面1~2分ずつ焼き、焼き加減を調整する。
醤油を回し入れて香りを出す。
皿に盛り付け、お好みでわさびを添えて完成。
これらのレシピは、提示された少ない食材で簡単に作れるものです。旬彩盛の内容がわからないため、具体的な調理法は示していませんが、他の野菜と同様に炒めたり、丼に添えたりできます。 必要に応じて、他の調味料や食材を追加してアレンジしてみてください。

とするとメモリが足りずにエラーとなる

このためpyttsx で音声再生をすることにする

とりあえずコンセプトとしては音声でのみのやり取り
まずは ollama + gemma3 4B の結果の音声で再生から実験

ウェイクワードエンジンで起動
faster-whisperで音声を取得し ollama + gemma3 に渡す
処理結果を pyttsx で再生

これを機能分解して作成してく

Googleカレンダーの読み上げ

Googleカレンダーの読み上げ

一週間の予定の取得はできたので
次はvoicevox で読み上げをする

今回も docker で起動して実験する

その前に

import os
import datetime
import pytz
from google.oauth2.credentials import Credentials
from googleapiclient.discovery import build

# カレンダーAPIのスコープ
SCOPES = ['https://www.googleapis.com/auth/calendar']

def main():
    """今週の残りのGoogleカレンダーの予定を取得して表示します。"""
    creds = None
    # 既存のトークンファイルを使用
    if os.path.exists('token.json'):
        creds = Credentials.from_authorized_user_file('token.json', SCOPES)
    else:
        print("トークンファイルが見つかりません。認証を実行してください。")
        return

    # Google Calendar APIサービスを構築
    service = build('calendar', 'v3', credentials=creds)

    # タイムゾーンの設定(日本時間)
    tz = pytz.timezone('Asia/Tokyo')

    # 現在の日時を取得
    now = datetime.datetime.now(tz)

    # 今週の終了日(日曜日)を計算
    start_of_week = now - datetime.timedelta(days=now.weekday())
    end_of_week = start_of_week + datetime.timedelta(days=7)

    # time_minを現在の日時に設定
    time_min = now.isoformat()
    # time_maxは今週の終了日時
    time_max = end_of_week.isoformat()

    print(f"{time_min} から {time_max} までの予定を取得します。")

    # イベントを取得
    events_result = service.events().list(
        calendarId='primary',
        timeMin=time_min,
        timeMax=time_max,
        singleEvents=True,
        orderBy='startTime'
    ).execute()
    events = events_result.get('items', [])

    if not events:
        print('今週の残りの予定はありません。')
    else:
        print('今週の残りの予定:')
        for event in events:
            start = event['start'].get('dateTime', event['start'].get('date'))
            summary = event.get('summary', '(タイトルなし)')
            print(f"{start} - {summary}")

if __name__ == '__main__':
    main()

をモジュール化する

カレンダーの予定を取得する関数を別ファイルとして整理し、他のスクリプトからインポートできるようにする

touch calendar_utils.py

import os
import datetime
import pytz
from google.oauth2.credentials import Credentials
from googleapiclient.discovery import build

# カレンダーAPIのスコープ
SCOPES = ['https://www.googleapis.com/auth/calendar']

def authenticate():
    """Google Calendar APIの認証を行います。"""
    if os.path.exists('token.json'):
        creds = Credentials.from_authorized_user_file('token.json', SCOPES)
        return creds
    else:
        print("トークンファイルが見つかりません。認証を実行してください。")
        return None

def get_upcoming_events(creds, days=7):
    """指定された期間内のGoogleカレンダーの予定を取得します。"""
    # Google Calendar APIサービスを構築
    service = build('calendar', 'v3', credentials=creds)

    # タイムゾーンの設定(日本時間)
    tz = pytz.timezone('Asia/Tokyo')
    now = datetime.datetime.now(tz)

    # 開始日と終了日を計算
    start_of_week = now - datetime.timedelta(days=now.weekday())
    end_of_week = start_of_week + datetime.timedelta(days=days)

    # 開始日時と終了日時を設定
    time_min = now.isoformat()
    time_max = end_of_week.isoformat()

    print(f"{time_min} から {time_max} までの予定を取得します。")

    # イベントを取得
    events_result = service.events().list(
        calendarId='primary',
        timeMin=time_min,
        timeMax=time_max,
        singleEvents=True,
        orderBy='startTime'
    ).execute()
    events = events_result.get('items', [])

    return events

これで
calendar_utils.pyに、カレンダー認証を行うauthenticate関数と、
指定された期間の予定を取得するget_upcoming_events関数を作成

また

def authenticate():
    """Google Calendar APIの認証を行います。"""
    if os.path.exists('token.json'):
        creds = Credentials.from_authorized_user_file('token.json', SCOPES)
        return creds
    else:
        print("トークンファイルが見つかりません。認証を実行してください。")
        return None


Noneを返した場合(トークンがない場合)や、予定がない場合の処理をする

次に
メインスクリプトからモジュールをインポートして使用

touch main_script.py

内容は

from calendar_utils import authenticate, get_upcoming_events

def main():
    creds = authenticate()
    if creds:
        events = get_upcoming_events(creds)
        if not events:
            print('今週の残りの予定はありません。')
        else:
            print('今週の残りの予定:')
            for event in events:
                start = event['start'].get('dateTime', event['start'].get('date'))
                summary = event.get('summary', '(タイトルなし)')
                print(f"{start} - {summary}")

if __name__ == '__main__':
    main()

これで

python main_script.py 
2024-10-11T00:06:41.572834+09:00 から 2024-10-14T00:06:41.572834+09:00 までの予定を取得します。
今週の残りの予定はありません。

というように同じ結果が出ればOK

次に voievox
これは以前作成したプロジェクトの中を参考に行う

まず

ssh -i .ssh/ubuntu22 snowpool@192.168.1.69

でログイン

docker run -d  -p '192.168.1.69:50021:50021' voicevox/voicevox_engine:cpu-ubuntu20.04-lates

で起動

次にコード変更
calendar_utils.pyに音声合成の関数を追加し、カレンダー予定をVoiceVox経由で音声ファイルとして保存

import os
import datetime
import pytz
import requests
from google.oauth2.credentials import Credentials
from googleapiclient.discovery import build

# カレンダーAPIのスコープ
SCOPES = ['https://www.googleapis.com/auth/calendar']
VOICEVOX_API_URL = "http://192.168.1.69:50021"  # VoiceVoxのAPIサーバーURL

def authenticate():
    """Google Calendar APIの認証を行います。"""
    if os.path.exists('token.json'):
        creds = Credentials.from_authorized_user_file('token.json', SCOPES)
        return creds
    else:
        print("トークンファイルが見つかりません。認証を実行してください。")
        return None

def get_upcoming_events(creds, days=7):
    """指定された期間内のGoogleカレンダーの予定を取得します。"""
    service = build('calendar', 'v3', credentials=creds)
    tz = pytz.timezone('Asia/Tokyo')
    now = datetime.datetime.now(tz)
    start_of_week = now - datetime.timedelta(days=now.weekday())
    end_of_week = start_of_week + datetime.timedelta(days=days)
    time_min = now.isoformat()
    time_max = end_of_week.isoformat()

    print(f"{time_min} から {time_max} までの予定を取得します。")

    events_result = service.events().list(
        calendarId='primary',
        timeMin=time_min,
        timeMax=time_max,
        singleEvents=True,
        orderBy='startTime'
    ).execute()
    events = events_result.get('items', [])

    return events

def synthesize_speech(text, speaker=1):
    """VoiceVox APIを使って音声合成を行い、音声ファイルを生成します。"""
    params = {'text': text, 'speaker': speaker}
    response = requests.post(f"{VOICEVOX_API_URL}/audio_query", params=params)
    if response.status_code == 200:
        query_data = response.json()
        synthesis_response = requests.post(f"{VOICEVOX_API_URL}/synthesis", params={'speaker': speaker}, json=query_data)
        if synthesis_response.status_code == 200:
            filename = f"event_voice_{datetime.datetime.now().strftime('%Y%m%d%H%M%S')}.wav"
            with open(filename, "wb") as f:
                f.write(synthesis_response.content)
            print(f"音声ファイルを生成しました: {filename}")
        else:
            print("音声の生成に失敗しました")
    else:
        print("クエリの作成に失敗しました")

次に

カレンダーの予定を取得し、各予定を音声に変換する処理をメインスクリプトに追加

from calendar_utils import authenticate, get_upcoming_events, synthesize_speech

def main():
    creds = authenticate()
    if creds:
        events = get_upcoming_events(creds)
        if not events:
            print('今週の残りの予定はありません。')
            synthesize_speech("今週の残りの予定はありません。")
        else:
            print('今週の残りの予定:')
            for event in events:
                start = event['start'].get('dateTime', event['start'].get('date'))
                summary = event.get('summary', '(タイトルなし)')
                event_text = f"{start} - {summary}"
                print(event_text)
                synthesize_speech(event_text)

if __name__ == '__main__':
    main()

実行すると音声ファイルが作成されるが
2024-10-11 – 診断書の取得
の場合はそのまm数字を読み上げるため
年月日に変換が必要
できれば曜日もほしい

Google Calendar APIでは、曜日自体を直接返す項目はありませんが、
予定の開始日時がISO形式の文字列として返されるので、
これをPythonで処理して曜日を取得することができます。
datetimeオブジェクトを使用すれば、APIから取得した日時を簡単に曜日に変換できます
とのこと

日時を変換する関数を追加し、
YYYY-MM-DD形式の日時を「YYYY年M月D日(曜日)」
のように整形してVoiceVoxで読み上げるようにする

calendar_utils.py

import os
import datetime
import pytz
import requests
from google.oauth2.credentials import Credentials
from googleapiclient.discovery import build

SCOPES = ['https://www.googleapis.com/auth/calendar']
VOICEVOX_API_URL = "http://192.168.1.69:50021"  # VoiceVoxのAPIサーバーURL

def authenticate():
    if os.path.exists('token.json'):
        creds = Credentials.from_authorized_user_file('token.json', SCOPES)
        return creds
    else:
        print("トークンファイルが見つかりません。認証を実行してください。")
        return None

def get_upcoming_events(creds, days=7):
    service = build('calendar', 'v3', credentials=creds)
    tz = pytz.timezone('Asia/Tokyo')
    now = datetime.datetime.now(tz)
    start_of_week = now - datetime.timedelta(days=now.weekday())
    end_of_week = start_of_week + datetime.timedelta(days=days)
    time_min = now.isoformat()
    time_max = end_of_week.isoformat()

    events_result = service.events().list(
        calendarId='primary',
        timeMin=time_min,
        timeMax=time_max,
        singleEvents=True,
        orderBy='startTime'
    ).execute()
    events = events_result.get('items', [])

    return events

def format_date_with_weekday(date_str):
    """ 日付文字列を「YYYY年M月D日(曜日)」形式に変換します """
    date_obj = datetime.datetime.fromisoformat(date_str)
    # 曜日を日本語で取得
    weekday = date_obj.strftime("%A")
    weekday_dict = {
        "Monday": "月",
        "Tuesday": "火",
        "Wednesday": "水",
        "Thursday": "木",
        "Friday": "金",
        "Saturday": "土",
        "Sunday": "日"
    }
    weekday_jp = weekday_dict.get(weekday, weekday)  # 日本語の曜日に変換
    formatted_date = date_obj.strftime(f"%Y年%m月%d日({weekday_jp})")
    return formatted_date

def synthesize_speech(text, speaker=1):
    params = {'text': text, 'speaker': speaker}
    response = requests.post(f"{VOICEVOX_API_URL}/audio_query", params=params)
    if response.status_code == 200:
        query_data = response.json()
        synthesis_response = requests.post(f"{VOICEVOX_API_URL}/synthesis", params={'speaker': speaker}, json=query_data)
        if synthesis_response.status_code == 200:
            filename = f"event_voice_{datetime.datetime.now().strftime('%Y%m%d%H%M%S')}.wav"
            with open(filename, "wb") as f:
                f.write(synthesis_response.content)
            print(f"音声ファイルを生成しました: {filename}")
        else:
            print("音声の生成に失敗しました")
    else:
        print("クエリの作成に失敗しました")

として

main_script.py

from calendar_utils import authenticate, get_upcoming_events, synthesize_speech, format_date_with_weekday

def main():
    creds = authenticate()
    if creds:
        events = get_upcoming_events(creds)
        if not events:
            print('今週の残りの予定はありません。')
            synthesize_speech("今週の残りの予定はありません。")
        else:
            print('今週の残りの予定:')
            for event in events:
                start = event['start'].get('dateTime', event['start'].get('date'))
                summary = event.get('summary', '(タイトルなし)')
                formatted_date = format_date_with_weekday(start)
                event_text = f"{formatted_date} - {summary}"
                print(event_text)
                synthesize_speech(event_text)

if __name__ == '__main__':
    main()

として保存

これで実行すると

2024年10月11日(金) - 診断書の取得

というように目的通りの音声が作成された

次は作成した音声を再生できるようにする
この時に今週の予定をお知らせします
という音声ファイルを再生するようにする

これは以前作成したものを使う

touch create_voice.py

import subprocess
import sys

def generate_and_play_audio_from_text(file_path, server_ip):
    # テキストファイルからテキストを読み込む
    with open(file_path, 'r') as file:
        text = file.read()

    # JSONファイルを作成するためのcurlコマンド
    command_json = [
        "curl", "-s", "-X", "POST",
        f"http://{server_ip}/audio_query?speaker=1",
        "--get", "--data-urlencode", f"text={text}"
    ]

    # 音声ファイルを作成するためのcurlコマンド
    command_audio = [
        "curl", "-s", "-H", "Content-Type: application/json", "-X", "POST",
        "-d", "@query.json", f"http://{server_ip}/synthesis?speaker=1"
    ]

    # JSONファイルと音声ファイルを作成
    with open('query.json', 'w') as file:
        subprocess.run(command_json, stdout=file)
    with open('audio_output.wav', 'wb') as file:
        subprocess.run(command_audio, stdout=file)

    # 音声ファイルを再生
    subprocess.run(["afplay", "audio_output.wav"])

if __name__ == "__main__":
    if len(sys.argv) < 3:
        print("Usage: python script.py <file_path> <server_ip>")
        sys.exit(1)
    
    file_path = sys.argv[1]
    server_ip = sys.argv[2]
    generate_and_play_audio_from_text(file_path, server_ip)

として保存

touch voice.txt 

で中身を

今週の予定をお知らせします

として保存

python create_voice.py voice.txt 192.168.1.69:50021

とすれば

audio_output.wav

が作成される

これをnotice.wavにファイル名を変更する

そしてこれを再生するようにする

生成された音声ファイルを再生するには、
Pythonのsubprocessモジュールやplaysoundライブラリなどを利用する方法がある

pip install playsound

でインストール

一週間分の予定を表示した後、保存した音声ファイルを順番に再生する
音声ファイルのリストを作成: 各イベントの音声ファイルを生成した後、
そのファイル名をaudio_filesリストに追加

予定の表示後にファイルを再生:
audio_filesリストの各ファイルを順番に再生しています。
playsound関数を使って音声ファイルを再生し、全ての音声が順番に再生

これで、カレンダーの予定を表示した後に、順次生成された音声ファイルを再生

残りの予定がない場合に音声が再生されないように、
audio_filesリストにファイルが追加されているか確認する条件を追加します。
audio_filesが空でない場合のみ再生処理を行うように、コードを修正

予定がないときは音声ファイルを再生せず、予定がある場合のみリスト内の音声ファイルを再生

予定があり音声ファイルを再生する前に notice.wav を再生

このためには

calendar_utils.py

を修正する

# 音声合成の関数を修正して、生成されたファイル名を返すようにします
def synthesize_speech(text, speaker=1):
    params = {'text': text, 'speaker': speaker}
    response = requests.post(f"{VOICEVOX_API_URL}/audio_query", params=params)
    if response.status_code == 200:
        query_data = response.json()
        synthesis_response = requests.post(f"{VOICEVOX_API_URL}/synthesis", params={'speaker': speaker}, json=query_data)
        if synthesis_response.status_code == 200:
            filename = f"event_voice_{datetime.datetime.now().strftime('%Y%m%d%H%M%S')}.wav"
            with open(filename, "wb") as f:
                f.write(synthesis_response.content)
            print(f"音声ファイルを生成しました: {filename}")
            return filename  # 生成されたファイル名を返す
        else:
            print("音声の生成に失敗しました")
            return None
    else:
        print("クエリの作成に失敗しました")
        return None

そして

main_script.py

from calendar_utils import authenticate, get_upcoming_events, synthesize_speech, format_date_with_weekday
from playsound import playsound

def main():
    creds = authenticate()
    audio_files = []  # 音声ファイルのリスト
    if creds:
        events = get_upcoming_events(creds)
        if not events:
            print('今週の残りの予定はありません。')
            # 音声ファイルは再生しない
        else:
            print('今週の残りの予定:')
            for event in events:
                start = event['start'].get('dateTime', event['start'].get('date'))
                summary = event.get('summary', '(タイトルなし)')
                formatted_date = format_date_with_weekday(start)
                event_text = f"{formatted_date} - {summary}"
                print(event_text)
                filename = synthesize_speech(event_text)
                if filename:
                    audio_files.append(filename)  # 生成されたファイル名をリストに追加

        # 音声ファイルが存在する場合のみ notice.wav と各予定の音声を再生
        if audio_files:
            # notice.wavを最初に再生
            print("再生中: notice.wav")
            playsound("notice.wav")
            
            # 各予定の音声ファイルを再生
            for audio_file in audio_files:
                print(f"再生中: {audio_file}")
                playsound(audio_file)

if __name__ == '__main__':
    main()

として保存

これで実行すると

今週の予定をお知らせします
2024年10月11日(金) - 診断書の取得

というように読み上げてくれる

テキストファイルからvoicevoxで音声を作成するスクリプト

テキストファイルからvoicevoxで音声を作成するスクリプト

Dockerマシンの voicevox で音声を作成するときにコマンドを毎回実行は面倒
ということで
Python スクリプトにして
テキストファイルを引数にして
音声を作成するようにする

元は

import subprocess
import pygame
import time

def generate_and_play_audio_from_text(file_path):
    # テキストファイルからテキストを読み込む
    with open(file_path, 'r') as file:
        text = file.read()

    # JSONファイルを作成するためのcurlコマンド
    command_json = [
        "curl", "-s", "-X", "POST",
        "http://{server_ip}/audio_query?speaker=1",
        "--get", "--data-urlencode", f"text={text}"
    ]

    # 音声ファイルを作成するためのcurlコマンド
    command_audio = [
        "curl", "-s", "-H", "Content-Type: application/json", "-X", "POST",
        "-d", "@query.json", "http://{server_ip}/synthesis?speaker=1"
    ]

    # JSONファイルと音声ファイルを作成
    with open('query.json', 'w') as file:
        subprocess.run(command_json, stdout=file)
    with open('audio_output.wav', 'wb') as file:
        subprocess.run(command_audio, stdout=file)

    # Pygameで音声ファイルを再生
    pygame.init()
    pygame.mixer.init()
    sound = pygame.mixer.Sound("audio_output.wav")
    sound.play()
    while pygame.mixer.get_busy():
        time.sleep(0.1)

# email_body.txtから音声を生成して再生
generate_and_play_audio_from_text('email_body.txt')

というgmail本文を抽出したテキストファイルを音声にするコードがあったので
これを改造する

create_voice.py

として

import subprocess
import sys

def generate_and_play_audio_from_text(file_path, server_ip):
    # テキストファイルからテキストを読み込む
    with open(file_path, 'r') as file:
        text = file.read()

    # JSONファイルを作成するためのcurlコマンド
    command_json = [
        "curl", "-s", "-X", "POST",
        f"http://{server_ip}/audio_query?speaker=1",
        "--get", "--data-urlencode", f"text={text}"
    ]

    # 音声ファイルを作成するためのcurlコマンド
    command_audio = [
        "curl", "-s", "-H", "Content-Type: application/json", "-X", "POST",
        "-d", "@query.json", f"http://{server_ip}/synthesis?speaker=1"
    ]

    # JSONファイルと音声ファイルを作成
    with open('query.json', 'w') as file:
        subprocess.run(command_json, stdout=file)
    with open('audio_output.wav', 'wb') as file:
        subprocess.run(command_audio, stdout=file)

    # 音声ファイルを再生
    subprocess.run(["afplay", "audio_output.wav"])

if __name__ == "__main__":
    if len(sys.argv) < 3:
        print("Usage: python script.py <file_path> <server_ip>")
        sys.exit(1)
    
    file_path = sys.argv[1]
    server_ip = sys.argv[2]
    generate_and_play_audio_from_text(file_path, server_ip)

として

python script.py example.txt 192.168.1.5

というように実行すればいけるはず

python create_voice.py voice.txt 192.168.1.69:50021

で実行したら
音声が流れて、音声ファイルも作成された

voicevox でお知らせメッセージの作成

voicevox でお知らせメッセージの作成

新着メールがあった時とかに急に読み上げするのではなく

内容は長文のためPDFをご確認ください

特別支援学校からのお知らせがあります

Voicevoxで作成した音声を流すようにするため
音声の作成

これは
curlで作成できるので

まず

docker run -d  -p '192.168.1.69:50021:50021' voicevox/voicevox_engine:cpu-ubuntu20.04-latest

でdockerを起動

次に

vim notice.txt

で内容を
特別支援学校からのお知らせがあります

vim notice_pdf.txt


内容は長文のためPDFをご確認ください

として

curl -s -X POST "192.168.1.69:50021/audio_query?speaker=1" --get --data-urlencode text@notice.txt > query.json

でjsonを作成

このjsonを元に

curl -s -H "Content-Type: application/json" -X POST -d @query.json "192.168.1.69:50021/synthesis?speaker=1" > notice.wav    

で音声を作成

同様に

curl -s -X POST "192.168.1.69:50021/audio_query?speaker=1" --get --data-urlencode text@notice_pdf.txt > query.json

でJSON作成

curl -s -H "Content-Type: application/json" -X POST -d @query.json "192.168.1.69:50021/synthesis?speaker=1" > notice_pdf.wav

で音声を作成

支援学校のPDFをダウンロードし
本文をテキストファイルに保存するプログラムは

main3.py

に記述してある

ダウンロード関連は

pdf_downloader.py

Gmailの認証系は

gmail_utils.py

にまとめてある

また認証関連は

token.json

これらと作成したwavファイルを
mail_voiceディレクトリに移動

これで処理を完成させる

とりあえずPDFとgmail本文の取得した文字列はできているので
あとはテキストファイルの読み上げをvoicevoxとするのと
PDFの本文の長さを取得し

docker のVoicevox で再生できるのは100文字程度だった
それ以上なら
音声ファイルを再生しPDFを閲覧するように促す
このためには文字数のカウントをする

まずはPDFの文字列の長さを取得する

M1MacBookAir でVOICEVOX

Windows/Macに入れたVOICEVOXをPython経由で使う方法

ラズパイ等の非力なPCからPythonやcurlで呼び出し使う方法
が載っていた

残念ながら
NVIDIA製のGPUを持っていないので
とりあえずM1Mac で動かしてみて
できるようならラズパイ4から実行してみる

まずはM1Mac で実行

https://voicevox.hiroshiba.jp/how_to_use/
でダウンロードをクリック

OSの選択をするときにMac にすると
CPUのみの選択になる

パッケージは
インストーラを選択

ダウンロード後に起動しようとしたけど
VOICEVOX”は、開発元を検証できないため開けません。
となるため

https://voicevox.hiroshiba.jp/how_to_use/
を参考に
アップルメニューから「システム環境設定」を選択して「セキュリティとプライバシー」 をクリックしこのまま開くをクリックすることで起動

もしくは
Finder で ctrを押しながら開くことでも可能

使い方の細かい説明は
https://voicevox.hiroshiba.jp/how_to_use/
を参考に

とりあえずインストールはできたので
Voicevox core を使うようにする

Raspberry PiでVOICEVOXに可愛くしゃべってもらおう
では
ラズパイで実行しているけど
M1macで実行してるのを調べたら

VOICEVOX ENGINE(API)のスループット検証

インストール先のディレクトリに移動して、以下のコマンドを実行
とあるので
まずはインストール先ディレクトリを探す

https://voicevox.hiroshiba.jp/qa/
によれば
Mac 版

/Applications/VOICEVOX もしくは /Users/(ユーザー名)/Applications/VOICEVOX

とあったけど
Ls コマンドで調べたら

ls -a /Applications/VOICEVOX.app/Contents/MacOS/

でこの中のディレクトリに
Run が入っていた

cd /Applications/VOICEVOX.app/Contents/MacOS 

で移動し

./run

を実行したけど
VOICEVOXを動かしている状態だとエラーになる

以下エラーログ

Warning: cpu_num_threads is set to 0. ( The library leaves the decision to the synthesis runtime )
INFO:     Started server process [43567]
INFO:     Waiting for application startup.
reading /Users/snowpool/Library/Application Support/voicevox-engine/tmpjvxkxzht ... 64
emitting double-array: 100% |###########################################| 

done!
INFO:     Application startup complete.
ERROR:    [Errno 48] error while attempting to bind on address ('127.0.0.1', 50021): address already in use
INFO:     Waiting for application shutdown.
INFO:     Application shutdown complete.

一度VOICEVOXを終了し再度実行

Warning: cpu_num_threads is set to 0. ( The library leaves the decision to the synthesis runtime )
INFO:     Started server process [43611]
INFO:     Waiting for application startup.
reading /Users/snowpool/Library/Application Support/voicevox-engine/tmpnlpnbr_w ... 64
emitting double-array: 100% |###########################################| 

done!
INFO:     Application startup complete.
INFO:     Uvicorn running on http://127.0.0.1:50021 (Press CTRL+C to quit)
    

となり起動したので
http://127.0.0.1:50021
へアクセスしたけど

{"detail":"Not Found"}

となってしまう

chmod 755 run
./run --host 0.0.0.0

の後に
https://0.0.0.0:50021
でアクセスすると
ページを開けません
0.0.0.0:50021はアドレスが無効なため開けません
となる

よく調べたら
http://127.0.0.1:50021/docs
が正解だった

これでAPIドキュメントが表示される