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?

PythonでgTTS・pydubを活用!中国語・英語のリピート学習音声を1.2倍速MP3で自動生成する

0
Posted at

user:

次のコードを理解してください。生成する音声を、音声部分だけ1.2倍速にしたmp3を出力するコードに変換してください。

# ==============================================================================
# 1. 必要なライブラリのインストール
# ==============================================================================

!pip install -q "gTTS==2.2.4" "pydub==0.25.1" "click>=8.4.2"


# ==============================================================================
# 2. ライブラリ
# ==============================================================================

import os
import time
import shutil
import logging
from datetime import datetime

from gtts import gTTS
from pydub import AudioSegment


# ==============================================================================
# 3. gTTSの警告を抑制
# ==============================================================================

# gTTSが出す
# WARNING:gtts.lang:'zh-TW' has been deprecated...
# というログだけを表示しないようにします。

logging.getLogger("gtts.lang").setLevel(logging.ERROR)


# ==============================================================================
# 4. パッケージバージョン確認
# ==============================================================================

from importlib.metadata import version, PackageNotFoundError


def get_package_version(package_name):
    try:
        return version(package_name)
    except PackageNotFoundError:
        return "不明"


print("======================================")
print("パッケージバージョン")
print("======================================")

print("gTTS  :", get_package_version("gTTS"))
print("pydub :", get_package_version("pydub"))
print("click :", get_package_version("click"))

print("======================================")


# ==============================================================================
# 5. FFmpeg確認
# ==============================================================================

ffmpeg_path = shutil.which("ffmpeg")

if ffmpeg_path is None:

    print("FFmpegが見つかりません。")
    print("FFmpegをインストールします。")

    !apt-get update -qq
    !apt-get install -y -qq ffmpeg

    ffmpeg_path = shutil.which("ffmpeg")


if ffmpeg_path is None:

    raise RuntimeError(
        "FFmpegが見つかりません。"
    )


print(f"FFmpeg: {ffmpeg_path}")


# ==============================================================================
# 6. pydubにFFmpegを設定
# ==============================================================================

AudioSegment.converter = ffmpeg_path

print("pydubのFFmpeg設定完了")


# ==============================================================================
# 7. TTS音声を安全に生成する関数
# ==============================================================================

def create_tts(text, lang, output_path, max_retries=5):

    for attempt in range(1, max_retries + 1):

        try:

            print(
                f"  音声生成: {lang} "
                f"(試行 {attempt}/{max_retries})"
            )

            tts = gTTS(
                text=text,
                lang=lang,
                slow=False
            )

            tts.save(output_path)


            # --------------------------------------------------------------
            # ファイル確認
            # --------------------------------------------------------------

            if os.path.exists(output_path):

                file_size = os.path.getsize(
                    output_path
                )

                if file_size > 1000:

                    print(
                        f"  OK: {output_path} "
                        f"({file_size:,} bytes)"
                    )

                    return True


            raise RuntimeError(
                "音声ファイルが正常に生成されませんでした。"
            )


        except Exception as e:

            print(f"  エラー: {e}")


            if os.path.exists(output_path):
                os.remove(output_path)


            if attempt < max_retries:

                wait_time = attempt * 3

                print(
                    f"  {wait_time}秒待ってから再試行します..."
                )

                time.sleep(wait_time)


            else:

                print(
                    f"  【失敗】{lang}: "
                    f"{max_retries}回試行しましたが失敗しました。"
                )

                return False


    return False


# ==============================================================================
# 8. 一時フォルダ
# ==============================================================================

temp_dir = "temp_audio"


if os.path.exists(temp_dir):
    shutil.rmtree(temp_dir)


os.makedirs(
    temp_dir,
    exist_ok=True
)


print(f"一時フォルダ: {temp_dir}")


# ==============================================================================
# 9. 結合用AudioSegment
# ==============================================================================

combined_audio = AudioSegment.empty()


# ==============================================================================
# 10. 無音時間
# ==============================================================================

silence_short = AudioSegment.silent(
    duration=800
)

silence_long = AudioSegment.silent(
    duration=1500
)


# ==============================================================================
# 11. 音声生成と結合
# ==============================================================================

print()
print(
    "音声の生成と結合を開始します。"
    "しばらくお待ちください..."
)


total = len(sentences_data)


for idx, (zh_text, en_text) in enumerate(
    sentences_data,
    1
):

    print()
    print(f"===== {idx} / {total} =====")


    # ==========================================================================
    # 中国語(台湾)
    # ==========================================================================

    zh_path = os.path.join(
        temp_dir,
        f"zh_{idx}.mp3"
    )


    zh_success = create_tts(
        zh_text,
        "zh-TW",
        zh_path
    )


    if not zh_success:

        raise RuntimeError(
            f"{idx}番目の中国語音声の生成に失敗しました。"
        )


    # ==========================================================================
    # 英語
    # ==========================================================================

    en_path = os.path.join(
        temp_dir,
        f"en_{idx}.mp3"
    )


    en_success = create_tts(
        en_text,
        "en",
        en_path
    )


    if not en_success:

        raise RuntimeError(
            f"{idx}番目の英語音声の生成に失敗しました。"
        )


    # ==========================================================================
    # MP3読み込み
    # ==========================================================================

    audio_zh = AudioSegment.from_mp3(
        zh_path
    )

    audio_en = AudioSegment.from_mp3(
        en_path
    )


    # ==========================================================================
    # 中国語 → 英語 → 中国語 → 英語
    # ==========================================================================

    sentence_block = (
        audio_zh
        + silence_short
        + audio_en
        + silence_short
        + audio_zh
        + silence_short
        + audio_en
    )


    # ==========================================================================
    # 全体に追加
    # ==========================================================================

    if idx == 1:

        combined_audio += sentence_block

    else:

        combined_audio += (
            silence_long
            + sentence_block
        )


    # ==========================================================================
    # 進捗表示
    # ==========================================================================

    if idx % 10 == 0 or idx == total:

        print()
        print(
            f"進捗: {idx} / {total} 文が完了しました。"
        )


# ==============================================================================
# 12. MP3として保存
# ==============================================================================

date_str = datetime.now().strftime(
    "%Y%m%d"
)


output_filename = (
    f"{date_str}_台英リピート学習音声"
    f"{total}選.mp3"
)


print()
print("======================================")
print("すべての音声を結合しています。")
print("MP3ファイルをエクスポート中...")
print("======================================")


combined_audio.export(
    output_filename,
    format="mp3",
    bitrate="128k"
)


# ==============================================================================
# 13. 保存確認
# ==============================================================================

if not os.path.exists(output_filename):

    raise RuntimeError(
        "MP3ファイルの保存に失敗しました。"
    )


output_size = os.path.getsize(
    output_filename
)


print()
print("保存が完了しました。")
print(f"ファイル名: {output_filename}")
print(f"ファイルサイズ: {output_size:,} bytes")


# ==============================================================================
# 14. Google Colabからダウンロード
# ==============================================================================

from google.colab import files


files.download(
    output_filename
)


print()
print("ダウンロードを開始しました。")

ChatGPT:

もちろんです!コード全体の構造を理解しました。

今回の変更では、中国語・英語の音声部分だけをそれぞれ1.2倍速にし、無音時間は変更しないようにします。

つまり、次のようになります。

  • 中国語音声:1.2倍速
  • 英語音声:1.2倍速
  • 中国語と英語の間の無音:800ミリ秒のまま
  • 各文章間の無音:1500ミリ秒のまま
  • 繰り返し構成:中国語 → 英語 → 中国語 → 英語(変更なし)
  • 出力形式:MP3(128kbps)

変更のポイント

pydubでは、再生速度を変更するために、次のような関数を使用できます。

def change_speed(audio, speed=1.2):

    new_frame_rate = int(
        audio.frame_rate * speed
    )

    return audio._spawn(
        audio.raw_data,
        overrides={
            "frame_rate": new_frame_rate
        }
    ).set_frame_rate(
        audio.frame_rate
    )

この方法は音声の再生速度を速くするため、音程も少し高くなります。音程を維持するタイムストレッチ方式ではありません。

修正版コード

以下が、元のコードの構造を維持した修正版です。


# ==============================================================================
# 1. 必要なライブラリのインストール
# ==============================================================================

!pip install -q "gTTS==2.2.4" "pydub==0.25.1" "click>=8.4.2"


# ==============================================================================
# 2. ライブラリ
# ==============================================================================

import os
import time
import shutil
import logging
from datetime import datetime

from gtts import gTTS
from pydub import AudioSegment


# ==============================================================================
# 3. gTTSの警告を抑制
# ==============================================================================

logging.getLogger("gtts.lang").setLevel(logging.ERROR)


# ==============================================================================
# 4. パッケージバージョン確認
# ==============================================================================

from importlib.metadata import version, PackageNotFoundError


def get_package_version(package_name):
    try:
        return version(package_name)
    except PackageNotFoundError:
        return "不明"


print("======================================")
print("パッケージバージョン")
print("======================================")

print("gTTS  :", get_package_version("gTTS"))
print("pydub :", get_package_version("pydub"))
print("click :", get_package_version("click"))

print("======================================")


# ==============================================================================
# 5. FFmpeg確認
# ==============================================================================

ffmpeg_path = shutil.which("ffmpeg")

if ffmpeg_path is None:

    print("FFmpegが見つかりません。")
    print("FFmpegをインストールします。")

    !apt-get update -qq
    !apt-get install -y -qq ffmpeg

    ffmpeg_path = shutil.which("ffmpeg")


if ffmpeg_path is None:

    raise RuntimeError(
        "FFmpegが見つかりません。"
    )


print(f"FFmpeg: {ffmpeg_path}")


# ==============================================================================
# 6. pydubにFFmpegを設定
# ==============================================================================

AudioSegment.converter = ffmpeg_path

print("pydubのFFmpeg設定完了")


# ==============================================================================
# 7. 音声再生速度変更関数
# ==============================================================================

def change_speed(audio, speed=1.2):

    new_frame_rate = int(
        audio.frame_rate * speed
    )

    return audio._spawn(
        audio.raw_data,
        overrides={
            "frame_rate": new_frame_rate
        }
    ).set_frame_rate(
        audio.frame_rate
    )


# ==============================================================================
# 8. TTS音声を安全に生成する関数
# ==============================================================================

def create_tts(text, lang, output_path, max_retries=5):

    for attempt in range(1, max_retries + 1):

        try:

            print(
                f"  音声生成: {lang} "
                f"(試行 {attempt}/{max_retries})"
            )

            tts = gTTS(
                text=text,
                lang=lang,
                slow=False
            )

            tts.save(output_path)

            # --------------------------------------------------------------
            # ファイル確認
            # --------------------------------------------------------------

            if os.path.exists(output_path):

                file_size = os.path.getsize(
                    output_path
                )

                if file_size > 1000:

                    print(
                        f"  OK: {output_path} "
                        f"({file_size:,} bytes)"
                    )

                    return True

            raise RuntimeError(
                "音声ファイルが正常に生成されませんでした。"
            )

        except Exception as e:

            print(f"  エラー: {e}")

            if os.path.exists(output_path):
                os.remove(output_path)

            if attempt < max_retries:

                wait_time = attempt * 3

                print(
                    f"  {wait_time}秒待ってから再試行します..."
                )

                time.sleep(wait_time)

            else:

                print(
                    f"  【失敗】{lang}: "
                    f"{max_retries}回試行しましたが失敗しました。"
                )

                return False

    return False


# ==============================================================================
# 9. 一時フォルダ
# ==============================================================================

temp_dir = "temp_audio"

if os.path.exists(temp_dir):
    shutil.rmtree(temp_dir)

os.makedirs(
    temp_dir,
    exist_ok=True
)

print(f"一時フォルダ: {temp_dir}")


# ==============================================================================
# 10. 結合用AudioSegment
# ==============================================================================

combined_audio = AudioSegment.empty()


# ==============================================================================
# 11. 無音時間
# ==============================================================================

silence_short = AudioSegment.silent(
    duration=800
)

silence_long = AudioSegment.silent(
    duration=1500
)


# ==============================================================================
# 12. 音声再生速度設定
# ==============================================================================

AUDIO_SPEED = 1.2

print(
    f"音声再生速度: {AUDIO_SPEED}倍速"
)


# ==============================================================================
# 13. 音声生成と結合
# ==============================================================================

print()
print(
    "音声の生成と結合を開始します。"
    "しばらくお待ちください..."
)

total = len(sentences_data)

for idx, (zh_text, en_text) in enumerate(
    sentences_data,
    1
):

    print()
    print(f"===== {idx} / {total} =====")


    # ==========================================================================
    # 中国語(台湾)
    # ==========================================================================

    zh_path = os.path.join(
        temp_dir,
        f"zh_{idx}.mp3"
    )

    zh_success = create_tts(
        zh_text,
        "zh-TW",
        zh_path
    )

    if not zh_success:

        raise RuntimeError(
            f"{idx}番目の中国語音声の生成に失敗しました。"
        )


    # ==========================================================================
    # 英語
    # ==========================================================================

    en_path = os.path.join(
        temp_dir,
        f"en_{idx}.mp3"
    )

    en_success = create_tts(
        en_text,
        "en",
        en_path
    )

    if not en_success:

        raise RuntimeError(
            f"{idx}番目の英語音声の生成に失敗しました。"
        )


    # ==========================================================================
    # MP3読み込み
    # ==========================================================================

    audio_zh = AudioSegment.from_mp3(
        zh_path
    )

    audio_en = AudioSegment.from_mp3(
        en_path
    )


    # ==========================================================================
    # 音声部分だけを1.2倍速に変更
    # ==========================================================================

    audio_zh = change_speed(
        audio_zh,
        AUDIO_SPEED
    )

    audio_en = change_speed(
        audio_en,
        AUDIO_SPEED
    )


    # ==========================================================================
    # 中国語 → 英語 → 中国語 → 英語
    # 無音部分は速度変更しない
    # ==========================================================================

    sentence_block = (
        audio_zh
        + silence_short
        + audio_en
        + silence_short
        + audio_zh
        + silence_short
        + audio_en
    )


    # ==========================================================================
    # 全体に追加
    # ==========================================================================

    if idx == 1:

        combined_audio += sentence_block

    else:

        combined_audio += (
            silence_long
            + sentence_block
        )


    # ==========================================================================
    # 進捗表示
    # ==========================================================================

    if idx % 10 == 0 or idx == total:

        print()
        print(
            f"進捗: {idx} / {total} 文が完了しました。"
        )


# ==============================================================================
# 14. MP3として保存
# ==============================================================================

date_str = datetime.now().strftime(
    "%Y%m%d"
)

output_filename = (
    f"{date_str}_台英リピート学習音声"
    f"{total}選_1.2倍速.mp3"
)


print()
print("======================================")
print("すべての音声を結合しています。")
print("MP3ファイルをエクスポート中...")
print("======================================")


combined_audio.export(
    output_filename,
    format="mp3",
    bitrate="128k"
)


# ==============================================================================
# 15. 保存確認
# ==============================================================================

if not os.path.exists(output_filename):

    raise RuntimeError(
        "MP3ファイルの保存に失敗しました。"
    )

output_size = os.path.getsize(
    output_filename
)

print()
print("保存が完了しました。")
print(f"ファイル名: {output_filename}")
print(f"ファイルサイズ: {output_size:,} bytes")


# ==============================================================================
# 16. Google Colabからダウンロード
# ==============================================================================

from google.colab import files

files.download(
    output_filename
)

print()
print("ダウンロードを開始しました。")

補足: 1.2倍速にすると、音声部分の再生時間は元の約83.3%になります。無音時間を短縮しないため、学習者が次の文章に備える時間は確保できます。

今回の変更なら、元のコードの音声生成・リトライ・結合・ダウンロードの仕組みをそのまま活用できます。

user:

ありがとうございます。qiita記事として投稿したいのでタイトルとまとめを書いてください。

ChatGPT:

今回の内容なら、単なるMP3の高速化ではなく、Pythonによる多言語リピート学習音声の生成と、音声部分だけを1.2倍速にする工夫をテーマにすると、Qiitaの記事として分かりやすいと思います。

おすすめタイトル

PythonでgTTS・pydubを活用!中国語・英語のリピート学習音声を1.2倍速MP3で自動生成する

記事のまとめ

まとめ

今回は、Pythonの gTTS・pydub・FFmpeg を活用して、中国語(台湾)と英語のリピート学習用音声を自動生成し、音声部分だけを1.2倍速にしたMP3ファイルとして出力する仕組みを構築しました。

従来のプログラムでは、gTTSで生成した中国語と英語の音声を、

  • 中国語
  • 英語
  • 中国語
  • 英語

という順番で繰り返し再生する音声ファイルを作成していました。

今回は、この仕組みに再生速度の変更機能を追加しました。

今回の主な変更点

  1. 中国語・英語の音声を1.2倍速に変更
    • pydubのAudioSegmentを利用して再生速度を調整。
    • 中国語と英語の両方に同じ速度設定を適用。
  2. 無音時間は変更しない
    • 音声と音声の間の無音時間は800ミリ秒。
    • 各文章の間の無音時間は1500ミリ秒。
    • 学習者が次の音声を聞く準備をする時間を確保。
  3. MP3ファイルとして出力
    • FFmpegを利用してMP3形式にエクスポート。
    • ビットレートは128kbps。
    • ファイル名には生成日・文章数・1.2倍速の情報を付与。
  4. 既存の安全対策を維持
    • gTTSの音声生成に最大5回のリトライを設定。
    • 生成ファイルの存在とファイルサイズを確認。
    • FFmpegの存在確認と自動インストールに対応。

再生速度変更のポイント

今回の実装では、音声データのフレームレートを一時的に変更し、元のフレームレートに戻すことで再生速度を調整しています。

これにより、音声部分だけを高速化し、無音部分の長さはそのまま維持できます。

なお、この方式では再生速度に応じて音程も高くなります。音程を維持したまま高速化する方式とは異なります。

今回の取り組みを通じて

外国語のリスニング学習では、同じ文章を何度も聞くことが効果的です。

一方、通常速度では学習に時間がかかる場合もあります。

今回のようにPythonで音声生成・結合・速度調整を自動化すれば、学習者のレベルや目的に合わせたオリジナルのリスニング教材を効率よく作成できます。

今後は、1.0倍速・1.2倍速・1.5倍速など、複数の再生速度を選択できる仕組みに発展させることも考えられます。

Pythonを活用することで、語学学習用の音声教材も柔軟にカスタマイズできることを確認できました。

個人的なおすすめポイント: 今回の記事では「無音時間を維持したまま音声だけを高速化する」という点を強調すると、単純なMP3変換記事との差別化になります。

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?