Qwen のリアルタイム音声合成モデルは、ストリーミング形式のテキスト入力とオーディオ出力をサポートします。複数の臨場感あふれる音声オプションを提供し、多言語および方言の合成に対応。同一音声で複数言語の出力が可能であり、イントネーションを自動調整し、複雑なテキストもスムーズに処理します。
主な機能
-
中国語や英語など複数言語で自然な発音を実現した、高忠実度のリアルタイム音声出力を生成します。
-
音声カスタマイズには、ボイスクローン(Qwen) および ボイスデザイン(Qwen) の 2 つの方法を提供します。
-
リアルタイム対話シナリオ向けに、低遅延のストリーミング入出力をサポートします。
-
話速、ピッチ、音量、ビットレートを調整可能で、音声表現を細かく制御できます。
-
主要なオーディオフォーマットに対応し、最大 48 kHz のサンプルレートでの出力が可能です。
-
命令制御 をサポートしており、自然言語による命令で音声の表現力を制御できます。
可用性
サポートされるモデル:
国際
「国際デプロイメントモード」では、アクセスポイントおよびデータストレージの両方が シンガポール リージョンに配置されます。モデル推論の計算リソースは、中国本土を除くグローバル範囲で動的にスケジュールされます。
以下のモデルを呼び出す際は、シンガポール リージョンの API キー を選択してください:
-
Qwen3-TTS-Instruct-Flash-Realtime:qwen3-tts-instruct-flash-realtime(安定版、qwen3-tts-instruct-flash-realtime-2026-01-22 と同等)、qwen3-tts-instruct-flash-realtime-2026-01-22(最新スナップショット)
-
Qwen3-TTS-VD-Realtime:qwen3-tts-vd-realtime-2026-01-15(最新スナップショット)、qwen3-tts-vd-realtime-2025-12-16(スナップショット)
-
Qwen3-TTS-VC-Realtime:qwen3-tts-vc-realtime-2026-01-15(最新スナップショット)、qwen3-tts-vc-realtime-2025-11-27(スナップショット)
-
Qwen3-TTS-Flash-Realtime:qwen3-tts-flash-realtime(安定版、qwen3-tts-flash-realtime-2025-11-27 と同等)、qwen3-tts-flash-realtime-2025-11-27(最新スナップショット)、qwen3-tts-flash-realtime-2025-09-18 (スナップショット)
中国本土
「中国本土デプロイメントモード」では、アクセスポイントおよびデータストレージの両方が 中国 (北京) リージョンに配置されます。モデル推論の計算リソースは中国本土内に限定されます。
以下のモデルを呼び出す際は、中国 (北京) リージョンの API キーを選択してください:API キー
-
Qwen3-TTS-Instruct-Flash-Realtime:qwen3-tts-instruct-flash-realtime(安定版、qwen3-tts-instruct-flash-realtime-2026-01-22 と同等)、qwen3-tts-instruct-flash-realtime-2026-01-22(最新スナップショット)
-
Qwen3-TTS-VD-Realtime:qwen3-tts-vd-realtime-2026-01-15(最新スナップショット)、qwen3-tts-vd-realtime-2025-12-16(スナップショット)
-
Qwen3-TTS-VC-Realtime:qwen3-tts-vc-realtime-2026-01-15(最新スナップショット)、qwen3-tts-vc-realtime-2025-11-27(スナップショット)
-
Qwen3-TTS-Flash-Realtime:qwen3-tts-flash-realtime(安定版、qwen3-tts-flash-realtime-2025-11-27 と同等)、qwen3-tts-flash-realtime-2025-11-27(最新スナップショット)、qwen3-tts-flash-realtime-2025-09-18 (スナップショット)
-
Qwen-TTS-Realtime:qwen-tts-realtime (安定版、qwen-tts-realtime-2025-07-15と同等)、qwen-tts-realtime-latest(最新版、qwen-tts-realtime-2025-07-15 と同等)、qwen-tts-realtime-2025-07-15 (スナップショット)
詳細については、「モデル一覧」をご参照ください。
モデル選択ガイド
|
シナリオ |
推奨モデル |
理由 |
|
ブランドアイデンティティ、専用音声、または拡張システム音声のための音声カスタマイズ(テキスト記述に基づく) |
qwen3-tts-vd-realtime-2026-01-15 |
ボイスデザインをサポート。音声サンプルを必要とせず、テキスト記述からカスタマイズされた音声を作成できます。ブランド専用音声をゼロから設計するのに最適です。 |
|
ブランドアイデンティティ、専用音声、または拡張システム音声のための音声カスタマイズ(音声サンプルに基づく) |
qwen3-tts-vc-realtime-2026-01-15 |
ボイスクローンをサポート。実際の音声サンプルから音声を迅速に再現し、高忠実度かつ一貫性のあるブランドボイスプリントを作成できます。 |
|
感情的なコンテンツ制作(オーディオブック、ラジオドラマ、ゲーム/アニメの吹き替え) |
qwen3-tts-instruct-flash-realtime |
命令制御をサポート。自然言語による説明でトーン、話速、感情、キャラクターの個性を正確に制御できます。豊かな表現力とキャラクター構築を要するシナリオに最適です。 |
|
プロフェッショナルな放送(ニュース、ドキュメンタリー、広告) |
qwen3-tts-instruct-flash-realtime |
命令制御をサポート。「権威的で厳粛」や「カジュアルでフレンドリー」などの放送スタイルおよびトーン特性を説明できます。プロフェッショナルなコンテンツ制作に適しています。 |
|
インテリジェントカスタマーサービスおよび会話型ボット |
qwen3-tts-flash-realtime、qwen3-tts-instruct-flash-realtime |
ストリーミング入出力をサポートし、話速およびピッチを調整可能です。命令制御対応バージョン(instruct)では、会話の文脈に応じてトーン(安心感を与える、熱意ある、プロフェッショナルなど)を動的に調整できます。 |
|
多言語コンテンツの放送 |
qwen3-tts-flash-realtime、qwen3-tts-instruct-flash-realtime |
複数言語および中国語の方言をサポートし、グローバルなコンテンツ配信ニーズに対応します。 |
|
オーディオブックの朗読および一般コンテンツ制作 |
qwen3-tts-flash-realtime、qwen3-tts-instruct-flash-realtime |
音量、話速、ピッチを調整可能で、オーディオブック、ポッドキャストなど、細かい制作要件に対応できます。 |
|
E コマースライブストリーミングおよび短尺動画の吹き替え |
qwen3-tts-flash-realtime、qwen3-tts-instruct-flash-realtime |
mp3/opus 圧縮フォーマットをサポートし、帯域幅が制限されたシナリオに適しています。 |
詳細については、「機能比較」をご参照ください。
クイックスタート
API キーの取得 および 最新版 DashScope SDK のインストール を、コード実行前に完了してください。
システム音声の使用
以下の例では、システム音声(「サポートされる音声」を参照)を用いた音声合成を行います。
model パラメーターを qwen3-tts-instruct-flash-realtime に置き換え、instructions パラメーターを設定することで、命令制御 機能を利用できます。
DashScope SDK
Python
サーバーコミットモード
import os
import base64
import threading
import time
import dashscope
from dashscope.audio.qwen_tts_realtime import *
qwen_tts_realtime: QwenTtsRealtime = None
text_to_synthesize = [
'そうですね?こんなスーパーが大好きです。',
'特に旧正月の時期には、',
'スーパーでお買い物に行きます。',
'そして私は、',
'とてもワクワクします!',
'たくさん買いたいものがたくさんあります!'
]
DO_VIDEO_TEST = False
def init_dashscope_api_key():
"""
DashScope API キーを設定します。詳細については、以下を参照してください:
https://github.com/aliyun/alibabacloud-bailian-speech-demo/blob/master/PREREQUISITES.md
"""
# API キーはシンガポールと北京の各リージョンで異なります。API キーの取得先:https://www.alibabacloud.com/help/zh/model-studio/get-api-key
if 'DASHSCOPE_API_KEY' in os.environ:
dashscope.api_key = os.environ[
'DASHSCOPE_API_KEY'] # 環境変数 DASHSCOPE_API_KEY から API キーを読み込みます
else:
dashscope.api_key = 'your-dashscope-api-key' # 手動で API キーを設定します
class MyCallback(QwenTtsRealtimeCallback):
def __init__(self):
self.complete_event = threading.Event()
self.file = open('result_24k.pcm', 'wb')
def on_open(self) -> None:
print('接続が確立されました。プレーヤーを初期化します。')
def on_close(self, close_status_code, close_msg) -> None:
self.file.close()
print('接続が終了しました。コード:{}、メッセージ:{}、プレーヤーを破棄します。'.format(close_status_code, close_msg))
def on_event(self, response: str) -> None:
try:
global qwen_tts_realtime
type = response['type']
if 'session.created' == type:
print('セッション開始:{}'.format(response['session']['id']))
if 'response.audio.delta' == type:
recv_audio_b64 = response['delta']
self.file.write(base64.b64decode(recv_audio_b64))
if 'response.done' == type:
print(f'応答 {qwen_tts_realtime.get_last_response_id()} が完了しました。')
if 'session.finished' == type:
print('セッションが終了しました。')
self.complete_event.set()
except Exception as e:
print('[エラー] {}'.format(e))
return
def wait_for_finished(self):
self.complete_event.wait()
if __name__ == '__main__':
init_dashscope_api_key()
print('初期化中 ...')
callback = MyCallback()
qwen_tts_realtime = QwenTtsRealtime(
# 命令制御を使用するには、model を qwen3-tts-instruct-flash-realtime に置き換えてください。
model='qwen3-tts-flash-realtime',
callback=callback,
# この URL はシンガポールリージョン用です。北京リージョンを使用する場合は、wss://dashscope.aliyuncs.com/api-ws/v1/realtime に置き換えてください。
url='wss://dashscope-intl.aliyuncs.com/api-ws/v1/realtime'
)
qwen_tts_realtime.connect()
qwen_tts_realtime.update_session(
voice = 'Cherry',
response_format = AudioFormat.PCM_24000HZ_MONO_16BIT,
# 命令制御を使用するには、以下の行のコメントを解除し、model を qwen3-tts-instruct-flash-realtime に置き換えてください。
# instructions='ファッション製品の紹介に適した、上昇するイントネーションで素早く話す。',
# optimize_instructions=True,
mode = 'server_commit'
)
for text_chunk in text_to_synthesize:
print(f'テキストを送信:{text_chunk}')
qwen_tts_realtime.append_text(text_chunk)
time.sleep(0.1)
qwen_tts_realtime.finish()
callback.wait_for_finished()
print('[メトリクス] セッション:{}、初回音声遅延:{}'.format(
qwen_tts_realtime.get_session_id(),
qwen_tts_realtime.get_first_audio_delay(),
))
コミットモード
import base64
import os
import threading
import dashscope
from dashscope.audio.qwen_tts_realtime import *
qwen_tts_realtime: QwenTtsRealtime = None
text_to_synthesize = [
'これは最初の文です。',
'これは二番目の文です。',
'これは三番目の文です。',
]
DO_VIDEO_TEST = False
def init_dashscope_api_key():
"""
DashScope API キーを設定します。詳細については、以下を参照してください:
https://github.com/aliyun/alibabacloud-bailian-speech-demo/blob/master/PREREQUISITES.md
"""
# API キーはシンガポールと北京の各リージョンで異なります。API キーの取得先:https://www.alibabacloud.com/help/zh/model-studio/get-api-key
if 'DASHSCOPE_API_KEY' in os.environ:
dashscope.api_key = os.environ[
'DASHSCOPE_API_KEY'] # 環境変数 DASHSCOPE_API_KEY から API キーを読み込みます
else:
dashscope.api_key = 'your-dashscope-api-key' # 手動で API キーを設定します
class MyCallback(QwenTtsRealtimeCallback):
def __init__(self):
super().__init__()
self.response_counter = 0
self.complete_event = threading.Event()
self.file = open(f'result_{self.response_counter}_24k.pcm', 'wb')
def reset_event(self):
self.response_counter += 1
self.file = open(f'result_{self.response_counter}_24k.pcm', 'wb')
self.complete_event = threading.Event()
def on_open(self) -> None:
print('接続が確立されました。プレーヤーを初期化します。')
def on_close(self, close_status_code, close_msg) -> None:
print('接続が終了しました。コード:{}、メッセージ:{}、プレーヤーを破棄します。'.format(close_status_code, close_msg))
def on_event(self, response: str) -> None:
try:
global qwen_tts_realtime
type = response['type']
if 'session.created' == type:
print('セッション開始:{}'.format(response['session']['id']))
if 'response.audio.delta' == type:
recv_audio_b64 = response['delta']
self.file.write(base64.b64decode(recv_audio_b64))
if 'response.done' == type:
print(f'応答 {qwen_tts_realtime.get_last_response_id()} が完了しました。')
self.complete_event.set()
self.file.close()
if 'session.finished' == type:
print('セッションが終了しました。')
self.complete_event.set()
except Exception as e:
print('[エラー] {}'.format(e))
return
def wait_for_response_done(self):
self.complete_event.wait()
if __name__ == '__main__':
init_dashscope_api_key()
print('初期化中 ...')
callback = MyCallback()
qwen_tts_realtime = QwenTtsRealtime(
# 命令制御を使用するには、model を qwen3-tts-instruct-flash-realtime に置き換えてください。
model='qwen3-tts-flash-realtime',
callback=callback,
# この URL はシンガポールリージョン用です。北京リージョンを使用する場合は、wss://dashscope.aliyuncs.com/api-ws/v1/realtime に置き換えてください。
url='wss://dashscope-intl.aliyuncs.com/api-ws/v1/realtime'
)
qwen_tts_realtime.connect()
qwen_tts_realtime.update_session(
voice = 'Cherry',
response_format = AudioFormat.PCM_24000HZ_MONO_16BIT,
# 命令制御を使用するには、以下の行のコメントを解除し、model を qwen3-tts-instruct-flash-realtime に置き換えてください。
# instructions='ファッション製品の紹介に適した、上昇するイントネーションで素早く話す。',
# optimize_instructions=True,
mode = 'commit'
)
print(f'テキストを送信:{text_to_synthesize[0]}')
qwen_tts_realtime.append_text(text_to_synthesize[0])
qwen_tts_realtime.commit()
callback.wait_for_response_done()
callback.reset_event()
print(f'テキストを送信:{text_to_synthesize[1]}')
qwen_tts_realtime.append_text(text_to_synthesize[1])
qwen_tts_realtime.commit()
callback.wait_for_response_done()
callback.reset_event()
print(f'テキストを送信:{text_to_synthesize[2]}')
qwen_tts_realtime.append_text(text_to_synthesize[2])
qwen_tts_realtime.commit()
callback.wait_for_response_done()
qwen_tts_realtime.finish()
print('[メトリクス] セッション:{}、初回音声遅延:{}'.format(
qwen_tts_realtime.get_session_id(),
qwen_tts_realtime.get_first_audio_delay(),
))Java
サーバーコミットモード
import com.alibaba.dashscope.audio.qwen_tts_realtime.*;
import com.alibaba.dashscope.exception.NoApiKeyException;
import com.google.gson.JsonObject;
import javax.sound.sampled.LineUnavailableException;
import javax.sound.sampled.SourceDataLine;
import javax.sound.sampled.AudioFormat;
import javax.sound.sampled.DataLine;
import javax.sound.sampled.AudioSystem;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.Base64;
import java.util.Queue;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicReference;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.atomic.AtomicBoolean;
public class Main {
static String[] textToSynthesize = {
"そうですね?こんなスーパーが大好きです。",
"特に旧正月の時期には、",
"スーパーでお買い物に行きます。",
"そして私は、",
"とてもワクワクします!",
"たくさん買いたいものがたくさんあります!"
};
// リアルタイム PCM オーディオプレーヤークラス
public static class RealtimePcmPlayer {
private int sampleRate;
private SourceDataLine line;
private AudioFormat audioFormat;
private Thread decoderThread;
private Thread playerThread;
private AtomicBoolean stopped = new AtomicBoolean(false);
private Queue<String> b64AudioBuffer = new ConcurrentLinkedQueue<>();
private Queue<byte[]> RawAudioBuffer = new ConcurrentLinkedQueue<>();
// コンストラクターはオーディオフォーマットとオーディオラインを初期化します。
public RealtimePcmPlayer(int sampleRate) throws LineUnavailableException {
this.sampleRate = sampleRate;
this.audioFormat = new AudioFormat(this.sampleRate, 16, 1, true, false);
DataLine.Info info = new DataLine.Info(SourceDataLine.class, audioFormat);
line = (SourceDataLine) AudioSystem.getLine(info);
line.open(audioFormat);
line.start();
decoderThread = new Thread(new Runnable() {
@Override
public void run() {
while (!stopped.get()) {
String b64Audio = b64AudioBuffer.poll();
if (b64Audio != null) {
byte[] rawAudio = Base64.getDecoder().decode(b64Audio);
RawAudioBuffer.add(rawAudio);
} else {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
}
});
playerThread = new Thread(new Runnable() {
@Override
public void run() {
while (!stopped.get()) {
byte[] rawAudio = RawAudioBuffer.poll();
if (rawAudio != null) {
try {
playChunk(rawAudio);
} catch (IOException e) {
throw new RuntimeException(e);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
} else {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
}
});
decoderThread.start();
playerThread.start();
}
// オーディオチャンクを再生し、再生完了までブロックします。
private void playChunk(byte[] chunk) throws IOException, InterruptedException {
if (chunk == null || chunk.length == 0) return;
int bytesWritten = 0;
while (bytesWritten < chunk.length) {
bytesWritten += line.write(chunk, bytesWritten, chunk.length - bytesWritten);
}
int audioLength = chunk.length / (this.sampleRate*2/1000);
// バッファー内のオーディオの再生が完了するまで待機します。
Thread.sleep(audioLength - 10);
}
public void write(String b64Audio) {
b64AudioBuffer.add(b64Audio);
}
public void cancel() {
b64AudioBuffer.clear();
RawAudioBuffer.clear();
}
public void waitForComplete() throws InterruptedException {
while (!b64AudioBuffer.isEmpty() || !RawAudioBuffer.isEmpty()) {
Thread.sleep(100);
}
line.drain();
}
public void shutdown() throws InterruptedException {
stopped.set(true);
decoderThread.join();
playerThread.join();
if (line != null && line.isRunning()) {
line.drain();
line.close();
}
}
}
public static void main(String[] args) throws InterruptedException, LineUnavailableException, FileNotFoundException {
QwenTtsRealtimeParam param = QwenTtsRealtimeParam.builder()
// 命令制御機能を使用するには、model を qwen3-tts-instruct-flash-realtime に置き換えてください。
.model("qwen3-tts-flash-realtime")
// 以下の URL はシンガポールリージョン用です。中国 (北京) リージョンのモデルを使用する場合は、wss://dashscope.aliyuncs.com/api-ws/v1/realtime に置き換えてください。
.url("wss://dashscope-intl.aliyuncs.com/api-ws/v1/realtime")
// シンガポールおよび中国 (北京) リージョンの API キーは異なります。API キーの取得先:https://www.alibabacloud.com/help/en/model-studio/get-api-key。
.apikey(System.getenv("DASHSCOPE_API_KEY"))
.build();
AtomicReference<CountDownLatch> completeLatch = new AtomicReference<>(new CountDownLatch(1));
final AtomicReference<QwenTtsRealtime> qwenTtsRef = new AtomicReference<>(null);
// リアルタイムオーディオプレーヤーインスタンスを作成します。
RealtimePcmPlayer audioPlayer = new RealtimePcmPlayer(24000);
QwenTtsRealtime qwenTtsRealtime = new QwenTtsRealtime(param, new QwenTtsRealtimeCallback() {
@Override
public void onOpen() {
// 接続確立時のイベントを処理します。
}
@Override
public void onEvent(JsonObject message) {
String type = message.get("type").getAsString();
switch(type) {
case "session.created":
// セッション作成時のイベントを処理します。
break;
case "response.audio.delta":
String recvAudioB64 = message.get("delta").getAsString();
// オーディオをリアルタイムで再生します。
audioPlayer.write(recvAudioB64);
break;
case "response.done":
// 応答完了時のイベントを処理します。
break;
case "session.finished":
// セッション終了時のイベントを処理します。
completeLatch.get().countDown();
default:
break;
}
}
@Override
public void onClose(int code, String reason) {
// 接続終了時のイベントを処理します。
}
});
qwenTtsRef.set(qwenTtsRealtime);
try {
qwenTtsRealtime.connect();
} catch (NoApiKeyException e) {
throw new RuntimeException(e);
}
QwenTtsRealtimeConfig config = QwenTtsRealtimeConfig.builder()
.voice("Cherry")
.responseFormat(QwenTtsRealtimeAudioFormat.PCM_24000HZ_MONO_16BIT)
.mode("server_commit")
// 命令制御機能を使用するには、以下の行のコメントを解除し、model を qwen3-tts-instruct-flash-realtime に置き換えてください。
// .instructions("")
// .optimizeInstructions(true)
.build();
qwenTtsRealtime.updateSession(config);
for (String text:textToSynthesize) {
qwenTtsRealtime.appendText(text);
Thread.sleep(100);
}
qwenTtsRealtime.finish();
completeLatch.get().await();
qwenTtsRealtime.close();
// オーディオ再生が完了するのを待ってからプレーヤーをシャットダウンします。
audioPlayer.waitForComplete();
audioPlayer.shutdown();
System.exit(0);
}
}コミットモード
import com.alibaba.dashscope.audio.qwen_tts_realtime.*;
import com.alibaba.dashscope.exception.NoApiKeyException;
import com.google.gson.JsonObject;
import javax.sound.sampled.LineUnavailableException;
import javax.sound.sampled.SourceDataLine;
import javax.sound.sampled.AudioFormat;
import javax.sound.sampled.DataLine;
import javax.sound.sampled.AudioSystem;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Base64;
import java.util.Queue;
import java.util.Scanner;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicReference;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.atomic.AtomicBoolean;
public class commit {
// リアルタイム PCM オーディオプレーヤークラス
public static class RealtimePcmPlayer {
private int sampleRate;
private SourceDataLine line;
private AudioFormat audioFormat;
private Thread decoderThread;
private Thread playerThread;
private AtomicBoolean stopped = new AtomicBoolean(false);
private Queue<String> b64AudioBuffer = new ConcurrentLinkedQueue<>();
private Queue<byte[]> RawAudioBuffer = new ConcurrentLinkedQueue<>();
// コンストラクターはオーディオフォーマットとオーディオラインを初期化します。
public RealtimePcmPlayer(int sampleRate) throws LineUnavailableException {
this.sampleRate = sampleRate;
this.audioFormat = new AudioFormat(this.sampleRate, 16, 1, true, false);
DataLine.Info info = new DataLine.Info(SourceDataLine.class, audioFormat);
line = (SourceDataLine) AudioSystem.getLine(info);
line.open(audioFormat);
line.start();
decoderThread = new Thread(new Runnable() {
@Override
public void run() {
while (!stopped.get()) {
String b64Audio = b64AudioBuffer.poll();
if (b64Audio != null) {
byte[] rawAudio = Base64.getDecoder().decode(b64Audio);
RawAudioBuffer.add(rawAudio);
} else {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
}
});
playerThread = new Thread(new Runnable() {
@Override
public void run() {
while (!stopped.get()) {
byte[] rawAudio = RawAudioBuffer.poll();
if (rawAudio != null) {
try {
playChunk(rawAudio);
} catch (IOException e) {
throw new RuntimeException(e);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
} else {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
}
});
decoderThread.start();
playerThread.start();
}
// オーディオチャンクを再生し、再生完了までブロックします。
private void playChunk(byte[] chunk) throws IOException, InterruptedException {
if (chunk == null || chunk.length == 0) return;
int bytesWritten = 0;
while (bytesWritten < chunk.length) {
bytesWritten += line.write(chunk, bytesWritten, chunk.length - bytesWritten);
}
int audioLength = chunk.length / (this.sampleRate*2/1000);
// バッファー内のオーディオの再生が完了するまで待機します。
Thread.sleep(audioLength - 10);
}
public void write(String b64Audio) {
b64AudioBuffer.add(b64Audio);
}
public void cancel() {
b64AudioBuffer.clear();
RawAudioBuffer.clear();
}
public void waitForComplete() throws InterruptedException {
// バッファー内のすべてのオーディオデータの再生が完了するまで待機します。
while (!b64AudioBuffer.isEmpty() || !RawAudioBuffer.isEmpty()) {
Thread.sleep(100);
}
// オーディオラインの再生が完了するまで待機します。
line.drain();
}
public void shutdown() throws InterruptedException {
stopped.set(true);
decoderThread.join();
playerThread.join();
if (line != null && line.isRunning()) {
line.drain();
line.close();
}
}
}
public static void main(String[] args) throws InterruptedException, LineUnavailableException, FileNotFoundException {
Scanner scanner = new Scanner(System.in);
QwenTtsRealtimeParam param = QwenTtsRealtimeParam.builder()
// 命令制御機能を使用するには、model を qwen3-tts-instruct-flash-realtime に置き換えてください。
.model("qwen3-tts-flash-realtime")
// 以下の URL はシンガポールリージョン用です。中国 (北京) リージョンのモデルを使用する場合は、wss://dashscope.aliyuncs.com/api-ws/v1/realtime に置き換えてください。
.url("wss://dashscope-intl.aliyuncs.com/api-ws/v1/realtime")
// シンガポールおよび中国 (北京) リージョンの API キーは異なります。API キーの取得先:https://www.alibabacloud.com/help/en/model-studio/get-api-key。
.apikey(System.getenv("DASHSCOPE_API_KEY"))
.build();
AtomicReference<CountDownLatch> completeLatch = new AtomicReference<>(new CountDownLatch(1));
// リアルタイムプレーヤーインスタンスを作成します。
RealtimePcmPlayer audioPlayer = new RealtimePcmPlayer(24000);
final AtomicReference<QwenTtsRealtime> qwenTtsRef = new AtomicReference<>(null);
QwenTtsRealtime qwenTtsRealtime = new QwenTtsRealtime(param, new QwenTtsRealtimeCallback() {
// File file = new File("result_24k.pcm");
// FileOutputStream fos = new FileOutputStream(file);
@Override
public void onOpen() {
System.out.println("接続が確立されました。");
System.out.println("テキストを入力して Enter キーを押すと送信されます。「quit」と入力するとプログラムを終了します。");
}
@Override
public void onEvent(JsonObject message) {
String type = message.get("type").getAsString();
switch(type) {
case "session.created":
System.out.println("セッション開始:" + message.get("session").getAsJsonObject().get("id").getAsString());
break;
case "response.audio.delta":
String recvAudioB64 = message.get("delta").getAsString();
byte[] rawAudio = Base64.getDecoder().decode(recvAudioB64);
// fos.write(rawAudio);
// オーディオをリアルタイムで再生します。
audioPlayer.write(recvAudioB64);
break;
case "response.done":
System.out.println("応答が完了しました。");
// オーディオ再生が完了するのを待機します。
try {
audioPlayer.waitForComplete();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
// 次の入力の準備をします。
completeLatch.get().countDown();
break;
case "session.finished":
System.out.println("セッションが終了しました。");
if (qwenTtsRef.get() != null) {
System.out.println("[メトリクス] 応答:{}、初回音声遅延:{} ms".formatted(qwenTtsRef.get().getResponseId(), qwenTtsRef.get().getFirstAudioDelay()));
}
completeLatch.get().countDown();
default:
break;
}
}
@Override
public void onClose(int code, String reason) {
System.out.println("接続が終了しました。コード:{}、理由:{}".formatted(code, reason));
try {
// fos.close();
// 再生が完了するのを待ってからプレーヤーをシャットダウンします。
audioPlayer.waitForComplete();
audioPlayer.shutdown();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
});
qwenTtsRef.set(qwenTtsRealtime);
try {
qwenTtsRealtime.connect();
} catch (NoApiKeyException e) {
throw new RuntimeException(e);
}
QwenTtsRealtimeConfig config = QwenTtsRealtimeConfig.builder()
.voice("Cherry")
.responseFormat(QwenTtsRealtimeAudioFormat.PCM_24000HZ_MONO_16BIT)
.mode("commit")
// 命令制御機能を使用するには、以下の行のコメントを解除し、model を qwen3-tts-instruct-flash-realtime に置き換えてください。
// .instructions("")
// .optimizeInstructions(true)
.build();
qwenTtsRealtime.updateSession(config);
// ユーザー入力をループで読み取ります。
while (true) {
System.out.print("合成するテキストを入力してください:");
String text = scanner.nextLine();
// ユーザーが「quit」と入力した場合、プログラムを終了します。
if ("quit".equalsIgnoreCase(text.trim())) {
System.out.println("接続を終了しています...");
qwenTtsRealtime.finish();
completeLatch.get().await();
break;
}
// ユーザー入力が空の場合、スキップします。
if (text.trim().isEmpty()) {
continue;
}
// カウントダウンラッチを再初期化します。
completeLatch.set(new CountDownLatch(1));
// テキストを送信します。
qwenTtsRealtime.appendText(text);
qwenTtsRealtime.commit();
// 現在の合成が完了するのを待機します。
completeLatch.get().await();
}
// リソースをクリーンアップします。
audioPlayer.waitForComplete();
audioPlayer.shutdown();
scanner.close();
System.exit(0);
}
}WebSocket API
-
ランタイム環境の準備
お使いのオペレーティングシステムに応じて pyaudio をインストールします。
macOS
brew install portaudio && pip install pyaudioDebian/Ubuntu
sudo apt-get install python3-pyaudio または pip install pyaudioCentOS
sudo yum install -y portaudio portaudio-devel && pip install pyaudioWindows
pip install pyaudio次に、pip を使用して WebSocket 依存関係をインストールします:
pip install websocket-client==1.8.0 websockets -
クライアントの作成
ローカルに
tts_realtime_client.pyという名前の新しい Python ファイルを作成し、以下のコードをファイルにコピーしてください: -
音声合成モードの選択
Realtime API は、以下の 2 つのモードをサポートしています:
-
サーバーコミットモード
クライアントはテキストのみを送信します。サーバーがテキストの区切りと合成タイミングを知能的に判断します。GPS ナビゲーションなど、手動による合成制御を必要としない低遅延シナリオに適しています。
-
コミットモード
まずテキストをバッファーに追加し、その後サーバーにそのテキストの合成を指示します。ニュース放送など、一時停止や文の区切りを細かく制御する必要があるシナリオに適しています。
サーバーコミットモード
server_commit.pyという名前の別の Python ファイルをtts_realtime_client.pyと同じディレクトリに作成し、以下のコードをファイルにコピーしてください:server_commit.pyを実行して、Realtime API によって生成されたリアルタイム音声を聴取します。コミットモード
commit.pyという名前の別の Python ファイルをtts_realtime_client.pyと同じディレクトリに作成し、以下のコードをファイルにコピーしてください:commit.pyを実行して、複数のテキストを合成します。空のまま Enter キーを押すと、Realtime API から返された音声をスピーカーで聴取できます。 -
クローン音声の使用
ボイスクローンサービスは、プレビュー音声を提供しません。効果の確認および評価は、音声合成インターフェイスを通じて行ってください。初期テストには短いテキストを使用してください。
以下の例では、ボイスクローンによって生成されたカスタム音声を音声合成で使用し、元の音声に非常に近い出力を生成する方法を示します。この例は、「システム音声の使用」における DashScope SDK の「サーバーコミットモード」のサンプルコードを基にしており、voice パラメーターをクローンされたカスタム音声に置き換えています。
-
基本原則:ボイスクローンモデル(
target_model)と音声合成モデル(model)を一致させる必要があります。一致していない場合、合成は失敗します。 -
この例では、ローカルの音声ファイル
voice.mp3をボイスクローンに使用しています。コードを実行する際に、このファイルを置き換えてください。
Python
# coding=utf-8
# pyaudio のインストール手順:
# APPLE Mac OS X
# brew install portaudio
# pip install pyaudio
# Debian/Ubuntu
# sudo apt-get install python-pyaudio python3-pyaudio
# または
# pip install pyaudio
# CentOS
# sudo yum install -y portaudio portaudio-devel && pip install pyaudio
# Microsoft Windows
# python -m pip install pyaudio
import pyaudio
import os
import requests
import base64
import pathlib
import threading
import time
import dashscope # DashScope Python SDK のバージョンは 1.23.9 以降である必要があります
from dashscope.audio.qwen_tts_realtime import QwenTtsRealtime, QwenTtsRealtimeCallback, AudioFormat
# ======= 定数 =======
DEFAULT_TARGET_MODEL = "qwen3-tts-vc-realtime-2026-01-15" # 音声クローンと音声合成で同じモデルを使用します
DEFAULT_PREFERRED_NAME = "guanyu"
DEFAULT_AUDIO_MIME_TYPE = "audio/mpeg"
VOICE_FILE_PATH = "voice.mp3" # 音声クローン用のローカル音声ファイルへの相対パス
TEXT_TO_SYNTHESIZE = [
'Right? I really love this kind of supermarket,',
'especially during Chinese New Year',
'when I go shopping',
'I feel',
'super super happy!',
'I want to buy so many things!'
]
def create_voice(file_path: str,
target_model: str = DEFAULT_TARGET_MODEL,
preferred_name: str = DEFAULT_PREFERRED_NAME,
audio_mime_type: str = DEFAULT_AUDIO_MIME_TYPE) -> str:
"""
音声を作成し、音声パラメーターを返します
"""
# API キーはシンガポールリージョンと北京リージョンで異なります。API キーの取得方法: https://www.alibabacloud.com/help/zh/model-studio/get-api-key
# 環境変数が設定されていない場合は、Model Studio の API キーに置き換えてください: api_key = "sk-xxx"
api_key = os.getenv("DASHSCOPE_API_KEY")
file_path_obj = pathlib.Path(file_path)
if not file_path_obj.exists():
raise FileNotFoundError(f"音声ファイルが見つかりません: {file_path}")
base64_str = base64.b64encode(file_path_obj.read_bytes()).decode()
data_uri = f"data:{audio_mime_type};base64,{base64_str}"
# シンガポールリージョンの URL。北京リージョンのモデルの場合は、https://dashscope.aliyuncs.com/api/v1/services/audio/tts/customization に置き換えてください
url = "https://dashscope-intl.aliyuncs.com/api/v1/services/audio/tts/customization"
payload = {
"model": "qwen-voice-enrollment", # この値は変更しないでください
"input": {
"action": "create",
"target_model": target_model,
"preferred_name": preferred_name,
"audio": {"data": data_uri}
}
}
headers = {
"Authorization": f"Bearer {api_key}",
"Content-Type": "application/json"
}
resp = requests.post(url, json=payload, headers=headers)
if resp.status_code != 200:
raise RuntimeError(f"音声作成に失敗しました: {resp.status_code}, {resp.text}")
try:
return resp.json()["output"]["voice"]
except (KeyError, ValueError) as e:
raise RuntimeError(f"音声応答の解析に失敗しました: {e}")
def init_dashscope_api_key():
"""
DashScope SDK の API キーを初期化します
"""
# API キーはシンガポールリージョンと北京リージョンで異なります。API キーの取得方法: https://www.alibabacloud.com/help/zh/model-studio/get-api-key
# 環境変数が設定されていない場合は、Model Studio の API キーに置き換えてください: dashscope.api_key = "sk-xxx"
dashscope.api_key = os.getenv("DASHSCOPE_API_KEY")
# ======= コールバッククラス =======
class MyCallback(QwenTtsRealtimeCallback):
"""
カスタム TTS ストリーミングコールバック
"""
def __init__(self):
self.complete_event = threading.Event()
self._player = pyaudio.PyAudio()
self._stream = self._player.open(
format=pyaudio.paInt16, channels=1, rate=24000, output=True
)
def on_open(self) -> None:
print('[TTS] 接続が確立されました')
def on_close(self, close_status_code, close_msg) -> None:
self._stream.stop_stream()
self._stream.close()
self._player.terminate()
print(f'[TTS] 接続が終了しました。コード={close_status_code}、メッセージ={close_msg}')
def on_event(self, response: dict) -> None:
try:
event_type = response.get('type', '')
if event_type == 'session.created':
print(f'[TTS] セッションが開始されました: {response["session"]["id"]}')
elif event_type == 'response.audio.delta':
audio_data = base64.b64decode(response['delta'])
self._stream.write(audio_data)
elif event_type == 'response.done':
print(f'[TTS] 応答が完了しました。応答 ID: {qwen_tts_realtime.get_last_response_id()}')
elif event_type == 'session.finished':
print('[TTS] セッションが終了しました')
self.complete_event.set()
except Exception as e:
print(f'[エラー] コールバックイベントの処理中にエラーが発生しました: {e}')
def wait_for_finished(self):
self.complete_event.wait()
# ======= メイン実行ロジック =======
if __name__ == '__main__':
init_dashscope_api_key()
print('[System] Qwen TTS Realtime を初期化しています...')
callback = MyCallback()
qwen_tts_realtime = QwenTtsRealtime(
model=DEFAULT_TARGET_MODEL,
callback=callback,
# シンガポールリージョンの URL。北京リージョンのモデルの場合は、wss://dashscope.aliyuncs.com/api-ws/v1/realtime に置き換えてください
url='wss://dashscope-intl.aliyuncs.com/api-ws/v1/realtime'
)
qwen_tts_realtime.connect()
qwen_tts_realtime.update_session(
voice=create_voice(VOICE_FILE_PATH), # 音声パラメーターをクローンしたカスタム音声に置き換えます
response_format=AudioFormat.PCM_24000HZ_MONO_16BIT,
mode='server_commit'
)
for text_chunk in TEXT_TO_SYNTHESIZE:
print(f'[テキスト送信中]: {text_chunk}')
qwen_tts_realtime.append_text(text_chunk)
time.sleep(0.1)
qwen_tts_realtime.finish()
callback.wait_for_finished()
print(f'[メトリクス] session_id={qwen_tts_realtime.get_session_id()}, '
f'first_audio_delay={qwen_tts_realtime.get_first_audio_delay()}s')
Java
Gson 依存関係をインポートする必要があります。Maven または Gradle を使用している場合は、以下のように依存関係を追加してください:
Maven
pom.xml に以下の内容を追加します:
<!-- https://mvnrepository.com/artifact/com.google.code.gson/gson -->
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>2.13.1</version>
</dependency>
Gradle
build.gradle に以下の内容を追加します:
// https://mvnrepository.com/artifact/com.google.code.gson/gson
implementation("com.google.code.gson:gson:2.13.1")
import com.alibaba.dashscope.audio.qwen_tts_realtime.*;
import com.alibaba.dashscope.exception.NoApiKeyException;
import com.google.gson.Gson;
import com.google.gson.JsonObject;
import javax.sound.sampled.*;
import java.io.*;
import java.net.HttpURLConnection;
import java.net.URL;
import java.nio.file.*;
import java.nio.charset.StandardCharsets;
import java.util.Base64;
import java.util.Queue;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicReference;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.atomic.AtomicBoolean;
public class Main {
// ===== 定数 =====
// ボイスクローンと音声合成で同じモデルを使用
private static final String TARGET_MODEL = "qwen3-tts-vc-realtime-2026-01-15";
private static final String PREFERRED_NAME = "guanyu";
// ボイスクローン用のローカル音声ファイルの相対パス
private static final String AUDIO_FILE = "voice.mp3";
private static final String AUDIO_MIME_TYPE = "audio/mpeg";
private static String[] textToSynthesize = {
"そうですね?こんなスーパーが大好きです。",
"特に旧正月の時期には、",
"スーパーでお買い物に行きます。",
"そして私は、",
"とてもワクワクします!",
"たくさん買いたいものがたくさんあります!"
};
// データ URI を生成
public static String toDataUrl(String filePath) throws IOException {
byte[] bytes = Files.readAllBytes(Paths.get(filePath));
String encoded = Base64.getEncoder().encodeToString(bytes);
return "data:" + AUDIO_MIME_TYPE + ";base64," + encoded;
}
// API を呼び出して音声を作成
public static String createVoice() throws Exception {
// API キーはシンガポールと北京の各リージョンで異なります。API キーの取得先:https://www.alibabacloud.com/help/zh/model-studio/get-api-key
// 環境変数が設定されていない場合は、String apiKey = "sk-xxx" として直接設定してください。
String apiKey = System.getenv("DASHSCOPE_API_KEY");
String jsonPayload =
"{"
+ "\"model\": \"qwen-voice-enrollment\"," // この値は変更しないでください。
+ "\"input\": {"
+ "\"action\": \"create\","
+ "\"target_model\": \"" + TARGET_MODEL + "\","
+ "\"preferred_name\": \"" + PREFERRED_NAME + "\","
+ "\"audio\": {"
+ "\"data\": \"" + toDataUrl(AUDIO_FILE) + "\""
+ "}"
+ "}"
+ "}";
HttpURLConnection con = (HttpURLConnection) new URL("https://dashscope.aliyuncs.com/api/v1/services/audio/tts/customization").openConnection();
con.setRequestMethod("POST");
con.setRequestProperty("Authorization", "Bearer " + apiKey);
con.setRequestProperty("Content-Type", "application/json");
con.setDoOutput(true);
try (OutputStream os = con.getOutputStream()) {
os.write(jsonPayload.getBytes(StandardCharsets.UTF_8));
}
int status = con.getResponseCode();
System.out.println("HTTP ステータスコード:" + status);
try (BufferedReader br = new BufferedReader(
new InputStreamReader(status >= 200 && status < 300 ? con.getInputStream() : con.getErrorStream(),
StandardCharsets.UTF_8))) {
StringBuilder response = new StringBuilder();
String line;
while ((line = br.readLine()) != null) {
response.append(line);
}
System.out.println("応答内容:" + response);
if (status == 200) {
JsonObject jsonObj = new Gson().fromJson(response.toString(), JsonObject.class);
return jsonObj.getAsJsonObject("output").get("voice").getAsString();
}
throw new IOException("音声作成に失敗しました:" + status + " - " + response);
}
}
// リアルタイム PCM オーディオプレーヤークラス
public static class RealtimePcmPlayer {
private int sampleRate;
private SourceDataLine line;
private AudioFormat audioFormat;
private Thread decoderThread;
private Thread playerThread;
private AtomicBoolean stopped = new AtomicBoolean(false);
private Queue<String> b64AudioBuffer = new ConcurrentLinkedQueue<>();
private Queue<byte[]> RawAudioBuffer = new ConcurrentLinkedQueue<>();
// コンストラクターはオーディオフォーマットとオーディオラインを初期化します。
public RealtimePcmPlayer(int sampleRate) throws LineUnavailableException {
this.sampleRate = sampleRate;
this.audioFormat = new AudioFormat(this.sampleRate, 16, 1, true, false);
DataLine.Info info = new DataLine.Info(SourceDataLine.class, audioFormat);
line = (SourceDataLine) AudioSystem.getLine(info);
line.open(audioFormat);
line.start();
decoderThread = new Thread(new Runnable() {
@Override
public void run() {
while (!stopped.get()) {
String b64Audio = b64AudioBuffer.poll();
if (b64Audio != null) {
byte[] rawAudio = Base64.getDecoder().decode(b64Audio);
RawAudioBuffer.add(rawAudio);
} else {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
}
});
playerThread = new Thread(new Runnable() {
@Override
public void run() {
while (!stopped.get()) {
byte[] rawAudio = RawAudioBuffer.poll();
if (rawAudio != null) {
try {
playChunk(rawAudio);
} catch (IOException e) {
throw new RuntimeException(e);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
} else {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
}
});
decoderThread.start();
playerThread.start();
}
// オーディオチャンクを再生し、再生完了までブロックします。
private void playChunk(byte[] chunk) throws IOException, InterruptedException {
if (chunk == null || chunk.length == 0) return;
int bytesWritten = 0;
while (bytesWritten < chunk.length) {
bytesWritten += line.write(chunk, bytesWritten, chunk.length - bytesWritten);
}
int audioLength = chunk.length / (this.sampleRate*2/1000);
// バッファー内のオーディオの再生が完了するまで待機します。
Thread.sleep(audioLength - 10);
}
public void write(String b64Audio) {
b64AudioBuffer.add(b64Audio);
}
public void cancel() {
b64AudioBuffer.clear();
RawAudioBuffer.clear();
}
public void waitForComplete() throws InterruptedException {
while (!b64AudioBuffer.isEmpty() || !RawAudioBuffer.isEmpty()) {
Thread.sleep(100);
}
line.drain();
}
public void shutdown() throws InterruptedException {
stopped.set(true);
decoderThread.join();
playerThread.join();
if (line != null && line.isRunning()) {
line.drain();
line.close();
}
}
}
public static void main(String[] args) throws Exception {
QwenTtsRealtimeParam param = QwenTtsRealtimeParam.builder()
.model(TARGET_MODEL)
// シンガポールリージョンの URL。北京リージョンのモデルを使用する場合は、wss://dashscope.aliyuncs.com/api-ws/v1/realtime に置き換えてください。
.url("wss://dashscope-intl.aliyuncs.com/api-ws/v1/realtime")
// API キーはシンガポールと北京の各リージョンで異なります。API キーの取得先:https://www.alibabacloud.com/help/zh/model-studio/get-api-key
// 環境変数が設定されていない場合は、.apikey("sk-xxx") として直接設定してください。
.apikey(System.getenv("DASHSCOPE_API_KEY"))
.build();
AtomicReference<CountDownLatch> completeLatch = new AtomicReference<>(new CountDownLatch(1));
final AtomicReference<QwenTtsRealtime> qwenTtsRef = new AtomicReference<>(null);
// リアルタイムオーディオプレーヤーインスタンスを作成
RealtimePcmPlayer audioPlayer = new RealtimePcmPlayer(24000);
QwenTtsRealtime qwenTtsRealtime = new QwenTtsRealtime(param, new QwenTtsRealtimeCallback() {
@Override
public void onOpen() {
// 接続確立時の処理
}
@Override
public void onEvent(JsonObject message) {
String type = message.get("type").getAsString();
switch(type) {
case "session.created":
// セッション作成時の処理
break;
case "response.audio.delta":
String recvAudioB64 = message.get("delta").getAsString();
// オーディオをリアルタイムで再生
audioPlayer.write(recvAudioB64);
break;
case "response.done":
// 応答完了時の処理
break;
case "session.finished":
// セッション終了時の処理
completeLatch.get().countDown();
default:
break;
}
}
@Override
public void onClose(int code, String reason) {
// 接続終了時の処理
}
});
qwenTtsRef.set(qwenTtsRealtime);
try {
qwenTtsRealtime.connect();
} catch (NoApiKeyException e) {
throw new RuntimeException(e);
}
QwenTtsRealtimeConfig config = QwenTtsRealtimeConfig.builder()
.voice(createVoice()) // voice パラメーターをクローンされたカスタム音声に置き換えます。
.responseFormat(QwenTtsRealtimeAudioFormat.PCM_24000HZ_MONO_16BIT)
.mode("server_commit")
.build();
qwenTtsRealtime.updateSession(config);
for (String text:textToSynthesize) {
qwenTtsRealtime.appendText(text);
Thread.sleep(100);
}
qwenTtsRealtime.finish();
completeLatch.get().await();
// オーディオ再生が完了するのを待ってからプレーヤーをシャットダウン
audioPlayer.waitForComplete();
audioPlayer.shutdown();
System.exit(0);
}
}
デザイン音声の使用
ボイスデザイン機能はプレビュー音声データを返します。まずこのプレビュー音声を聴いて、期待通りの効果が得られるか確認してから、音声合成に使用してください。
カスタム音声を生成し、プレビューを聴きます。満足できる場合は次に進み、そうでない場合は音声を再生成します。
Python
import requests import base64 import os def create_voice_and_play(): # API キーはシンガポールと北京の各リージョンで異なります。API キーの取得先:https://www.alibabacloud.com/help/zh/model-studio/get-api-key # 環境変数を設定していない場合は、以下の行を api_key = "sk-xxx" に置き換えてください。 api_key = os.getenv("DASHSCOPE_API_KEY") if not api_key: print("エラー:DASHSCOPE_API_KEY 環境変数が見つかりません。API キーを設定してください。") return None, None, None # リクエストデータを準備 headers = { "Authorization": f"Bearer {api_key}", "Content-Type": "application/json" } data = { "model": "qwen-voice-design", "input": { "action": "create", "target_model": "qwen3-tts-vd-realtime-2026-01-15", "voice_prompt": "落ち着いた中年の男性アナウンサーで、深く豊かで魅力的な声、安定した話速、明瞭な発音で、ニュース放送やドキュメンタリーのナレーションに適しています。", "preview_text": "リスナーの皆様、こんにちは。イブニングニュースへようこそ。", "preferred_name": "announcer", "language": "en" }, "parameters": { "sample_rate": 24000, "response_format": "wav" } } # シンガポールリージョンの URL。北京リージョンの場合は、https://dashscope.aliyuncs.com/api/v1/services/audio/tts/customization を使用してください。 url = "https://dashscope-intl.aliyuncs.com/api/v1/services/audio/tts/customization" try: # リクエストを送信 response = requests.post( url, headers=headers, json=data, timeout=60 # タイムアウト設定を追加 ) if response.status_code == 200: result = response.json() # 音声名を取得 voice_name = result["output"]["voice"] print(f"音声名:{voice_name}") # プレビュー音声データを取得 base64_audio = result["output"]["preview_audio"]["data"] # Base64 音声データをデコード audio_bytes = base64.b64decode(base64_audio) # 音声ファイルをローカルに保存 filename = f"{voice_name}_preview.wav" # 音声データをローカルファイルに書き込み with open(filename, 'wb') as f: f.write(audio_bytes) print(f"音声がローカルファイルに保存されました:{filename}") print(f"ファイルパス:{os.path.abspath(filename)}") return voice_name, audio_bytes, filename else: print(f"リクエストに失敗しました。ステータスコード:{response.status_code}") print(f"応答:{response.text}") return None, None, None except requests.exceptions.RequestException as e: print(f"ネットワークリクエストエラー:{e}") return None, None, None except KeyError as e: print(f"応答フォーマットエラー:必須フィールドがありません:{e}") print(f"応答:{'response' in locals() and response.text or '応答なし'}") return None, None, None except Exception as e: print(f"予期せぬエラー:{e}") return None, None, None if __name__ == "__main__": print("音声を作成中...") voice_name, audio_data, saved_filename = create_voice_and_play() if voice_name: print(f"\n音声 '{voice_name}' の作成に成功しました") print(f"音声ファイルが保存されました:'{saved_filename}'") print(f"ファイルサイズ:{os.path.getsize(saved_filename)} バイト") else: print("\n音声の作成に失敗しました")Java
Gson 依存関係をインポートする必要があります。Maven または Gradle を使用している場合は、依存関係を追加してください:
Maven
pom.xmlに以下の内容を追加します:<!-- https://mvnrepository.com/artifact/com.google.code.gson/gson --> <dependency> <groupId>com.google.code.gson</groupId> <artifactId>gson</artifactId> <version>2.13.1</version> </dependency>Gradle
build.gradleに以下の内容を追加します:// https://mvnrepository.com/artifact/com.google.code.gson/gson implementation("com.google.code.gson:gson:2.13.1")import com.google.gson.JsonObject; import com.google.gson.JsonParser; import java.io.*; import java.net.HttpURLConnection; import java.net.URL; import java.util.Base64; public class Main { public static void main(String[] args) { Main example = new Main(); example.createVoice(); } public void createVoice() { // API キーはシンガポールと北京の各リージョンで異なります。API キーの取得先:https://www.alibabacloud.com/help/zh/model-studio/get-api-key // 環境変数を設定していない場合は、以下の行を String apiKey = "sk-xxx" に置き換えてください。 String apiKey = System.getenv("DASHSCOPE_API_KEY"); // JSON リクエストボディ文字列を作成 String jsonBody = "{\n" + " \"model\": \"qwen-voice-design\",\n" + " \"input\": {\n" + " \"action\": \"create\",\n" + " \"target_model\": \"qwen3-tts-vd-realtime-2026-01-15\",\n" + " \"voice_prompt\": \"落ち着いた中年の男性アナウンサーで、深く豊かで魅力的な声、安定した話速、明瞭な発音で、ニュース放送やドキュメンタリーのナレーションに適しています。\",\n" + " \"preview_text\": \"リスナーの皆様、こんにちは。イブニングニュースへようこそ。\",\n" + " \"preferred_name\": \"announcer\",\n" + " \"language\": \"en\"\n" + " },\n" + " \"parameters\": {\n" + " \"sample_rate\": 24000,\n" + " \"response_format\": \"wav\"\n" + " }\n" + "}"; HttpURLConnection connection = null; try { // シンガポールリージョンの URL。北京リージョンの場合は、https://dashscope.aliyuncs.com/api/v1/services/audio/tts/customization を使用してください。 URL url = new URL("https://dashscope-intl.aliyuncs.com/api/v1/services/audio/tts/customization"); connection = (HttpURLConnection) url.openConnection(); // リクエストメソッドとヘッダーを設定 connection.setRequestMethod("POST"); connection.setRequestProperty("Authorization", "Bearer " + apiKey); connection.setRequestProperty("Content-Type", "application/json"); connection.setDoOutput(true); connection.setDoInput(true); // リクエストボディを送信 try (OutputStream os = connection.getOutputStream()) { byte[] input = jsonBody.getBytes("UTF-8"); os.write(input, 0, input.length); os.flush(); } // 応答を取得 int responseCode = connection.getResponseCode(); if (responseCode == HttpURLConnection.HTTP_OK) { // 応答内容を読み取り StringBuilder response = new StringBuilder(); try (BufferedReader br = new BufferedReader( new InputStreamReader(connection.getInputStream(), "UTF-8"))) { String responseLine; while ((responseLine = br.readLine()) != null) { response.append(responseLine.trim()); } } // JSON 応答を解析 JsonObject jsonResponse = JsonParser.parseString(response.toString()).getAsJsonObject(); JsonObject outputObj = jsonResponse.getAsJsonObject("output"); JsonObject previewAudioObj = outputObj.getAsJsonObject("preview_audio"); // 音声名を取得 String voiceName = outputObj.get("voice").getAsString(); System.out.println("音声名:" + voiceName); // Base64 エンコードされた音声データを取得 String base64Audio = previewAudioObj.get("data").getAsString(); // Base64 音声データをデコード byte[] audioBytes = Base64.getDecoder().decode(base64Audio); // 音声をローカルファイルに保存 String filename = voiceName + "_preview.wav"; saveAudioToFile(audioBytes, filename); System.out.println("音声がローカルファイルに保存されました:" + filename); } else { // エラー応答を読み取り StringBuilder errorResponse = new StringBuilder(); try (BufferedReader br = new BufferedReader( new InputStreamReader(connection.getErrorStream(), "UTF-8"))) { String responseLine; while ((responseLine = br.readLine()) != null) { errorResponse.append(responseLine.trim()); } } System.out.println("リクエストに失敗しました。ステータスコード:" + responseCode); System.out.println("エラー応答:" + errorResponse.toString()); } } catch (Exception e) { System.err.println("リクエストエラー:" + e.getMessage()); e.printStackTrace(); } finally { if (connection != null) { connection.disconnect(); } } } private void saveAudioToFile(byte[] audioBytes, String filename) { try { File file = new File(filename); try (FileOutputStream fos = new FileOutputStream(file)) { fos.write(audioBytes); } System.out.println("音声が保存されました:" + file.getAbsolutePath()); } catch (IOException e) { System.err.println("音声ファイルの保存エラー:" + e.getMessage()); e.printStackTrace(); } } }前のステップで生成したカスタム音声を音声合成に使用します。
この例は、システム音声を使用した DashScope SDK の「サーバーコミットモード」での音声合成に基づいています。
voiceパラメーターをボイスデザインで生成したカスタム音声に置き換えます。基本原則:ボイスデザイン時に使用したモデル(
target_model)と、その後の音声合成で使用するモデル(model)は、同一である必要があります。そうでない場合、合成は失敗します。Python
# coding=utf-8 # pyaudio のインストール手順: # APPLE Mac OS X # brew install portaudio # pip install pyaudio # Debian/Ubuntu # sudo apt-get install python-pyaudio python3-pyaudio # または # pip install pyaudio # CentOS # sudo yum install -y portaudio portaudio-devel && pip install pyaudio # Microsoft Windows # python -m pip install pyaudio import pyaudio import os import base64 import threading import time import dashscope # DashScope Python SDK バージョン 1.23.9 以降が必要です from dashscope.audio.qwen_tts_realtime import QwenTtsRealtime, QwenTtsRealtimeCallback, AudioFormat # ======= 定数設定 ======= TEXT_TO_SYNTHESIZE = [ 'そうですね?こんなスーパーが大好きです。', '特に旧正月の時期には、', 'スーパーでお買い物に行きます。', 'そして私は、', 'とてもワクワクします!', 'たくさん買いたいものがたくさんあります!' ] def init_dashscope_api_key(): """ DashScope SDK API キーを初期化します """ # API キーはシンガポールと北京の各リージョンで異なります。API キーの取得先:https://www.alibabacloud.com/help/zh/model-studio/get-api-key # 環境変数を設定していない場合は、以下の行を dashscope.api_key = "sk-xxx" に置き換えてください。 dashscope.api_key = os.getenv("DASHSCOPE_API_KEY") # ======= コールバッククラス ======= class MyCallback(QwenTtsRealtimeCallback): """ カスタム TTS ストリーミングコールバック """ def __init__(self): self.complete_event = threading.Event() self._player = pyaudio.PyAudio() self._stream = self._player.open( format=pyaudio.paInt16, channels=1, rate=24000, output=True ) def on_open(self) -> None: print('[TTS] 接続が確立されました') def on_close(self, close_status_code, close_msg) -> None: self._stream.stop_stream() self._stream.close() self._player.terminate() print(f'[TTS] 接続が終了しました。コード={close_status_code}、メッセージ={close_msg}') def on_event(self, response: dict) -> None: try: event_type = response.get('type', '') if event_type == 'session.created': print(f'[TTS] セッションが開始されました:{response["session"]["id"]}') elif event_type == 'response.audio.delta': audio_data = base64.b64decode(response['delta']) self._stream.write(audio_data) elif event_type == 'response.done': print(f'[TTS] 応答が完了しました。応答 ID:{qwen_tts_realtime.get_last_response_id()}') elif event_type == 'session.finished': print('[TTS] セッションが終了しました') self.complete_event.set() except Exception as e: print(f'[エラー] コールバックイベント処理中に例外が発生しました:{e}') def wait_for_finished(self): self.complete_event.wait() # ======= メイン実行ロジック ======= if __name__ == '__main__': init_dashscope_api_key() print('[システム] Qwen TTS Realtime を初期化中 ...') callback = MyCallback() qwen_tts_realtime = QwenTtsRealtime( # ボイスデザインと音声合成は同じモデルを使用する必要があります model="qwen3-tts-vd-realtime-2026-01-15", callback=callback, # シンガポールリージョンの URL。北京リージョンの場合は、wss://dashscope.aliyuncs.com/api-ws/v1/realtime を使用してください。 url='wss://dashscope-intl.aliyuncs.com/api-ws/v1/realtime' ) qwen_tts_realtime.connect() qwen_tts_realtime.update_session( voice="myvoice", # voice パラメーターをボイスデザインで生成したカスタム音声に置き換えます response_format=AudioFormat.PCM_24000HZ_MONO_16BIT, mode='server_commit' ) for text_chunk in TEXT_TO_SYNTHESIZE: print(f'[テキストを送信]:{text_chunk}') qwen_tts_realtime.append_text(text_chunk) time.sleep(0.1) qwen_tts_realtime.finish() callback.wait_for_finished() print(f'[メトリクス] session_id={qwen_tts_realtime.get_session_id()}, first_audio_delay={qwen_tts_realtime.get_first_audio_delay()}s')Java
import com.alibaba.dashscope.audio.qwen_tts_realtime.*; import com.alibaba.dashscope.exception.NoApiKeyException; import com.google.gson.JsonObject; import javax.sound.sampled.*; import java.io.*; import java.util.Base64; import java.util.Queue; import java.util.concurrent.CountDownLatch; import java.util.concurrent.atomic.AtomicReference; import java.util.concurrent.ConcurrentLinkedQueue; import java.util.concurrent.atomic.AtomicBoolean; public class Main { // ===== 定義 ===== private static String[] textToSynthesize = { "そうですね?こんなスーパーが大好きです。", "特に旧正月の時期には、", "スーパーでお買い物に行きます。", "そして私は、", "とてもワクワクします!", "たくさん買いたいものがたくさんあります!" }; // リアルタイムオーディオプレーヤークラス public static class RealtimePcmPlayer { private int sampleRate; private SourceDataLine line; private AudioFormat audioFormat; private Thread decoderThread; private Thread playerThread; private AtomicBoolean stopped = new AtomicBoolean(false); private Queue<String> b64AudioBuffer = new ConcurrentLinkedQueue<>(); private Queue<byte[]> RawAudioBuffer = new ConcurrentLinkedQueue<>(); // コンストラクターはオーディオフォーマットとオーディオラインを初期化します public RealtimePcmPlayer(int sampleRate) throws LineUnavailableException { this.sampleRate = sampleRate; this.audioFormat = new AudioFormat(this.sampleRate, 16, 1, true, false); DataLine.Info info = new DataLine.Info(SourceDataLine.class, audioFormat); line = (SourceDataLine) AudioSystem.getLine(info); line.open(audioFormat); line.start(); decoderThread = new Thread(new Runnable() { @Override public void run() { while (!stopped.get()) { String b64Audio = b64AudioBuffer.poll(); if (b64Audio != null) { byte[] rawAudio = Base64.getDecoder().decode(b64Audio); RawAudioBuffer.add(rawAudio); } else { try { Thread.sleep(100); } catch (InterruptedException e) { throw new RuntimeException(e); } } } } }); playerThread = new Thread(new Runnable() { @Override public void run() { while (!stopped.get()) { byte[] rawAudio = RawAudioBuffer.poll(); if (rawAudio != null) { try { playChunk(rawAudio); } catch (IOException e) { throw new RuntimeException(e); } catch (InterruptedException e) { throw new RuntimeException(e); } } else { try { Thread.sleep(100); } catch (InterruptedException e) { throw new RuntimeException(e); } } } } }); decoderThread.start(); playerThread.start(); } // オーディオチャンクを再生し、再生が完了するまでブロックします private void playChunk(byte[] chunk) throws IOException, InterruptedException { if (chunk == null || chunk.length == 0) return; int bytesWritten = 0; while (bytesWritten < chunk.length) { bytesWritten += line.write(chunk, bytesWritten, chunk.length - bytesWritten); } int audioLength = chunk.length / (this.sampleRate*2/1000); // バッファー内のオーディオの再生が完了するまで待機します Thread.sleep(audioLength - 10); } public void write(String b64Audio) { b64AudioBuffer.add(b64Audio); } public void cancel() { b64AudioBuffer.clear(); RawAudioBuffer.clear(); } public void waitForComplete() throws InterruptedException { while (!b64AudioBuffer.isEmpty() || !RawAudioBuffer.isEmpty()) { Thread.sleep(100); } line.drain(); } public void shutdown() throws InterruptedException { stopped.set(true); decoderThread.join(); playerThread.join(); if (line != null && line.isRunning()) { line.drain(); line.close(); } } } public static void main(String[] args) throws Exception { QwenTtsRealtimeParam param = QwenTtsRealtimeParam.builder() // ボイスデザインと音声合成は同じモデルを使用する必要があります .model("qwen3-tts-vd-realtime-2026-01-15") // シンガポールリージョンの URL。北京リージョンの場合は、wss://dashscope.aliyuncs.com/api-ws/v1/realtime を使用してください。 .url("wss://dashscope-intl.aliyuncs.com/api-ws/v1/realtime") // API キーはシンガポールと北京の各リージョンで異なります。API キーの取得先:https://www.alibabacloud.com/help/zh/model-studio/get-api-key // 環境変数を設定していない場合は、以下の行を .apikey("sk-xxx") に置き換えてください。 .apikey(System.getenv("DASHSCOPE_API_KEY")) .build(); AtomicReference<CountDownLatch> completeLatch = new AtomicReference<>(new CountDownLatch(1)); final AtomicReference<QwenTtsRealtime> qwenTtsRef = new AtomicReference<>(null); // リアルタイムオーディオプレーヤーインスタンスを作成 RealtimePcmPlayer audioPlayer = new RealtimePcmPlayer(24000); QwenTtsRealtime qwenTtsRealtime = new QwenTtsRealtime(param, new QwenTtsRealtimeCallback() { @Override public void onOpen() { // 接続開始時の処理 } @Override public void onEvent(JsonObject message) { String type = message.get("type").getAsString(); switch(type) { case "session.created": // セッション作成時の処理 break; case "response.audio.delta": String recvAudioB64 = message.get("delta").getAsString(); // オーディオをリアルタイムで再生 audioPlayer.write(recvAudioB64); break; case "response.done": // 応答完了時の処理 break; case "session.finished": // セッション終了時の処理 completeLatch.get().countDown(); default: break; } } @Override public void onClose(int code, String reason) { // 接続終了時の処理 } }); qwenTtsRef.set(qwenTtsRealtime); try { qwenTtsRealtime.connect(); } catch (NoApiKeyException e) { throw new RuntimeException(e); } QwenTtsRealtimeConfig config = QwenTtsRealtimeConfig.builder() .voice("myvoice") // voice パラメーターをボイスデザインで生成したカスタム音声に置き換えます .responseFormat(QwenTtsRealtimeAudioFormat.PCM_24000HZ_MONO_16BIT) .mode("server_commit") .build(); qwenTtsRealtime.updateSession(config); for (String text:textToSynthesize) { qwenTtsRealtime.appendText(text); Thread.sleep(100); } qwenTtsRealtime.finish(); completeLatch.get().await(); // オーディオ再生が完了するのを待ってからプレーヤーをシャットダウンします audioPlayer.waitForComplete(); audioPlayer.shutdown(); System.exit(0); } }
その他のサンプルコードについては、github をご参照ください。
インタラクションフロー
サーバーコミットモード
session.update イベントの session.mode プロパティを "server_commit" に設定すると、このモードが有効になります。サーバーはテキストのセグメント化と合成タイミングをインテリジェントに処理します。
インタラクションフロー:
-
クライアントが
session.updateイベントを送信します。サーバーはsession.createdおよびsession.updatedイベントで応答します。 -
クライアントが
input_text_buffer.appendイベントを送信して、サーバーのバッファーにテキストを追加します。 -
サーバーはテキストのセグメント化と合成タイミングをインテリジェントに処理し、
response.created、response.output_item.added、response.content_part.added、およびresponse.audio.deltaイベントを返します。 -
応答が完了すると、サーバーは
response.audio.done、response.content_part.done、response.output_item.done、およびresponse.doneを返します。 -
サーバーは
session.finishedで応答し、セッションを終了します。
|
ライフサイクル |
クライアントイベント |
サーバーイベント |
|
セッションの初期化 |
session.update セッション構成 |
session.created セッションが作成されました session.updated セッション構成が更新されました |
|
ユーザーテキスト入力 |
input_text_buffer.append サーバーにテキストを追加 input_text_buffer.commit サーバーにキャッシュされたテキストを即時合成 session.finish これ以上テキスト入力がないことをサーバーに通知 |
input_text_buffer.committed サーバーが送信されたテキストを受信しました |
|
サーバーのオーディオ出力 |
なし |
response.created サーバーが応答の生成を開始します response.output_item.added 応答に新しい出力コンテンツ response.content_part.added アシスタントメッセージに新しい出力コンテンツが追加されました response.audio.delta モデルによって生成された増分オーディオ response.content_part.done アシスタントメッセージのテキストまたはオーディオコンテンツストリームが完了しました response.output_item.done アシスタントメッセージの出力アイテムストリーム全体が完了しました response.audio.done オーディオ生成が完了しました response.done 応答が完了しました |
コミットモード
session.update イベントの session.mode プロパティを "commit" に設定すると、このモードが有効になります。クライアントは、応答を得るためにテキストバッファーをサーバーに能動的に送信する必要があります。
インタラクションフロー:
-
クライアントが
session.updateイベントを送信します。サーバーはsession.createdおよびsession.updatedイベントで応答します。 -
クライアントが
input_text_buffer.appendイベントを送信して、サーバーのバッファーにテキストを追加します。 -
クライアントが
input_text_buffer.commitイベントを送信してバッファーをサーバーに送信し、session.finishイベントを送信してこれ以上テキスト入力がないことを示します。 -
サーバーは
response.createdで応答し、応答の生成を開始します。 -
サーバーは
response.output_item.added、response.content_part.added、およびresponse.audio.deltaイベントで応答します。 -
応答が完了すると、サーバーは
response.audio.done、response.content_part.done、response.output_item.done、およびresponse.doneを返します。 -
サーバーは
session.finishedで応答し、セッションを終了します。
|
ライフサイクル |
クライアントイベント |
サーバーイベント |
|
セッションの初期化 |
session.update セッション構成 |
session.created セッションが作成されました session.updated セッション構成が更新されました |
|
ユーザーテキスト入力 |
input_text_buffer.append バッファーにテキストを追加 input_text_buffer.commit バッファーをサーバーに送信 input_text_buffer.clear バッファーをクリア |
input_text_buffer.committed サーバーが送信されたテキストを受信しました |
|
サーバーのオーディオ出力 |
なし |
response.created サーバーが応答の生成を開始します response.output_item.added 応答に新しい出力コンテンツ response.content_part.added アシスタントメッセージに新しい出力コンテンツが追加されました response.audio.delta モデルによって生成された増分オーディオ response.content_part.done アシスタントメッセージのテキストまたはオーディオコンテンツストリームが完了しました response.output_item.done アシスタントメッセージの出力アイテムストリーム全体が完了しました response.audio.done オーディオ生成が完了しました response.done 応答が完了しました |
命令制御
命令制御は、自然言語による記述を通じて音声表現を正確に制御する高度な音声合成機能です。複雑な音声パラメーターを調整することなく、簡単なテキスト記述で合成音声に特定のトーン、速度、感情、声の特徴を持たせることができます。
サポートされるモデル:Qwen3-TTS-Instruct-Flash-Realtime モデルでのみサポートされます。
使用方法:instructions パラメーターを使用して命令内容を指定します。例:「ファッション製品の紹介に適した、明確に上昇するイントネーションで素早く話す。」
サポートされる言語:記述テキストは中国語と英語のみをサポートします。
長さ制限:1600 トークンを超えてはなりません。
適用シナリオ:
-
オーディオブックおよびラジオドラマの吹き替え
-
広告およびプロモーションビデオの吹き替え
-
ゲームキャラクターおよびアニメーションの吹き替え
-
感情的にインテリジェントな音声アシスタント
-
ドキュメンタリーおよびニュース放送
高品質な音声記述の書き方:
-
基本原則:
-
具体的であること、曖昧でないこと:「深い」「歯切れの良い」「速いペース」など、具体的な音声特性を表す言葉を使用します。「良い響き」や「普通」など、情報量の少ない主観的な用語は避けてください。
-
多次元的であること、単一次元的でないこと:良い記述は通常、複数の次元(後述のピッチ、速度、感情など)を組み合わせます。「高音」だけのような単一次元的な記述は、特徴的な効果を生み出すには広すぎます。
-
客観的であること、主観的でないこと:個人の好みではなく、音声自体の物理的および知覚的特性に焦点を当てます。例えば、「私のお気に入りの声」ではなく、「少し高めでエネルギッシュ」を使用できます。
-
独創的であること、模倣でないこと:特定の人(有名人や俳優など)の模倣を要求するのではなく、音声の特性を記述します。そのような要求は著作権リスクを伴い、モデルは直接の模倣をサポートしていません。
-
簡潔であること、冗長でないこと:すべての単語に意味があることを確認します。同義語を繰り返したり、「非常に素晴らしい声」のような無意味な強調語を使用したりすることは避けてください。
-
-
記述の次元リファレンス:複数の次元を組み合わせて、より豊かな表現効果を生み出します。
次元
記述例
ピッチ
高い、中程度、低い、やや高い、やや低い
速度
速い、中程度、遅い、やや速い、やや遅い
感情
陽気、落ち着いた、優しい、真面目、活発、穏やか、癒し系
特徴
磁力的、歯切れの良い、ハスキー、まろやか、甘い、豊か、力強い
目的
ニュース放送、広告ナレーション、オーディオブック、アニメキャラクター、音声アシスタント、ドキュメンタリーナレーション
-
例:
-
標準的な放送スタイル:明瞭で正確な発音、完璧なアーティキュレーション
-
感情の進行効果:通常の会話から叫び声へと音量が急速に増加し、率直な性格で、興奮しやすく表現豊か
-
特殊な感情状態:泣いているために発音が少しこもり、わずかにかすれ、泣いていることによる明らかな緊張感がある
-
広告ナレーションスタイル:やや高めのピッチ、中程度の速度、エネルギーと魅力に満ち、広告に適している
-
優しく癒し系のスタイル:やや遅い速度、優しく甘いトーン、親しい友人のように思いやりがあり温かい
-
API リファレンス
機能比較
|
機能 |
Qwen3-TTS-Instruct-Flash-Realtime |
Qwen3-TTS-VD-Realtime |
Qwen3-TTS-VC-Realtime |
Qwen3-TTS-Flash-Realtime |
Qwen-TTS-Realtime |
|
サポート言語 |
中国語(標準語)、英語、スペイン語、ロシア語、イタリア語、フランス語、韓国語、日本語、ドイツ語、ポルトガル語 |
中国語(標準語)、英語、スペイン語、ロシア語、イタリア語、フランス語、韓国語、日本語、ドイツ語、ポルトガル語 |
中国語(標準語、北京語、上海語、四川語、南京語、陝西語、閩南語、天津語、広東語、音声により異なる)、英語、スペイン語、ロシア語、イタリア語、フランス語、韓国語、日本語、ドイツ語、ポルトガル語 |
中国語、英語 |
|
|
オーディオフォーマット |
pcm、wav、mp3、opus |
pcm |
|||
|
オーディオサンプルレート |
8kHz、16kHz、24kHz、48kHz |
24kHz |
|||
|
ボイスクローン |
|
|
|
||
|
ボイスデザイン |
|
|
|
||
|
SSML |
|
||||
|
LaTeX |
|
||||
|
音量調整 |
|
|
|||
|
速度調整 |
|
|
|||
|
ピッチ調整 |
|
|
|||
|
ビットレート調整 |
|
|
|||
|
タイムスタンプ |
|
||||
|
命令 |
|
|
|||
|
ストリーミング入力 |
|
||||
|
ストリーミング出力 |
|
||||
|
レート制限 |
1分あたりのリクエスト数(RPM):180 |
qwen3-tts-flash-realtime、qwen3-tts-flash-realtime-2025-11-27 RPM:180 qwen3-tts-flash-realtime-2025-09-18 RPM:10 |
RPM:10 1分あたりのトークン数(TPM):100,000 |
||
|
アクセス方法 |
Java/Python SDK、WebSocket API |
||||
|
価格 |
国際:10,000 文字あたり 0.143 ドル 中国本土:10,000 文字あたり 0.143 ドル |
国際:10,000 文字あたり 0.143353 ドル 中国本土:10,000 文字あたり 0.143353 ドル |
国際:10,000 文字あたり 0.13 ドル 中国本土:10,000 文字あたり 0.143353 ドル |
中国本土:
|
|
サポートされる音声
モデルによってサポートされる音声は異なります。リクエストを行う際は、voice リクエストパラメーターを音声リストの音声パラメーター列に記載されている値に設定してください。
| 詳細 | サポート言語 | サポートモデル |
| 名前:Cherry 説明:陽気で前向き、フレンドリーで自然な若い女性。 | 中国語、英語、フランス語、ドイツ語、ロシア語、イタリア語、スペイン語、ポルトガル語、日本語、韓国語 |
|
| 名前:Serena 説明:優しい女性 | 中国語、英語、フランス語、ドイツ語、ロシア語、イタリア語、スペイン語、ポルトガル語、日本語、韓国語 |
|
| 名前:Ethan 説明:明るく、温かく、エネルギッシュで活気のある男性の声。標準的な中国語の発音で、わずかに北方のアクセントがあります。 | 中国語、英語、フランス語、ドイツ語、ロシア語、イタリア語、スペイン語、ポルトガル語、日本語、韓国語 |
|
| 名前:Chelsie 説明:2D バーチャル彼女 | 中国語、英語、フランス語、ドイツ語、ロシア語、イタリア語、スペイン語、ポルトガル語、日本語、韓国語 |
|
| 名前:Momo 説明:陽気で可愛らしい女性の声。明るくデザインされています。 | 中国語、英語、フランス語、ドイツ語、ロシア語、イタリア語、スペイン語、ポルトガル語、日本語、韓国語 |
|
| 名前:Vivian 説明:クールで可愛らしく、少し気の強い女性の声。 | 中国語、英語、フランス語、ドイツ語、ロシア語、イタリア語、スペイン語、ポルトガル語、日本語、韓国語 |
|
| 名前:Moon 説明:月白(男性)、元気でハンサム | 中国語、英語、フランス語、ドイツ語、ロシア語、イタリア語、スペイン語、ポルトガル語、日本語、韓国語 |
|
| 名前:Maia 説明:知性と優しさを兼ね備えた女性の声。 | 中国語、英語、フランス語、ドイツ語、ロシア語、イタリア語、スペイン語、ポルトガル語、日本語、韓国語 |
|
| 名前:Kai 説明:耳のスパのような癒しの声。 | 中国語、英語、フランス語、ドイツ語、ロシア語、イタリア語、スペイン語、ポルトガル語、日本語、韓国語 |
|
| 名前:Nofish 説明:「sh」や「zh」の音を発音できない男性デザイナー。 | 中国語、英語、フランス語、ドイツ語、ロシア語、イタリア語、スペイン語、ポルトガル語、日本語、韓国語 |
|
| 名前:Bella 説明:お酒は飲むが酔拳は使わない若い女の子。 | 中国語、英語、フランス語、ドイツ語、ロシア語、イタリア語、スペイン語、ポルトガル語、日本語、韓国語 |
|
| 名前:Jennifer 説明:プレミアムで映画のようなアメリカ英語の女性の声。 | 中国語、英語、フランス語、ドイツ語、ロシア語、イタリア語、スペイン語、ポルトガル語、日本語、韓国語 |
|
| 名前:Ryan 説明:リズミカルでドラマチックな声で、リアリズムと緊張感があります。 | 中国語、英語、フランス語、ドイツ語、ロシア語、イタリア語、スペイン語、ポルトガル語、日本語、韓国語 |
|
| 名前:Katerina 説明:豊かなリズムと余韻のある成熟した女性の声。 | 中国語、英語、フランス語、ドイツ語、ロシア語、イタリア語、スペイン語、ポルトガル語、日本語、韓国語 |
|
| 名前:Aiden 説明:料理が得意な若いアメリカ人男性の声。 | 中国語、英語、フランス語、ドイツ語、ロシア語、イタリア語、スペイン語、ポルトガル語、日本語、韓国語 |
|
| 名前:Eldric Sage 説明:落ち着いて賢明な老人で、松の木のように風化した外見だが、心は鏡のように澄んでいる(男性) | 中国語、英語、フランス語、ドイツ語、ロシア語、イタリア語、スペイン語、ポルトガル語、日本語、韓国語 |
|
| 名前:Mia 説明:春の水のように優しく、初雪のように純粋(女性) | 中国語、英語、フランス語、ドイツ語、ロシア語、イタリア語、スペイン語、ポルトガル語、日本語、韓国語 |
|
| 名前:Mochi 説明:子供らしい無邪気さを持ちながらも禅のような知恵を持つ、賢くて明るい「小さな大人」の声。 | 中国語、英語、フランス語、ドイツ語、ロシア語、イタリア語、スペイン語、ポルトガル語、日本語、韓国語 |
|
| 名前:Bellona 説明:力強く響き渡る声で、明瞭な発音がキャラクターに命を吹き込み、リスナーの情熱をかき立てます。剣のぶつかり合う音と蹄の雷鳴が夢の中で響き、完璧にクリアで共鳴するトーンを通じて無数の声の世界を明らかにします。 | 中国語、英語、フランス語、ドイツ語、ロシア語、イタリア語、スペイン語、ポルトガル語、日本語、韓国語 |
|
| 名前:Vincent 説明:独特のかすれたスモーキーな声で、広大な軍隊と英雄的な冒険の物語を即座に思い起こさせます。 | 中国語、英語、フランス語、ドイツ語、ロシア語、イタリア語、スペイン語、ポルトガル語、日本語、韓国語 |
|
| 名前:Bunny 説明:「萌え」要素にあふれた女性キャラクター。 | 中国語、英語、フランス語、ドイツ語、ロシア語、イタリア語、スペイン語、ポルトガル語、日本語、韓国語 |
|
| 名前:Neil 説明:フラットなベースラインのイントネーションと、正確で明瞭な発音を持つプロのニュースアンカーの声。 | 中国語、英語、フランス語、ドイツ語、ロシア語、イタリア語、スペイン語、ポルトガル語、日本語、韓国語 |
|
| 名前:Elias 説明:学術的な厳密さを保ちつつ、物語的な手法を用いて複雑なトピックを消化しやすいモジュールに分解します(女性)。 | 中国語、英語、フランス語、ドイツ語、ロシア語、イタリア語、スペイン語、ポルトガル語、日本語、韓国語 |
|
| 名前:Arthur 説明:時間と乾いたタバコによって風化した素朴な声で、村の物語や奇妙な出来事をのんびりと語ります。 | 中国語、英語、フランス語、ドイツ語、ロシア語、イタリア語、スペイン語、ポルトガル語、日本語、韓国語 |
|
| 名前:Nini 説明:餅のように柔らかく粘りのある声で、「お兄ちゃん」という長引く呼び声は骨まで溶けるほど甘い。 | 中国語、英語、フランス語、ドイツ語、ロシア語、イタリア語、スペイン語、ポルトガル語、日本語、韓国語 |
|
| 名前:Ebona 説明:ささやくような声で、あなたの内面の最も暗い隅で錆びた鍵がゆっくりと回るような声。そこには、認められていない子供時代の影や未知の恐怖が隠されています。 | 中国語、英語、フランス語、ドイツ語、ロシア語、イタリア語、スペイン語、ポルトガル語、日本語、韓国語 |
|
| 名前:Seren 説明:優しく癒される声で、より早く眠りにつくのを助けます。おやすみなさい、良い夢を。 | 中国語、英語、フランス語、ドイツ語、ロシア語、イタリア語、スペイン語、ポルトガル語、日本語、韓国語 |
|
| 名前:Pip 説明:いたずら好きでやんちゃだが、子供らしい無邪気さを残している。これはあなたの覚えているしんちゃんですか?(男性) | 中国語、英語、フランス語、ドイツ語、ロシア語、イタリア語、スペイン語、ポルトガル語、日本語、韓国語 |
|
| 名前:Stella 説明:普段は甘ったるくぼんやりした声だが、「月に代わってお仕置きよ!」と叫ぶと、否定できない愛と正義に満ち溢れます。 | 中国語、英語、フランス語、ドイツ語、ロシア語、イタリア語、スペイン語、ポルトガル語、日本語、韓国語 |
|
| 名前:Bodega 説明:情熱的なスペインのおじさん | 中国語、英語、フランス語、ドイツ語、ロシア語、イタリア語、スペイン語、ポルトガル語、日本語、韓国語 |
|
| 名前:Sonrisa 説明:温かく社交的なラテンアメリカの女性。 | 中国語、英語、フランス語、ドイツ語、ロシア語、イタリア語、スペイン語、ポルトガル語、日本語、韓国語 |
|
| 名前:Alek 説明:最初はロシアのように冷たく聞こえるが、ウールのコートの下は温かい声。 | 中国語、英語、フランス語、ドイツ語、ロシア語、イタリア語、スペイン語、ポルトガル語、日本語、韓国語 |
|
| 名前:Dolce 説明:のんびりした中年のイタリア人男性 | 中国語、英語、フランス語、ドイツ語、ロシア語、イタリア語、スペイン語、ポルトガル語、日本語、韓国語 |
|
| 名前:Sohee 説明:優しく、陽気で、感情表現豊かな韓国のお姉さん的存在。 | 中国語、英語、フランス語、ドイツ語、ロシア語、イタリア語、スペイン語、ポルトガル語、日本語、韓国語 |
|
| 名前:Ono Anna 説明:元気でいたずら好きな若い女性で、幼なじみ。 | 中国語、英語、フランス語、ドイツ語、ロシア語、イタリア語、スペイン語、ポルトガル語、日本語、韓国語 |
|
| 名前:Lenn 説明:核心は合理的だが、細部では反抗的—スーツを着てポストパンクを聴く若いドイツ人男性。 | 中国語、英語、フランス語、ドイツ語、ロシア語、イタリア語、スペイン語、ポルトガル語、日本語、韓国語 |
|
| 名前:Emilien 説明:ロマンチックで成熟したフランス人男性 | 中国語、英語、フランス語、ドイツ語、ロシア語、イタリア語、スペイン語、ポルトガル語、日本語、韓国語 |
|
| 名前:Andre 説明:魅力的で、自然で、心地よく、落ち着いた男性の声。 | 中国語、英語、フランス語、ドイツ語、ロシア語、イタリア語、スペイン語、ポルトガル語、日本語、韓国語 |
|
| 名前:Radio Gol 説明:サッカーの詩人、Rádio Gol の声!「今日は名前を使ってサッカーの試合を実況します。」 | 中国語、英語、フランス語、ドイツ語、ロシア語、イタリア語、スペイン語、ポルトガル語、日本語、韓国語 |
|
| 名前:Shanghai-Jada 説明:エネルギッシュな上海出身の女性 | 中国語(上海語)、英語、フランス語、ドイツ語、ロシア語、イタリア語、スペイン語、ポルトガル語、日本語、韓国語 |
|
| 名前:Beijing-Dylan 説明:北京の胡同で育ったティーンエイジャーの少年。 | 中国語(北京語)、英語、フランス語、ドイツ語、ロシア語、イタリア語、スペイン語、ポルトガル語、日本語、韓国語 |
|
| 名前:Nanjing-Li 説明: 患者、男性のヨガ講師。 | 中国語(南京語)、英語、フランス語、ドイツ語、ロシア語、イタリア語、スペイン語、ポルトガル語、日本語、韓国語 |
|
| 名前:Shaanxi-Marcus 説明:顔が広く口数が少なく、心が誠実で声が低い—本物の陝西の味。 | 中国語(陝西語)、英語、フランス語、ドイツ語、ロシア語、イタリア語、スペイン語、ポルトガル語、日本語、韓国語 |
|
| 名前:Minnan-Roy 説明:ユーモラスで、率直で、活発な台湾の若い男性の声。 | 中国語(閩南語)、英語、フランス語、ドイツ語、ロシア語、イタリア語、スペイン語、ポルトガル語、日本語、韓国語 |
|
| 名前:Tianjin-Peter 説明: 天津クロストークのプロのツッコミ役の声。 | 中国語(天津語)、英語、フランス語、ドイツ語、ロシア語、イタリア語、スペイン語、ポルトガル語、日本語、韓国語 |
|
| 名前:Sichuan-Sunny 説明:心がとろけるような甘さの四川の女の子の声。 | 中国語(四川語)、英語、フランス語、ドイツ語、ロシア語、イタリア語、スペイン語、ポルトガル語、日本語、韓国語 |
|
| 名前:Sichuan-Eric 説明:世俗から離れた四川省成都出身の男性。 | 中国語(四川語)、英語、フランス語、ドイツ語、ロシア語、イタリア語、スペイン語、ポルトガル語、日本語、韓国語 |
|
| 名前:Cantonese-Rocky 説明:ユーモラスで機知に富んだロッキーの声、オンラインチャットのためにここにいます。 | 中国語(広東語)、英語、フランス語、ドイツ語、ロシア語、イタリア語、スペイン語、ポルトガル語、日本語、韓国語 |
|
| 名前:Cantonese-Kiki 説明:香港出身の甘い親友の女性。 | 中国語(広東語)、英語、フランス語、ドイツ語、ロシア語、イタリア語、スペイン語、ポルトガル語、日本語、韓国語 |
|