機械学習
記事内に商品プロモーションを含む場合があります

Qwen3-TTSの日本語ボイスクローンをMacで動かす【Python】

Aru

Text to Speechのモデル「Qwen3-TTS」が登場したので試してみました。このモデル、自分の声を数秒の音声で複製(ボイスクローン)できるというのが特徴です。今回は、ボイスクローンを実際にPythonコードでやってみましたので、サンプルコードを紹介します。

Qwen3-TTSとは、

Qwen3-TTSは、Alibaba CloudのQwenが公開したオープンソースのText to Speech(Text2Speech)モデルです(2026年1月22日公開)

このブログではTTSモデルをいくつか紹介してきましたが、このモデルは、①商用利用可能、②自身の音声を複製(ボイスクローン)できることが大きな特徴です。

対応言語に「日本語」も含まれているため、早速日本語でのボイスクローンに挑戦してみました。

この記事では、MacOS環境でPythonから利用する方法を紹介します。自分でプログラミングして使いたい方の参考になれば幸いです。

このブログのコードは以下にあります

https://github.com/aruaru0/Qwen3-TTS-test

インストール

最初にインストールを行います。Python環境にuvを使っている場合は、以下のコマンドで仮想環境を作っておきます。uvのインストール方法は以下のブログ記事にあります。

あわせて読みたい
Macで開発環境関連の初期設定する手順(VSCode, brew, iterm2, python, go)
Macで開発環境関連の初期設定する手順(VSCode, brew, iterm2, python, go)

仮想環境を作りたいフォルダで、以下のコマンドを実行します。

uv venv

初期化が完了すると .venv フォルダが作成されます。以下のコマンドで仮想環境を有効化し、qwen-ttsをインストールします。

source .venv/bin/activate
uv pip install qwen-tts  

qwen3-ttsではsoxを必要としますので、もしインストールしていない場合はhomebrewでインストールしておきます。homebrewのインストール方法は以下の記事を参考にしてください。

Macで開発環境関連の初期設定する手順(VSCode, brew, iterm2, python, go)
Macで開発環境関連の初期設定する手順(VSCode, brew, iterm2, python, go)

インストール後は、以下のコマンドでインストールできます。

brew install sox

環境構築まとめ

任意のフォルダを作成後、以下のコマンドを実行

uv venv
source .venv/bin/activate
uv pip install qwen-tts

基本的な使い方

英語音声

PythonからQwen3-TTSを使うのは非常に簡単です。

このプログラムを実行するとtest01.wav として音声ファイルが生成されます。

import torch
import soundfile as sf
from qwen_tts import Qwen3TTSModel

model = Qwen3TTSModel.from_pretrained(
    "Qwen/Qwen3-TTS-12Hz-1.7B-CustomVoice",
    device_map="mps",
    dtype=torch.bfloat16,
)

wavs, sr = model.generate_custom_voice(
    text="Hello, how are you?",
    language="English",
    speaker="Ryan",
)
sf.write("test01.wav", wavs[0], sr)

用意された声を使う場合、Qwen/Qwen3-TTS-12Hz-1.7B-CustomVoiceをモデルとして設定します。Macの場合、device_map="mps"にします。

modelのインスタンスを作成したら、あとは、generate_custom_voiceで喋らせたいテキストを指定します。

この例では、言語を英語にしています。公式にありますが、英語の発話モデルの話者はRyanという名前みたいです。

このプログラムを実行すると、test00.wavとして音声ファイルができます。

日本語音声

日本語音声の場合のプログラムも先ほどとほぼ同じです。

import torch
import soundfile as sf
from qwen_tts import Qwen3TTSModel

model = Qwen3TTSModel.from_pretrained(
    "Qwen/Qwen3-TTS-12Hz-1.7B-CustomVoice",
    device_map="mps",
    dtype=torch.bfloat16,
)

wavs, sr = model.generate_custom_voice(
    text="こんにちは、私になにか御用ですか?",
    language="Japanese",
    speaker="Ono_Anna",
)
sf.write("test02.wav", wavs[0], sr)

違いは、language="Japanese" を指定する点です。日本語話者は Ono_Anna が用意されています。

再生して聞いてみた感じ、なんかアクセントが少し変です・・・・

ボイスクローン

ここからメインのボイスクローンの方法を説明します。

ボイスクローンでは、録音したmp3, wavファイルが必要です。今回は、「あみたろの音声素材工房」の音声を使わせていただきました。

あみたろの音声素材工房: https://amitaro.net

ボイスクローン①

ボイスクローンを行うには、「参照する音声ファイル」と「音声ファイルの話している内容を書いたテキストファイル」の2つが必要です。秒数は60秒までと書かれていましたが、数秒あればそれなりに似たボイスを作れるようです。

import torch
import soundfile as sf
from qwen_tts import Qwen3TTSModel

model = Qwen3TTSModel.from_pretrained(
    "Qwen/Qwen3-TTS-12Hz-1.7B-Base",
    device_map="mps",
    dtype=torch.bfloat16,
)

ref_audio = "https://amitaro.net/download/voice/111_ganbaru/anmarimuzukashiku_01.wav"
ref_text  = "あんまりむずかしく考えすぎるといろいろ大変だしね、しっかり反省したらぱぱっと行こう!"

wavs, sr = model.generate_voice_clone(
    text="大丈夫だよ、これから何度だって挑戦すればいいんだから",
    language="Japanese",
    ref_audio=ref_audio,
    ref_text=ref_text,
    instruct="As the news anchor says"
)
sf.write("test03.wav", wavs[0], sr)

ボイスクローンを行う場合、モデルはQwen/Qwen3-TTS-12Hz-1.7B-Baseになります。

ref_audioは、ローカルにあるファイルだけでなく、URL指定もできました。

モデルのインスタンス作成後、generate_voice_clone関数を呼び出すことで音声を生成できます。

instruct引数は、いろいろな指示が与えられるようです(日本語もOK)。ここでは、アナウンサーのような喋り方を指定しましたが、うまく機能しているかよくわかりませんでした。

ボイスクローン②

ボイスクローンをする2つめの方法です。

あらかじめ音声をしておき、その後クローンを作るという方法です。繰り返しテキストを渡して音声を作る場合などは、こちらの方法が推奨されています。

import torch
import soundfile as sf
from qwen_tts import Qwen3TTSModel

model = Qwen3TTSModel.from_pretrained(
    "Qwen/Qwen3-TTS-12Hz-1.7B-Base",
    device_map="mps",
    dtype=torch.bfloat16,
)

ref_audio = "https://amitaro.net/download/voice/111_ganbaru/anmarimuzukashiku_01.wav"
ref_text  = "あんまりむずかしく考えすぎるといろいろ大変だしね、しっかり反省したらぱぱっと行こう!"


voice_clone_prompt = model.create_voice_clone_prompt(
    ref_audio=ref_audio,
    ref_text=ref_text,
)

wavs, sr = model.generate_voice_clone(
    text="""
    本日の天気は、全国的に概ね晴れとなる見込みです。
    しかし、午後は大気の状態が不安定になるため、急な雨や雷にご注意ください。
    お出かけの際は、折りたたみ傘を持っていくと安心でしょう。
    
    以上、気象情報をお伝えしました。
    """,
    language="Japanese",
    voice_clone_prompt=voice_clone_prompt,
    instruct="女性。標準的な日本語のアクセントで、落ち着いて発音。ゆっくり話す。",
)
sf.write("test04.wav", wavs[0], sr)

先ほどと異なるのは、create_voice_clone_promptであらかじめ、参照する音声とテキストを登録していることです。

今回は少し長めのテキストを話させてみました。instructも細かく指定してみましたが、なんとなく機能しているようにも聞こえます。

ただ、「雷」がおかしかったり、読み方を間違うパターンもあるみたいです。この場合はひらがなにすれば良さそうです。

処理時間の計測

①と②の手法で速度が異なるか時間計測してみました。測定につかったMacは以下のスペックです。

MacBook Pro : M4Max 128GB

以下は計測に使用したプログラムです。

import torch
import soundfile as sf
from qwen_tts import Qwen3TTSModel
import time
from functools import wraps

def timer(func):
    @wraps(func)  # 元の関数のメタデータを保持
    def wrapper(*args, **kwargs):
        start = time.perf_counter()  # 計測開始
        result = func(*args, **kwargs)
        end = time.perf_counter()    # 計測終了
        print(f"実行時間 ({func.__name__}): {end - start:.4f} 秒")
        return result
    return wrapper

model = Qwen3TTSModel.from_pretrained(
    "Qwen/Qwen3-TTS-12Hz-1.7B-Base",
    device_map="mps",
    dtype=torch.bfloat16,
)

ref_audio = "https://amitaro.net/download/voice/111_ganbaru/anmarimuzukashiku_01.wav"
ref_text  = "あんまりむずかしく考えすぎるといろいろ大変だしね、しっかり反省したらぱぱっと行こう!"


@timer
def method1():
    wavs, sr = model.generate_voice_clone(
        text="大丈夫だよ、これから何度だって挑戦すればいいんだから",
        language="Japanese",
        ref_audio=ref_audio,
        ref_text=ref_text,
    )

@timer
def method2_1():
    voice_clone_prompt = model.create_voice_clone_prompt(
        ref_audio=ref_audio,
        ref_text=ref_text,
    )
    return voice_clone_prompt

@timer
def method2_2():
    global voice_clone_prompt
    wavs, sr = model.generate_voice_clone(
        text="大丈夫だよ、これから何度だって挑戦すればいいんだから",
        language="Japanese",
        voice_clone_prompt=voice_clone_prompt,
    )

for _ in range(10):
    method1()

voice_clone_prompt = method2_1()

for _ in range(10):
    method2_2()

計測結果を見ると、②の方が若干速度が上がっている気はします。

実行結果
Fetching 4 files: 100%|████████████████████████████████████████████████████████████████████████████████████████████| 4/4 [00:00<00:00, 21760.33it/s]
Setting `pad_token_id` to `eos_token_id`:2150 for open-end generation.
実行時間 (method1): 12.5603 秒
Setting `pad_token_id` to `eos_token_id`:2150 for open-end generation.
実行時間 (method1): 10.2508 秒
Setting `pad_token_id` to `eos_token_id`:2150 for open-end generation.
実行時間 (method1): 9.3695 秒
Setting `pad_token_id` to `eos_token_id`:2150 for open-end generation.
実行時間 (method1): 10.6178 秒
Setting `pad_token_id` to `eos_token_id`:2150 for open-end generation.
実行時間 (method1): 10.7287 秒
Setting `pad_token_id` to `eos_token_id`:2150 for open-end generation.
実行時間 (method1): 10.1432 秒
Setting `pad_token_id` to `eos_token_id`:2150 for open-end generation.
実行時間 (method1): 10.3042 秒
Setting `pad_token_id` to `eos_token_id`:2150 for open-end generation.
実行時間 (method1): 12.3442 秒
Setting `pad_token_id` to `eos_token_id`:2150 for open-end generation.
実行時間 (method1): 10.0701 秒
Setting `pad_token_id` to `eos_token_id`:2150 for open-end generation.
実行時間 (method1): 11.3259 秒
実行時間 (method2_1): 0.6746 秒
Setting `pad_token_id` to `eos_token_id`:2150 for open-end generation.
実行時間 (method2_2): 10.4085 秒
Setting `pad_token_id` to `eos_token_id`:2150 for open-end generation.
実行時間 (method2_2): 8.5917 秒
Setting `pad_token_id` to `eos_token_id`:2150 for open-end generation.
実行時間 (method2_2): 8.2806 秒
Setting `pad_token_id` to `eos_token_id`:2150 for open-end generation.
実行時間 (method2_2): 11.3703 秒
Setting `pad_token_id` to `eos_token_id`:2150 for open-end generation.
実行時間 (method2_2): 9.2123 秒
Setting `pad_token_id` to `eos_token_id`:2150 for open-end generation.
実行時間 (method2_2): 10.2720 秒
Setting `pad_token_id` to `eos_token_id`:2150 for open-end generation.
実行時間 (method2_2): 9.1840 秒
Setting `pad_token_id` to `eos_token_id`:2150 for open-end generation.
実行時間 (method2_2): 10.0569 秒
Setting `pad_token_id` to `eos_token_id`:2150 for open-end generation.
実行時間 (method2_2): 9.5072 秒
Setting `pad_token_id` to `eos_token_id`:2150 for open-end generation.
実行時間 (method2_2): 9.2455 秒
方式平均実行時間
方式①平均10.77秒
方式②平均9.61秒

メモリ使用量は6GB〜7GBくらいだと思います。実行中のメモリを見るとPythonで使っていました。

その他気になる点

モデルの特性上しょうがないですが、実行するたびに音声が変化します(かなり変化します)。気に入ったパターンになるまで何度も実行する必要がありそうです。

LLMの制御と同じtemperatureなどのパラメータで制御できますが、思った発話を狙うのは難しそう

まとめ

Qwen3-TTSをMacOSのPythonから利用する方法について解説しました。ボイスクローンが割といい感じです。オープンソースでここまでできるとすごいとしか言いようがありません。

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

ABOUT ME
ある/Aru
ある/Aru
IT&機械学習エンジニア/ファイナンシャルプランナー(CFP®)
専門分野は並列処理・画像処理・機械学習・ディープラーニング。プログラミング言語はC, C++, Go, Pythonを中心として色々利用。現在は、Kaggle, 競プロなどをしながら悠々自適に活動中
記事URLをコピーしました