adbird(広告鳥) 備忘録

Open JTalkで長文を読み上げさせる(Ubuntuで)

※追記:ウェブサイト読み上げは、拡張機能Read Aloud: テキスト読み上げ音声リーダー を入れればOKだった…。


長いニュース記事などを読み上げてくれないかと試行錯誤した結果、以下の方法で、読み上げさせることができた。

下準備

Open JTalk をインストール

sudo apt install open-jtalk open-jtalk-mecab-naist-jdic hts-voice-nitech-jp-atr503-m001

ボイスデータをダウンロード

上記でダウンロードしたデフォルトのボイスデータ(nitech-jp-atr503-m001/nitech_jp_atr503_m001.htsvoice)では、長文を読ませると、ものすごく間延びするエラーが発生することがあるので、それ以外のボイスデータをダウンロードする。

以下のリンク先からzipファイル(2025年6月10日現在の最新は、MMDAgent_Example-1.8.zip)をダウンロードしてくる。

MMDAgent - Browse /MMDAgent_Example at SourceForge.net

zipファイルを展開したら、その中にある Voice ディレクトリを適当なところに置く。

pythonスクリプト(読み上げ.py)を作成する

ChatGPT曰く、Open JTalkのソースコードでは、バッファ長(MAXBUFLEN)が1024バイトに固定されているらしく、長文を読み上げさせると、途中で終わってしまう。

そこで、ChatGPTに長文を読み上げるスクリプトを作ってもらった。以下を、「読み上げ.py」として保存(端末を立ち上げて、すぐにスクリプトを実行させたいので、ホームディレクトリ(「/home/<ユーザ名>」)に保存するとよい。)

「# ボイスデータを指定」、「# 入力テキストファイル」、「# 入力テキストファイルのバックアップ(1度だけ)」、「# mpvで再生。デフォルトのボリューム調整。」の行を適宜、修正すること。

import subprocess
import os
import re
import textwrap
import sys
import shutil

# ========== 設定 ==========
OPEN_JTALK_BIN = '/usr/bin/open_jtalk'
DICT_PATH = '/var/lib/mecab/dic/open-jtalk/naist-jdic'
VOICE_PATH = '/home/your_username/ドキュメント/Voice/mei/mei_normal.htsvoice' # ボイスデータを指定
TMP_WAV = 'tmp.wav'
MAX_CHARS = 300
INPUT_ORIGINAL = '/home/your_username/ダウンロード/input.txt'  # 入力テキストファイル
INPUT_BACKUP = '/home/your_username/ダウンロード/input_backup.txt'  # 入力テキストファイルのバックアップ(1度だけ)
INPUT_FILE = 'tmp.txt' # 改行除去後の一時ファイル

# ========== 改行をスペースに置換してtmp.txtに保存 ==========
def preprocess_input_file():
    with open(INPUT_ORIGINAL, "r", encoding="utf-8") as infile:
        content = infile.read().replace('\n', ' ')
    with open(INPUT_FILE, "w", encoding="utf-8") as outfile:
        outfile.write(content)

# ========== 前処理 ==========
def clean_text(text):
    text = re.sub(r'[()【】[]\[\]{}<>「」『』“”]', '', text)
    text = re.sub(r'[^\wぁ-んァ-ン一-龯。、!?\s]', '', text)
    return text

def split_text(text, max_chars=MAX_CHARS):
    sentences = re.split(r'(?<=[。!?])', text)
    segments = []
    current = ''
    for sentence in sentences:
        if len(current) + len(sentence) < max_chars:
            current += sentence
        else:
            if current:
                segments.append(current.strip())
            current = sentence
    if current:
        segments.append(current.strip())
    return segments

# ========== 音声合成と再生 ==========
def tts_openjtalk(text, out_path=TMP_WAV):
    cmd = [
        OPEN_JTALK_BIN,
        '-x', DICT_PATH,
        '-m', VOICE_PATH,
        '-ow', out_path
    ]
    process = subprocess.Popen(cmd, stdin=subprocess.PIPE)
    process.communicate(text.encode('utf-8'))

def play_wav_mpv(file_path):
    subprocess.run(['mpv', '--volume=80', file_path])# mpvで再生。デフォルトのボリューム調整。

def read_long_text(text):
    cleaned = clean_text(text)
    segments = split_text(cleaned)
    for i, segment in enumerate(segments):
        print(f"\n▶️ Part {i+1}/{len(segments)} 再生中...")
        tts_openjtalk(segment)
        play_wav_mpv(TMP_WAV)

# ========== メイン処理 ==========
if __name__ == '__main__':
    preprocess_input_file()

    if not os.path.exists(INPUT_FILE):
        print(f"❌ 中間ファイル {INPUT_FILE} が見つかりません。")
        sys.exit(1)

    with open(INPUT_FILE, "r", encoding="utf-8") as f:
        input_text = f.read()

    read_long_text(input_text)
    print("\n✅ 読み上げ完了!")

# ===== 一時ファイル削除 =====
    for tmp_file in [INPUT_FILE, TMP_WAV]:
        try:
            if os.path.exists(tmp_file):
                os.remove(tmp_file)
                print(f"🗑️ {tmp_file} を削除しました。")
        except Exception as e:
            print(f"⚠️ {tmp_file} の削除に失敗しました: {e}")

# ===== input.txtを削除前にバックアップ(バックアップファイルがあった場合、上書き保存される) =====
# コピー元ファイルとコピー先パス
src_path = INPUT_ORIGINAL
dst_path = INPUT_BACKUP

# ファイルをコピー
shutil.copy2(src_path, dst_path)

# ===== input.txt削除 =====
os.remove(INPUT_ORIGINAL)
print(f"🗑️ input.txt を削除しました。")

読み上げるテキストを準備

読み上げるテキストを input.txt として保存(保存先はスクリプトの「# 入力テキストファイル」の行で指定された場所)。

webサイトの文章から input.txt を生成するには、ブラウザで選択した文章をテキストファイルにするブックマークレット - adbird(広告鳥) 備忘録 が便利。

読み上げ

端末を立ち上げて、以下を実行。

python3 読み上げ.py

スクリプト上でmpvのデフォルトのボリューム調整ができるが、いきなり大きな音量で再生されるかもしれないので、事前にシステムのボリュームを下げておいたほうが良いかもしれない。

逆に音量が小さすぎる場合は、長い文章を読み上げさせている間に、 設定>サウンドサウンド>Volume Levels>mpv で音量を調整するといい。

また、上記スクリプトでは自動的に input.txt を削除するようにしているので(一応、backupファイルをコピーするが、すでにbackupファイルがあったばあいは上書き保存されるので注意)、input.txt を削除したくないときは、最後の2行をコメントアウトする。

webサイトの文章を読み上げさせる

ブラウザで選択した文章をテキストファイルにするブックマークレット - adbird(広告鳥) 備忘録 と組み合わせると、3アクションで読み上げてくれる。

  1. (ニュース記事など)読み上げさせたいテキストをブラウザ上で選択。
  2. テキストが選択されている状態で、「input.txt出力」のブックマークをクリックすると、input.txt がダウンロードされる。
  3. 端末を開いて、python3 読み上げ.pyを実行。

参考URL