0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

f22: CUI (CLI) + 低メモリで音声合成 (TTS) してみる (vps + voicevox core)

Last updated at Posted at 2025-10-18

.

ずんだもんな voicevox core で、
python + TTS ができるということで
お安め CUI (CLI) 環境で音声合成が可能か試してみました

今さらのネタですが、検証記録をかねて記載します

具体的な環境構築方法につきましては
いっぱい記事が出てきますのでここでは割愛します

※補足:CUI は和製英語で、CLI と同義ではありませんが、
 GUI ではない OS で、CLI を用いた環境ということで、
 CUI (CLI) と記載してます


[ 01. キッチン ]

 xserver vps
 ・Xserver VPS / 2GB プラン
  -> Ubuntu 22.04 (64bit)

 voicevox_core
 ・ver. 0.14.4
  (voicevox_core-0.14.4+cpu-cp38-abi3-linux_x86_64.whl)
  -> CPU mode (accelemodes): CPU

 ONNX RUNTIME
 ・libonnxruntime.so.1.13.1 (スクリプト直下に必要)

 Open JTalk の辞書
 ・open_jtalk_dic_utf_8-1.11

 pip
 ・voicevox_core: 0.14.4+cpu
 ・pydub: 0.25.1


[ 02. 結論 ]

メモリ 2GB、キツイです、厳しいです

例えば、

 7月14日 08時45分 各地で強い雨 交通機関に影響
 7月15日 09時10分 家電メーカーが新型掃除機を発表 省エネ性能を強調
 7月16日 11時25分 国際会議が開催 温室効果ガス削減を主要議題に
 7月17日 11時40分 都市部で停電 一時的に数万戸が影響受ける
 7月18日 13時05分 プロサッカーの試合で新戦術が注目集める
 7月19日 13時20分 新しい交通アプリが公開 利便性向上に期待
 7月20日 15時30分 国内の観光地で入場者数が過去最高を更新
 7月21日 15時55分 医療研究機関が新しい治療法の成果を発表
 7月22日 18時10分 株式市場が大きく変動 投資家に警戒感広がる
 7月23日 18時35分 新しい教育カリキュラム導入へ 来年度から実施予定

という 10 行のテキストがあったとして、

 10行をまとめての一括処理では、killed レスポンスになる (メモリ枯渇による強制終了)
 5行でも、killed レスポンスになる (メモリ枯渇による強制終了)
 2行で、ギリ音声ファイルとしての生成は可能

という状況です

ですので、苦肉で、

 textb1 =
 '7月14日 08時45分 各地で強い雨 交通機関に影響。' +
 '7月15日 09時10分 家電メーカーが新型掃除機を発表 省エネ性能を強調。'

 textb2 =
 '7月16日 11時25分 国際会議が開催 温室効果ガス削減を主要議題に。' +
 '7月17日 11時40分 都市部で停電 一時的に数万戸が影響受ける。'

のように、2行ずつで、wav ファイル生成
-> 全 wav ファイルを生成後、結合
-> mp3 に 1ファイルとして保存

ということで回避してます


[ 03. 実際の実行 ]

・実行スクリプトは、voicevox core
 サンプルスクリプト (run.py) のカスタマイズです

core-test.py

# -*- coding: utf-8 -*-

import sys, os, re, shutil
import time, datetime

from pathlib import Path, PurePosixPath
import gc

import voicevox_core
from voicevox_core import AccelerationMode, AudioQuery, VoicevoxCore

from pydub import AudioSegment

# ===================================

now1 = datetime.datetime.now()
now2 = '{0:%Y%m%d-%H%M%S}'.format(now1)

dir1 = os.path.dirname(os.path.abspath(__file__))

# ===================================
def mains() -> None:

  global dir1

  accelemodes = 'CPU'

  # (VOICEVOX:2 -> 四国めたん)
  speakids = 2

  # jtalk 辞書 設定
  dir2 = os.path.join(dir1, 'open_jtalk_dic_utf_8-1.11')

  # wav / mp3 保存先
  dir3 = os.path.join(dir1, 'outQ/')

  textb1 = \
  '7月14日 08時45分 各地で強い雨 交通機関に影響。' + \
  '7月15日 09時10分 家電メーカーが新型掃除機を発表 省エネ性能を強調。'

  textb2 = \
  '7月16日 11時25分 国際会議が開催 温室効果ガス削減を主要議題に。' + \
  '7月17日 11時40分 都市部で停電 一時的に数万戸が影響受ける。'

  textb3 = \
  '7月18日 13時05分 プロサッカーの試合で新戦術が注目集める。' + \
  '7月19日 13時20分 新しい交通アプリが公開 利便性向上に期待。'

  textb4 = \
  '7月20日 15時30分 国内の観光地で入場者数が過去最高を更新。' + \
  '7月21日 15時55分 医療研究機関が新しい治療法の成果を発表。'

  textb5 = \
  '7月22日 18時10分 株式市場が大きく変動 投資家に警戒感広がる。' + \
  '7月23日 18時35分 新しい教育カリキュラム導入へ 来年度から実施予定。'

  # 明示的に辞書にまとめる
  textsG = {
    1: textb1,
    2: textb2,
    3: textb3,
    4: textb4,
    5: textb5
  }

  print('')
  print('wav 生成開始:')

  # 2行ずつ wav に生成
  for nn1 in range(1, 6):

    # textbX ごとに処理
    textb = textsG[nn1]

    # audioXX.wav を設定
    fname = f"audio{nn1:02d}.wav"

    # 生成処理 =============================

    outb = Path(dir3 + fname)
    core = VoicevoxCore(acceleration_mode=accelemodes, open_jtalk_dict_dir=dir2)
    core.load_model(speakids)
    audio_query = core.audio_query(textb, speakids)

    # 音量を2倍にする
    audio_query.volume_scale = 2.0

    wavb = core.synthesis(audio_query, speakids)
    outb.write_bytes(wavb)
    print('wav 生成: ' + fname)

    # 生成処理 =============================

    time.sleep(1)

    # --- メモリ解放処理 (念のため) --- 
    del wavb
    del audio_query
    del core
    gc.collect()

    time.sleep(1)


  # 出力 wav ファイルの結合準備
  file_names = [f"audio{nn1:02d}.wav" for nn1 in range(1, 6)]

  # 増幅dB
  amplify_db = 2

  silence_duration = 500 # 無音: 0.5秒(ミリ秒)
  silence = AudioSegment.silent(duration=silence_duration)

  audio_segments = []

  print('wav 編集開始:')

  # 各ファイルを読み込み -> 増幅/無音 処理
  for idx, fnzq in enumerate(file_names):

    pathpo = os.path.join(dir3, fnzq)
    audio34 = AudioSegment.from_wav(pathpo)
    audio34 = audio34 + amplify_db

    audio_segments.append(audio34)
    # 最終ファイル以外は0.5秒無音を追加
    if idx < len(file_names) - 1:
      audio_segments.append(silence)

  # ファイル結合
  combined = sum(audio_segments)

  # MP3で保存(変換)
  out_mp3 = os.path.join(dir3, now2 + ".mp3")
  combined.export(out_mp3, format="mp3")

  print('wav 結合 -> mp3 生成: ' + now2 +'.mp3')
  print('')

# ===================================
if __name__ == "__main__":

  mains()

  time.sleep(1)

  sys.exit(0)

# ===================================

$ python3 core-test.py
すると、
・wav x5 が生成され
・各 wav の編集を行い、
・wav x5 を 1 ファイルにして、mp3 で保存
という流れです

 wav 生成開始:
 wav 生成: audio01.wav
 wav 生成: audio02.wav
 wav 生成: audio03.wav
 wav 生成: audio04.wav
 wav 生成: audio05.wav
 wav 編集開始:
 wav 結合 -> mp3 生成: 20251018-181719.mp3


[ 04. クレジット表記について ]

・今回のテスト生成とは関係ないのですが、
 どこかに展開する場合は、以下注意してください

 「クレジット表記について」
  VOICEVOX の音声を公開・配布する際は、
  以下のクレジットを必ず記載してください
  例: VOICEVOX:四国めたん


[ 05. デザート]

結論に書いておりますが、結構むりくり感です

20行(2行ずつ処理 x 10 wav ファイル -> 1 mp3 ファイルに結合)
を 1処理として、cron で、数時間おきに実行してみてますが、
今のところ、処理落ち (killed) は、ないようですが、
メモリ専有できるわけではないので、他の処理が走ると落ちる可能性はあります
運用にのせるには、この環境はあまり現実的ではないですね

用途の規模感にもよりますが、音声合成をするなら、
例えば xserver vps でいうなら
最低でも、上位の 6GB プラン は欲しいところです


.

0
0
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
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?