見出し画像

Qwen3-TTS-VC ・ Qwen3-TTS-VC-Realtime の音声クローンを試す

「Qwen3-TTS-VC」「Qwen3-TTS-VC-Realtime」の音声クローンを試したのでまとめました。


1. はじめに

「Qwen3-TTS-VC」「Qwen3-TTS-VC-Realtime」は、「Qwen API」で利用できる音声クローニング対応のTTSモデルです。

・Qwen3-TTS-VC
通常の音声生成。
音声ファイルをまとめて生成したい場合。

・Qwen3-TTS-VC-Realtime
低遅延のリアルタイム音声生成。
会話アプリや音声エージェント向け。

音声ファイルを作るだけなら「Qwen3-TTS-VC」、リアルタイム再生したいなら「Qwen3-TTS-VC-Realtime」を使います。

2. Qwen3-TTS-VC

「Qwen3-TTS-VC」は、音声クローニングに対応した通常版のTTSモデルです。参照音声をアップロードして声を登録し、その声でテキストを読み上げます。

主な用途は、次のとおりです。

・ナレーション音声の作成
・キャラクターボイスの生成
・動画用の音声作成
・音声サンプルの一括生成
・音声クローンの品質確認

リアルタイム性よりも、安定して音声ファイルを作りたい場合に使いやすいです。

3. Qwen3-TTS-VC-Realtime

「Qwen3-TTS-VC-Realtime」は、音声クローニングに対応したリアルタイム版のTTSモデルです。WebSocket経由でテキストをストリーミング入力し、生成された音声もストリーミングで受け取ります。

主な用途は、次のとおりです。

・音声対話アプリ
・AIキャラクターとの会話
・リアルタイム読み上げ
・カスタマーサポート音声
・ライブ配信向けの読み上げ

通常版より低遅延で返ってくるため、ユーザーとの会話体験を作る場合はこちらを使います。

4. 参照音声の準備

音声クローニングには、参照音声が必要です。
参照音声は次の条件を満たす必要があります。

・形式 : WAV / MP3 / M4A
・長さ : 10〜20秒推奨。最大60秒
・ファイルサイズ : 10MB未満
・サンプルレート : 24kHz以上
・チャンネル : モノラル
・内容 : 3秒以上の連続した明瞭な音声を含むこと。残り部分の短い無音は2秒以内にする
・対応言語 : 中国語、英語、ドイツ語、イタリア語、ポルトガル語、スペイン語、日本語、韓国語、フランス語、ロシア語

参照音声は、できるだけ静かな環境で録音します。

次のような音声は避けます。

・BGM入り
・ノイズが多い
・複数人の声が入っている
・歌声
・途中で長い無音が入る
・声が小さい
・早口すぎる

10〜20秒程度で、自然なスピードの文章を読むのがよさそうです。

5. APIキーの設定

「Qwen API」を使うには、「Alibaba Cloud Model Studio」でAPIキーを作成します。

作成したAPIキーは、環境変数に設定しておきます。

export DASHSCOPE_API_KEY="取得したAPIキー"

6. Qwen3-TTS-VC を試す

(1) Codex で指示。

Qwen API の Qwen3-TTS-VC で、参照音声 voice.wav を使って音声クローニングで「こんにちは。今日は良い天気ですね」のwavを生成して

・qwen3_tts_vc_api.py

#!/usr/bin/env python3
import argparse, base64, json, os
from pathlib import Path
from urllib.request import Request, urlopen

BASE_URL = "https://dashscope-intl.aliyuncs.com/api/v1"
MODEL = "qwen3-tts-vc-2026-01-22"
CACHE = Path("qwen3_tts_vc_voice.txt")


def post(path, payload):
    req = Request(
        f"{os.getenv('DASHSCOPE_BASE_URL', BASE_URL).rstrip('/')}{path}",
        data=json.dumps(payload, ensure_ascii=False).encode(),
        headers={"Authorization": f"Bearer {os.environ['DASHSCOPE_API_KEY']}", "Content-Type": "application/json"},
    )
    return json.loads(urlopen(req, timeout=180).read())


# 音声登録
def get_voice(audio_path):
    if CACHE.exists():
        return CACHE.read_text().strip()
    audio = base64.b64encode(Path(audio_path).read_bytes()).decode()
    voice = post("/services/audio/tts/customization", {
        "model": "qwen-voice-enrollment",
        "input": {
            "action": "create",
            "target_model": MODEL,
            "preferred_name": "voice",
            "audio": {"data": f"data:audio/wav;base64,{audio}"},
        },
    })["output"]["voice"]
    CACHE.write_text(voice + "\n")
    return voice


def main():
    p = argparse.ArgumentParser()
    p.add_argument("--audio", default="voice.wav")
    p.add_argument("--text", default="こんにちは。今日は良い天気ですね")
    p.add_argument("--output", default="qwen3_tts_vc_output.wav")
    args = p.parse_args()

    # 音声再生
    wav_url = post("/services/aigc/multimodal-generation/generation", {
        "model": MODEL,
        "input": {"text": args.text, "voice": get_voice(args.audio), "language_type": "Japanese"},
    })["output"]["audio"]["url"]
    Path(args.output).write_bytes(urlopen(wav_url, timeout=180).read())
    print(args.output)


if __name__ == "__main__":
    main()

・注意点
初回実行時は voice.wav をAlibaba Cloud側に音声登録し、取得した voice ID だけをローカルに保存します。登録済みのクローン音声本体はサーバー側にあり、2回目以降はそのIDを使って再利用するため、再登録は行いません。

・料金体系
2026-06-07時点の公式料金表では、このコードに関係する課金は2種類です。

・音声登録 : qwen-voice-enrollment は $0.01 / voice
・音声合成 : qwen3-tts-vc-2026-01-22 は $0.115 / 10,000文字、最大入力文字数 : 600文字

合成の出力音声自体は課金対象外で、入力テキスト文字数に課金
International では無料枠として、TTSは 10,000文字、音声登録は 1,000 voices/account が用意されています。ただし、いずれも Model Studio 有効化後90日間の無料枠です。

7. Qwen3-TTS-VC-Realtime を試す

(1) Codex で指示。

Qwen API の Qwen3-TTS-VC-Realtime で、先ほど音声登録した声を使って音声クローニングで「こんにちは。今日は良い天気ですね」を再生して

・qwen3_tts_vc_realtime_play.py

#!/usr/bin/env python3
import argparse, base64, json, os, threading, wave
from pathlib import Path
from urllib.request import Request, urlopen

import dashscope, pyaudio
from dashscope.audio.qwen_tts_realtime import AudioFormat, QwenTtsRealtime, QwenTtsRealtimeCallback

BASE = "https://dashscope-intl.aliyuncs.com/api/v1"
WS = "wss://dashscope-intl.aliyuncs.com/api-ws/v1/realtime"
MODEL = "qwen3-tts-vc-realtime-2026-01-15"


def post(path, body):
    req = Request(
        os.getenv("DASHSCOPE_BASE_URL", BASE).rstrip("/") + path,
        json.dumps(body, ensure_ascii=False).encode(),
        {"Authorization": f"Bearer {os.environ['DASHSCOPE_API_KEY']}", "Content-Type": "application/json"},
    )
    return json.loads(urlopen(req, timeout=180).read())


# 音声登録
def voice(audio, cache=Path("qwen3_tts_vc_realtime_voice.txt")):
    if cache.exists():
        return cache.read_text().strip()
    b64 = base64.b64encode(Path(audio).read_bytes()).decode()
    v = post("/services/audio/tts/customization", {"model": "qwen-voice-enrollment", "input": {
        "action": "create", "target_model": MODEL, "preferred_name": "voice_rt",
        "audio": {"data": f"data:audio/wav;base64,{b64}"},
    }})["output"]["voice"]
    cache.write_text(v + "\n")
    return v

# 音声再生
class Player(QwenTtsRealtimeCallback):
    def __init__(self, out):
        self.done = threading.Event()
        self.pa = pyaudio.PyAudio()
        self.stream = self.pa.open(format=pyaudio.paInt16, channels=1, rate=24000, output=True)
        self.wav = wave.open(out, "wb")
        self.wav.setnchannels(1); self.wav.setsampwidth(2); self.wav.setframerate(24000)

    def on_event(self, msg):
        if msg["type"] == "response.audio.delta":
            pcm = base64.b64decode(msg["delta"])
            self.stream.write(pcm); self.wav.writeframes(pcm)
        elif msg["type"] == "session.finished":
            self.done.set()

    def close(self):
        self.stream.stop_stream(); self.stream.close(); self.pa.terminate(); self.wav.close()


p = argparse.ArgumentParser()
p.add_argument("--audio", default="voice.wav")
p.add_argument("--text", default="こんにちは。今日は良い天気ですね")
p.add_argument("--output", default="qwen3_tts_vc_realtime_output.wav")
a = p.parse_args()

dashscope.api_key = os.environ["DASHSCOPE_API_KEY"]
player = Player(a.output)
tts = QwenTtsRealtime(model=MODEL, callback=player, url=os.getenv("DASHSCOPE_WS_URL", WS))
tts.connect()
tts.update_session(voice=voice(a.audio), response_format=AudioFormat.PCM_24000HZ_MONO_16BIT, mode="server_commit")
tts.append_text(a.text)
tts.finish(); player.done.wait(); tts.close(); player.close()
print(a.output)

・注意点
初回実行時は voice.wav をAlibaba Cloud側に音声登録し、取得した voice ID だけをローカルに保存します。登録済みのクローン音声本体はサーバー側にあり、2回目以降はそのIDを使って再利用するため、再登録は行いません。

Realtime APIから音声チャンクを受信すると、メモリにためずに PyAudio
で即再生し、同時にWAVファイルへ保存します。キャッシュファイルを削除すると、次回また音声登録が実行されます。

・料金体系
2026-06-07時点の公式料金表では、このRealtimeコードに関係する課金は2種類です。

・音声登録 : qwen-voice-enrollment は $0.01 / voice
・音声合成 : qwen3-tts-vc-realtime-2026-01-15 は $0.13 / 10,000文字

合成の出力音声自体は課金対象外で、入力テキスト文字数に課金
International では無料枠として、TTSは 10,000文字、音声登録は 1,000 voices/account が用意されています。ただし、いずれも Model Studio 有効化後90日間の無料枠です。

関連



いいなと思ったら応援しよう!