WebSocket を介したストリーミング音声合成は、初回パケットレイテンシーが低いことが特徴です。リアルタイム音声合成は、ストリーミング入力と出力、音声クローニング、音声デザイン、および音声アシスタント、オーディオブック、インテリジェントカスタマーサービス向けのきめ細かな音声制御をサポートします。
概要
低レイテンシーの双方向 WebSocket ストリーミングプロトコルを介して、テキストをリアルタイムで音声に変換します。
初回パケットレイテンシーが低いストリーミング入力と出力
話速、ピッチ、音量、ビットレートを調整可能で、詳細な音声制御が可能
主流の音声フォーマット (PCM、WAV、MP3、Opus) と互換性があり、最大 48 kHz のサンプルレートで出力
命令制御をサポートし、自然言語の命令を通じて音声の表現力を制御できます
感情およびリッチ言語タグをサポートし、テキスト内に感情や効果音のタグを埋め込むことができます
オーディオブックや教材のナレーションなどのバッチシナリオでは、非リアルタイム音声合成を使用してください。モデル選択のガイダンスについては、「音声合成」をご参照ください。
前提条件
API キーを設定し、それを環境変数として設定します。
DashScope SDK を介して API を呼び出す場合は、最新の SDK をインストールしてください。
クイックスタート
以下の例は、各モデルの音声合成のデモです。その他の例とパラメーターの詳細については、「API リファレンス」をご参照ください。
Qwen-Audio-TTS
以下の例では、システム音声を使用して音声を合成します。
命令制御機能を使用するには、instruction パラメーターで命令を設定します。
Python
# coding=utf-8
import os
import dashscope
from dashscope.audio.tts_v2 import *
# シンガポールリージョンと北京リージョンでは API キーが異なります。API キーの取得方法:https://www.alibabacloud.com/help/model-studio/get-api-key
# 環境変数を設定していない場合は、次の行を中国 Model Studio の API キーに置き換えてください:dashscope.api_key = "sk-xxx"
dashscope.api_key = os.environ.get('DASHSCOPE_API_KEY')
# 以下はシンガポールリージョンの設定です。"{WorkspaceId}" を実際のワークスペース ID に置き換えてください。設定はリージョンによって異なります。
dashscope.base_websocket_api_url='wss://{WorkspaceId}.ap-southeast-1.maas.aliyuncs.com/api-ws/v1/inference'
# モデル
# qwen-audio-3.0-tts-flash/qwen-audio-3.0-tts-plus: longanlingxi などの音声を使用します。
# 各音声は異なる言語をサポートしています。日本語や韓国語など、中国語以外の言語を合成するには、ターゲット言語をサポートする音声を選択してください。詳細は音声リストをご参照ください。
model = "qwen-audio-3.0-tts-flash"
# 音声
voice = "longanlingxi"
# SpeechSynthesizer をインスタンス化し、コンストラクターでモデルや音声などのリクエストパラメーターを渡します
synthesizer = SpeechSynthesizer(model=model, voice=voice)
# 合成するテキストを送信し、バイナリ音声を取得します
audio = synthesizer.call("今日の天気はどうですか?")
# 最初のテキスト送信では WebSocket 接続を確立する必要があるため、初回パケットレイテンシーには接続確立時間が含まれます
print('[Metric] requestId: {}, first-packet latency: {} ms'.format(
synthesizer.get_last_request_id(),
synthesizer.get_first_package_delay()))
# 音声をローカルファイルに保存します
with open('output.mp3', 'wb') as f:
f.write(audio)Java
import com.alibaba.dashscope.audio.ttsv2.SpeechSynthesisParam;
import com.alibaba.dashscope.audio.ttsv2.SpeechSynthesizer;
import com.alibaba.dashscope.utils.Constants;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
public class Main {
// モデル
// qwen-audio-3.0-tts-flash/qwen-audio-3.0-tts-plus: longanlingxi などの音声を使用します。
// 各音声は異なる言語をサポートしています。日本語や韓国語など、中国語以外の言語を合成するには、ターゲット言語をサポートする音声を選択してください。詳細は音声リストをご参照ください。
private static String model = "qwen-audio-3.0-tts-flash";
// 音声
private static String voice = "longanlingxi";
public static void streamAudioDataToSpeaker() {
// リクエストパラメーター
SpeechSynthesisParam param =
SpeechSynthesisParam.builder()
// シンガポールリージョンと北京リージョンでは API キーが異なります。API キーの取得方法:https://www.alibabacloud.com/help/model-studio/get-api-key
// 環境変数を設定していない場合は、次の行を中国 Model Studio の API キーに置き換えてください:.apiKey("sk-xxx")
.apiKey(System.getenv("DASHSCOPE_API_KEY"))
.model(model) // モデル
.voice(voice) // 音声
.build();
// 同期モード:コールバックを無効にします (2番目のパラメーターは null)
SpeechSynthesizer synthesizer = new SpeechSynthesizer(param, null);
ByteBuffer audio = null;
try {
// 音声が返されるまでブロックします
audio = synthesizer.call("今日の天気はどうですか?");
} catch (Exception e) {
throw new RuntimeException(e);
} finally {
// タスクが完了したら WebSocket 接続を閉じます
synthesizer.getDuplexApi().close(1000, "bye");
}
if (audio != null) {
// 音声データをローカルファイル "output.mp3" に保存します
File file = new File("output.mp3");
// 最初のテキスト送信では WebSocket 接続を確立する必要があるため、初回パケットレイテンシーには接続確立時間が含まれます
// 注意:getFirstPackageDelay() には dashscope-sdk-java 2.18.0 以降が必要です
System.out.println(
"[Metric] requestId: "
+ synthesizer.getLastRequestId()
+ ", first-packet latency (ms): "
+ synthesizer.getFirstPackageDelay());
try (FileOutputStream fos = new FileOutputStream(file)) {
fos.write(audio.array());
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
public static void main(String[] args) {
// 以下はシンガポールリージョンの設定です。"{WorkspaceId}" を実際のワークスペース ID に置き換えてください。設定はリージョンによって異なります。
Constants.baseWebsocketApiUrl = "wss://{WorkspaceId}.ap-southeast-1.maas.aliyuncs.com/api-ws/v1/inference";
streamAudioDataToSpeaker();
System.exit(0);
}
}CosyVoice
以下の例では、システム音声を使用して音声を合成します (「CosyVoice 音声リスト」をご参照ください)。
命令制御機能を使用するには、instruction パラメーターで命令を設定します。
Python
# coding=utf-8
import os
import dashscope
from dashscope.audio.tts_v2 import *
# シンガポールリージョンと北京リージョンでは API キーが異なります。API キーの取得方法:https://www.alibabacloud.com/help/model-studio/get-api-key
# 環境変数を設定していない場合は、次の行を中国 Model Studio の API キーに置き換えてください:dashscope.api_key = "sk-xxx"
dashscope.api_key = os.environ.get('DASHSCOPE_API_KEY')
# 以下はシンガポールリージョンの設定です。"{WorkspaceId}" を実際のワークスペース ID に置き換えてください。設定はリージョンによって異なります。
dashscope.base_websocket_api_url='wss://{WorkspaceId}.ap-southeast-1.maas.aliyuncs.com/api-ws/v1/inference'
# モデル
# モデルのバージョンごとに対応する音声が必要です:
# cosyvoice-v3-flash/cosyvoice-v3-plus: longanyang などの音声を使用します。
# cosyvoice-v2: longxiaochun_v2 などの音声を使用します。
# 各音声は異なる言語をサポートしています。日本語や韓国語など、中国語以外の言語を合成するには、ターゲット言語をサポートする音声を選択してください。詳細は Qwen-Audio-TTS/CosyVoice 音声リストをご参照ください。
model = "cosyvoice-v3-flash"
# 音声
voice = "longanyang"
# SpeechSynthesizer をインスタンス化し、コンストラクターでモデルや音声などのリクエストパラメーターを渡します
synthesizer = SpeechSynthesizer(model=model, voice=voice)
# 合成するテキストを送信し、バイナリ音声を取得します
audio = synthesizer.call("今日の天気はどうですか?")
# 最初のテキスト送信では WebSocket 接続を確立する必要があるため、初回パケットレイテンシーには接続確立時間が含まれます
print('[Metric] requestId: {}, first-packet latency: {} ms'.format(
synthesizer.get_last_request_id(),
synthesizer.get_first_package_delay()))
# 音声をローカルファイルに保存します
with open('output.mp3', 'wb') as f:
f.write(audio)Java
import com.alibaba.dashscope.audio.ttsv2.SpeechSynthesisParam;
import com.alibaba.dashscope.audio.ttsv2.SpeechSynthesizer;
import com.alibaba.dashscope.utils.Constants;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
public class Main {
// モデル
// モデルのバージョンごとに対応する音声が必要です:
// cosyvoice-v3-flash/cosyvoice-v3-plus: longanyang などの音声を使用します。
// cosyvoice-v2: longxiaochun_v2 などの音声を使用します。
// 各音声は異なる言語をサポートしています。日本語や韓国語など、中国語以外の言語を合成するには、ターゲット言語をサポートする音声を選択してください。詳細は Qwen-Audio-TTS/CosyVoice 音声リストをご参照ください。
private static String model = "cosyvoice-v3-flash";
// 音声
private static String voice = "longanyang";
public static void streamAudioDataToSpeaker() {
// リクエストパラメーター
SpeechSynthesisParam param =
SpeechSynthesisParam.builder()
// シンガポールリージョンと北京リージョンでは API キーが異なります。API キーの取得方法:https://www.alibabacloud.com/help/model-studio/get-api-key
// 環境変数を設定していない場合は、次の行を中国 Model Studio の API キーに置き換えてください:.apiKey("sk-xxx")
.apiKey(System.getenv("DASHSCOPE_API_KEY"))
.model(model) // モデル
.voice(voice) // 音声
.build();
// 同期モード:コールバックを無効にします (2番目のパラメーターは null)
SpeechSynthesizer synthesizer = new SpeechSynthesizer(param, null);
ByteBuffer audio = null;
try {
// 音声が返されるまでブロックします
audio = synthesizer.call("今日の天気はどうですか?");
} catch (Exception e) {
throw new RuntimeException(e);
} finally {
// タスクが完了したら WebSocket 接続を閉じます
synthesizer.getDuplexApi().close(1000, "bye");
}
if (audio != null) {
// 音声データをローカルファイル "output.mp3" に保存します
File file = new File("output.mp3");
// 最初のテキスト送信では WebSocket 接続を確立する必要があるため、初回パケットレイテンシーには接続確立時間が含まれます
// 注意:getFirstPackageDelay() には dashscope-sdk-java 2.18.0 以降が必要です
System.out.println(
"[Metric] requestId: "
+ synthesizer.getLastRequestId()
+ ", first-packet latency (ms): "
+ synthesizer.getFirstPackageDelay());
try (FileOutputStream fos = new FileOutputStream(file)) {
fos.write(audio.array());
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
public static void main(String[] args) {
// 以下はシンガポールリージョンの設定です。"{WorkspaceId}" を実際のワークスペース ID に置き換えてください。設定はリージョンによって異なります。
Constants.baseWebsocketApiUrl = "wss://{WorkspaceId}.ap-southeast-1.maas.aliyuncs.com/api-ws/v1/inference";
streamAudioDataToSpeaker();
System.exit(0);
}
}Qwen-TTS
以下の例では、システム音声を使用して音声を合成します (「サポートされている音声」をご参照ください)。
命令制御機能を使用するには、model を qwen3-tts-instruct-flash-realtime に置き換え、instructions パラメーターで命令を設定します。
Python
Server commit モード
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/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(
# 命令制御を使用するには、モデルを qwen3-tts-instruct-flash-realtime に置き換えます
model='qwen3-tts-flash-realtime',
callback=callback,
# シンガポールリージョン
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,
# 命令制御を使用するには、以下の行のコメントを解除し、モデルを 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(),
))
Commit モード
import base64
import os
import threading
import dashscope
from dashscope.audio.qwen_tts_realtime import *
qwen_tts_realtime: QwenTtsRealtime = None
text_to_synthesize = [
'これは最初の文です。',
'これは2番目の文です。',
'これは3番目の文です。',
]
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/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(
# 命令制御を使用するには、モデルを qwen3-tts-instruct-flash-realtime に置き換えます
model='qwen3-tts-flash-realtime',
callback=callback,
# シンガポールリージョン
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,
# 命令制御を使用するには、以下の行のコメントを解除し、モデルを 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
Server commit モード
appendText()
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.*;
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 = {
"そうですよね?こういうスーパー大好きなんです。",
"特に旧正月の時期は、",
"スーパーに買い物に行きます。",
"そして、",
"ものすごくワクワクします!",
"たくさん買いたくなります!"
};
public static QwenTtsRealtimeAudioFormat ttsFormat = QwenTtsRealtimeAudioFormat.PCM_24000HZ_MONO_16BIT;
// リアルタイム 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<>();
private ByteArrayOutputStream totalAudioStream = new ByteArrayOutputStream();
// オーディオフォーマットとオーディオラインを初期化します。
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);
// オーディオデータを totalAudioStream に書き込みます。
try {
totalAudioStream.write(rawAudio);
} catch (IOException e) {
throw new RuntimeException(e);
}
} 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, IOException {
stopped.set(true);
decoderThread.join();
playerThread.join();
// 完全なオーディオファイルを保存します。
File file = new File("TotalAudio_"+ttsFormat.getSampleRate()+"."+ttsFormat.getFormat());
try (FileOutputStream fos = new FileOutputStream(file)) {
fos.write(totalAudioStream.toByteArray());
}
if (line != null && line.isRunning()) {
line.drain();
line.close();
}
}
}
public static void main(String[] args) throws InterruptedException, LineUnavailableException, IOException {
QwenTtsRealtimeParam param = QwenTtsRealtimeParam.builder()
// 命令制御を使用するには、モデルを qwen3-tts-instruct-flash-realtime に置き換えます。
.model("qwen3-tts-flash-realtime")
// 中国 (北京) リージョン
.url("wss://dashscope-intl.aliyuncs.com/api-ws/v1/realtime")
// API キーはシンガポールと中国 (北京) で異なります。https://www.alibabacloud.com/help/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":
// セッション作成を処理します。
if (message.has("session")) {
String eventId = message.get("event_id").getAsString();
String sessionId = message.get("session").getAsJsonObject().get("id").getAsString();
System.out.println("[onEvent] session.created, session_id: "
+ sessionId + ", event_id: " + eventId);
}
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(ttsFormat)
.mode("server_commit")
// 命令制御を使用するには、以下の行のコメントを解除し、モデルを 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);
}
}
Commit モード
commit()
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.*;
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 Main {
public static QwenTtsRealtimeAudioFormat ttsFormat = QwenTtsRealtimeAudioFormat.PCM_24000HZ_MONO_16BIT;
// リアルタイム 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<>();
private ByteArrayOutputStream totalAudioStream = new ByteArrayOutputStream();
// オーディオフォーマットとオーディオラインを初期化します。
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);
// オーディオデータを totalAudioStream に書き込みます。
try {
totalAudioStream.write(rawAudio);
} catch (IOException e) {
throw new RuntimeException(e);
}
} 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();
// 完全なオーディオファイルを保存します。
File file = new File("TotalAudio_"+ttsFormat.getSampleRate()+"."+ttsFormat.getFormat());
try (FileOutputStream fos = new FileOutputStream(file)) {
fos.write(totalAudioStream.toByteArray());
} catch (FileNotFoundException e) {
throw new RuntimeException(e);
} catch (IOException e) {
throw new RuntimeException(e);
}
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()
// 命令制御を使用するには、モデルを qwen3-tts-instruct-flash-realtime に置き換えます。
.model("qwen3-tts-flash-realtime")
// 中国 (北京) リージョン
.url("wss://dashscope-intl.aliyuncs.com/api-ws/v1/realtime")
// API キーはシンガポールと中国 (北京) で異なります。https://www.alibabacloud.com/help/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() {
@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);
// オーディオをリアルタイムで再生します。
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("[Metric] response: " + qwenTtsRef.get().getResponseId() +
", first audio delay: " + qwenTtsRef.get().getFirstAudioDelay() + " ms");
}
completeLatch.get().countDown();
default:
break;
}
}
@Override
public void onClose(int code, String reason) {
System.out.println("接続終了コード: " + code + ", 理由: " + reason);
try {
// 再生が完了するのを待ってから、プレーヤーをシャットダウンします。
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(ttsFormat)
.mode("commit")
// 命令制御を使用するには、以下の行のコメントを解除し、モデルを 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);
}
}
セッション設定
Qwen-TTS 対話モード
Qwen-TTS Realtime API は、2つの対話モードを提供します:
`server_commit` モード:サーバーがテキスト分割と合成タイミングを自動的に処理します。大量のテキストブロックを連続して合成するのに適しています。クライアントは、分割や送信を管理せずにテキストを追加します。
`commit` モード:クライアントが明示的にテキストバッファーを送信して合成をトリガーします。会話型 AI のターンごとの合成など、合成タイミングを正確に制御する必要があるシナリオに適しています。
対話モードの切り替え:
WebSocket:
session.updateイベントのmodeフィールドを設定します。{ "type": "session.update", "session": { "mode": "server_commit" } }Python SDK:
update_sessionメソッドのmodeパラメーターを設定します。qwen_tts_realtime.update_session( voice='Cherry', response_format=AudioFormat.PCM_24000HZ_MONO_16BIT, mode='server_commit' )Java SDK:
QwenTtsRealtimeConfig.builder()を介してmodeパラメーターを設定します。QwenTtsRealtimeConfig config = QwenTtsRealtimeConfig.builder() .voice("Cherry") .responseFormat(ttsFormat) .mode("server_commit") .build(); qwenTtsRealtime.updateSession(config);
完全な SDK コード例については、「Python SDK」および「Java SDK」をご参照ください。WebSocket イベントのライフサイクルと接続の再利用の詳細については、「WebSocket API リファレンス」をご参照ください。
高度な機能
命令制御
命令制御は、自然言語の記述を使用して、複雑な音声パラメーターを設定することなく、音声のトーン、速度、感情、音色の特性を調整します。
モデル別の命令仕様:
Qwen-Audio-TTS
サポートモデル:qwen-audio-3.0-tts-plus、qwen-audio-3.0-tts-flash
システム音声と音声クローニング音声:任意の命令を受け付けます。
CosyVoice
サポートモデル:cosyvoice-v3.5-plus、cosyvoice-v3.5-flash、cosyvoice-v3-plus、cosyvoice-v3-flash
命令フォーマットの要件はモデルによって異なります:
cosyvoice-v3.5-plus、cosyvoice-v3.5-flash:
音声クローニング/デザイン音声:任意の命令を受け付けます。
システム音声:v3.5 はシステム音声をサポートしていません。
cosyvoice-v3-plus:
音声クローニング/デザイン音声:命令制御をサポートしていません。
システム音声:命令は固定のフォーマットと内容を使用する必要があります。「CosyVoice 音声リスト」をご参照ください。
cosyvoice-v3-flash:
音声クローニング/デザイン音声:任意の命令を受け付けます。
システム音声:命令は固定のフォーマットと内容を使用する必要があります。「CosyVoice 音声リスト」をご参照ください。
使用方法:instruction パラメーターで命令内容を指定します。
命令テキストでサポートされる言語:
cosyvoice-v3.5-plus、cosyvoice-v3.5-flash:
音声クローニング/デザイン音声:中国語、英語、フランス語、ドイツ語、日本語、韓国語、ロシア語、ポルトガル語、タイ語、インドネシア語、ベトナム語。
システム音声:v3.5 はシステム音声をサポートしていません。
cosyvoice-v3-plus:
音声クローニング/デザイン音声:中国語、英語、フランス語、ドイツ語、日本語、韓国語、ロシア語。
システム音声:命令は固定のフォーマットと内容を使用する必要があります。「CosyVoice 音声リスト」をご参照ください。
cosyvoice-v3-flash:
音声クローニング/デザイン音声:中国語、英語、フランス語、ドイツ語、日本語、韓国語、ロシア語。
システム音声:中国語のみ。
命令テキストの長さ制限:最大 100 文字。中国語の文字 (簡体字/繁体字中国語、日本の漢字、韓国の漢字を含む) はそれぞれ 2 文字としてカウントされます。その他すべての文字 (句読点、アルファベット、数字、日本のかな、韓国のハングルなど) はそれぞれ 1 文字としてカウントされます。
Qwen-TTS
サポートモデル:Qwen3-TTS-Instruct-Flash-Realtime シリーズモデルのみ。
使用方法:instructions パラメーターで命令内容を指定します。
命令テキストでサポートされる言語:中国語と英語のみ。
命令テキストの長さ制限:最大 1,600 トークン。
ユースケース:
オーディオブックとラジオドラマのナレーション
広告およびプロモーションビデオのナレーション
ゲームキャラクターとアニメーションのナレーション
感情表現豊かな音声アシスタント
ドキュメンタリーとニュース放送のナレーション
効果的な音声記述の作成:
基本原則:
曖昧ではなく、具体的に:「深い」「張りのある」「少し速いペース」など、声質を表す言葉を使用します。「良い」や「普通」などの主観的または曖昧な用語は避けてください。
一次元的ではなく、多次元的に:良い記述は通常、複数の次元 (性別、年齢、感情など) をカバーします。「女性の声」とだけ書くのは、特徴的な音色を生成するには広すぎます。
主観的ではなく、客観的に:声の物理的および知覚的な特性に焦点を当てます。例えば、「私のお気に入りの声」ではなく、「エネルギッシュなトーンで高めのピッチ」を使用します。
模倣ではなく、独創的に:特定の人 (有名人や俳優など) の模倣を要求するのではなく、声質を記述します。モデルは模倣をサポートしておらず、著作権リスクをもたらす可能性があります。
冗長ではなく、簡潔に:すべての単語が目的を果たすようにします。繰り返しの同義語や無意味な修飾語は避けてください。
記述の次元リファレンス:
以下の次元を組み合わせて声を記述します。含める次元が多いほど、出力はより正確になります。
次元
記述例
性別
男性、女性、中性的
年齢
子供 (5-12)、ティーンエイジャー (13-18)、若者 (19-35)、中年 (36-55)、高齢者 (55+)
ピッチ
高い、中程度、低い、やや高い、やや低い
速度
速い、中程度、遅い、やや速い、やや遅い
感情
陽気、穏やか、優しい、真面目、活発、落ち着いた、癒し系
特徴
磁力的、張りのある、ハスキー、まろやか、甘い、豊か、力強い
ユースケース
ニュース放送、広告ナレーション、オーディオブック、アニメキャラクター、音声アシスタント、ドキュメンタリーナレーション
例:
標準的な放送スタイル:完璧な発音で明瞭かつ正確なアーティキュレーション
若々しく活発な女性の声、速めのペースで顕著な上昇イントネーション、ファッション製品の紹介に適している
落ち着いた中年男性、遅いペース、深く磁力的な声、ニュースの読み上げやドキュメンタリーのナレーションに適している
優しく知的な女性、30歳前後、均一なトーン、オーディオブックのナレーションに適している
かわいい子供の声、約8歳の女の子、少し子供っぽい話し方、アニメキャラクターのナレーションに適している
方言
このセクションでは、中国語の方言 (河南方言、四川方言、広東語など) で音声を生成する方法について説明します。設定方法はモデルと音声タイプによって異なります。
モデル別の方言設定:
Qwen-Audio-TTS
システム音声:以下のいずれかの音声タイプを選択します:
方言サポートが組み込まれたシステム音声。追加設定なしで対応する方言を出力します。
命令制御をサポートし、命令テキストを通じて特定の方言を出力するように設定できる音声。
音声クローニング音声:命令制御機能を通じて設定します。例えば、命令テキストを
请用河南话表达に設定します。
サポートされている方言:「Qwen-Audio-TTS」の各モデルの「サポート言語」列をご参照ください。
CosyVoice
システム音声:「CosyVoice 音声リスト」から以下のいずれかの音声タイプを選択します:
方言サポートが組み込まれたシステム音声 (例:
longshange_v3)。追加設定なしで対応する方言を出力します。命令制御をサポートし、命令テキストを通じて特定の方言を出力するように設定できる音声 (例:
longanhuan_v3)。
音声クローニング音声:命令制御機能を通じて設定します。例えば、命令テキストを
请用河南话表达に設定します。音声デザイン音声:方言をサポートしていません。
サポートされている方言:「CosyVoice」の各モデルの「サポート言語」列をご参照ください。
例:cosyvoice-v3-flash と longanhuan_v3 音声を使用し、命令テキストを "请用河南话表达。" に設定して、河南方言の音声を生成します。
# coding=utf-8
import os
import dashscope
from dashscope.audio.tts_v2 import *
# シンガポールリージョンと北京リージョンでは API キーが異なります。API キーの取得方法:https://www.alibabacloud.com/help/model-studio/get-api-key
# 環境変数を設定していない場合は、次の行を中国 Model Studio の API キーに置き換えてください:dashscope.api_key = "sk-xxx"
dashscope.api_key = os.environ.get('DASHSCOPE_API_KEY')
# 以下はシンガポールリージョンの設定です。"{WorkspaceId}" を実際のワークスペース ID に置き換えてください。設定はリージョンによって異なります。
dashscope.base_websocket_api_url='wss://{WorkspaceId}.ap-southeast-1.maas.aliyuncs.com/api-ws/v1/inference'
# モデル
# モデルのバージョンごとに対応する音声が必要です:
# cosyvoice-v3-flash/cosyvoice-v3-plus: longanyang などの音声を使用します。
# cosyvoice-v2: longxiaochun_v2 などの音声を使用します。
# ターゲット言語に適した音声を選択してください
model = "cosyvoice-v3-flash"
# 音声
voice = "longanhuan_v3"
# SpeechSynthesizer をインスタンス化し、コンストラクターでモデルや音声などのリクエストパラメーターを渡します
synthesizer = SpeechSynthesizer(model=model, voice=voice, instruction="请用河南话表达。")
# 合成するテキストを送信し、バイナリ音声を取得します
audio = synthesizer.call("叫你去买盐,你买回来一袋面,这不是弄啥嘞吗!")
# 最初のテキスト送信では WebSocket 接続を確立する必要があるため、初回パケットレイテンシーには接続確立時間が含まれます
print('[Metric] requestId: {}, first-packet latency: {} ms'.format(
synthesizer.get_last_request_id(),
synthesizer.get_first_package_delay()))
# 音声をローカルファイルに保存します
with open('output.mp3', 'wb') as f:
f.write(audio)Qwen-TTS
システム音声:方言サポートが組み込まれたシステム音声を使用します。「サポートされている音声」の Qwen-TTS 音声リストをご参照ください。
音声クローニング音声:方言をサポートしていません。
音声デザイン音声:方言をサポートしていません。
サポートされている方言:「Qwen3-TTS」の各モデルの「サポート言語」列をご参照ください。
感情およびリッチ言語タグ
Qwen-Audio-TTS シリーズモデルは、合成するテキスト (text パラメーター) に感情およびリッチ言語タグを直接埋め込むことをサポートしています。これらのタグは、感情表現を制御したり、指定した位置に声の効果 (笑い声やため息など) を挿入したりすることで、複雑な音声パラメーターを設定することなく、より表現力豊かな音声を生成します。
サポートモデル:qwen-audio-3.0-tts-plus および qwen-audio-3.0-tts-flash のみ。
制限:単方向ストリーミングモードのみがサポートされています。
制御タグ
制御タグは、音声の感情やスタイルを設定します。テキストにタグを配置すると、次の制御タグが表示されるか、文が長さのために自動的に分割されるまで、後続のすべてのテキストに影響します。
タグ | 説明 |
| 悲しい |
| 驚いた |
| 深く大きな叫び声 |
| 震える |
| 怒っている |
| 興奮した |
| 皮肉な |
| 好奇心旺盛な |
| ドラキュラスタイル (深く、不気味) |
| 退屈した |
| 疲れた |
| 歌う |
| 軽蔑的な |
| 叫ぶ |
| ASMR のようなささやき声 |
| パニックになった |
| いたずらっぽい |
| 共感的な |
| ささやき |
| しぶしぶ |
| 泣いている |
| 真面目な |
| 非常に遅い話し方 |
| 非常に速い話し方 |
リッチ言語タグ
リッチ言語タグは、テキストの現在の位置に声の効果を挿入し、周囲のテキストの感情スタイルには影響しません。
タグ | 説明 |
| 息をのむ |
| ため息 |
| 咳払い |
| くすくす笑う |
| 笑い声 |
| 咳 |
| Snort |
使用例
以下の例は、text パラメーターで制御タグとリッチ言語タグを組み合わせる方法を示しています:
[excited]今日はなんて美しい日なんだ![laughing]一緒に外に出て楽しもう!
このテキストでは、[excited] は後続のすべてのテキストに興奮した感情を適用する制御タグです。[laughing] は、その位置に笑い声を挿入してから残りのテキストの合成を続けるリッチ言語タグです。
同じテキスト内で異なる感情を切り替えることもできます:
[serious]安全上の注意に注意してください。[excited]さあ、今すぐ始めましょう!
ここでは、[serious] が最初の文を真面目なトーンに設定し、[excited] が2番目の文から興奮したトーンに切り替えます。
WebSocket 生プロトコル呼び出し
以下の例は、DashScope SDK を使用しないシナリオに適した、WebSocket 生プロトコルを介してサーバーに直接接続する方法を示しています。これらは最小限の実行可能な実装です。WebSocket プロトコルの詳細については、各モデルの API リファレンスをご参照ください。
本番環境での適用
接続の再利用 (WebSocket)
WebSocket 接続は再利用可能です:合成タスクが完了した後、再確立することなく同じ接続で次のタスクを開始できます。
再利用プロセス:
Qwen-Audio-TTS / Qwen-Audio-TTS/CosyVoice:クライアントが
finish-taskを送信し、サーバーがtask-finishedを返した後、クライアントはrun-taskを送信して新しいタスクを開始できます。Qwen-TTS:クライアントが
session.finishを送信し、サーバーがsession.finishedを返した後、クライアントは新しいセッションを作成して次のタスクを開始できます。
新しいタスクを開始する前に、サーバーが完了イベント (
task-finishedまたはsession.finished) を返すのを待ちます。Qwen-Audio-TTS, Qwen-Audio-TTS/CosyVoice は、再利用された接続上の各タスクに異なる
task_idを必要とします。タスクが失敗した場合、サーバーはエラーイベントを返し、接続を閉じます。接続は再利用できません。
前のタスクが終了してから 60 秒以内に新しいタスクが開始されない場合、接続は自動的に閉じられます。
各モデルのイベントの詳細については、対応するAPI リファレンスをご参照ください。
高い同時実行性のベストプラクティス
DashScope SDK には、WebSocket 接続とシンセサイザーオブジェクトを再利用する組み込みのプーリングがあり、それらを繰り返し作成および破棄するオーバーヘッドを排除します。
サポートされているモデルとリージョン
シンガポール
以下のモデルを呼び出すには、シンガポールリージョンから API キーを選択してください:
Qwen-Audio-TTS: qwen-audio-3.0-tts-plus, qwen-audio-3.0-tts-flash
Qwen-Audio-TTS/CosyVoice: cosyvoice-v3-plus, cosyvoice-v3-flash
Qwen-TTS:
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 キーを選択してください:
Qwen-Audio-TTS: qwen-audio-3.0-tts-plus, qwen-audio-3.0-tts-flash
Qwen-Audio-TTS/CosyVoice: cosyvoice-v3.5-plus, cosyvoice-v3.5-flash, cosyvoice-v3-plus, cosyvoice-v3-flash, cosyvoice-v2
Qwen-TTS:
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 (スナップショット)
サポートされている音声
モデルごとにサポートされている音声が異なります。voice リクエストパラメーターを、対応する音声リストの音声パラメーター列の値に設定してください。
API リファレンス
よくある質問
Q:音声合成で誤った発音を修正するにはどうすればよいですか?多音字の発音を制御するにはどうすればよいですか?
多音字を同音異義語に置き換えて、発音の問題を迅速に修正します。
SSML マークアップを使用して発音を制御します。
Q:クローン音声を使用すると音声がサイレントになる問題を解決するにはどうすればよいですか?
音声ステータスの確認
音声クローニング/デザイン API インターフェイスを呼び出し、音声の
statusがOKであることを確認します。モデルバージョンの一貫性の確認
音声クローニング時に使用した
target_modelパラメーターが、音声合成に使用したmodelパラメーターと一致していることを確認します。例:クローニングに
cosyvoice-v3-plusを使用した場合合成にも
cosyvoice-v3-plusを使用する必要があります
ソースオーディオの品質の確認
音声クローニングに使用したソースオーディオが、「音声クローニング/デザイン API」の要件を満たしているかどうかを確認します:
オーディオの持続時間:10〜20 秒
クリアな音質
バックグラウンドノイズなし
リクエストパラメーターの確認
音声合成リクエストの
voiceパラメーターが、クローンされた音声 ID に設定されていることを確認します。
Q:クローン音声から合成されたオーディオが不安定または不完全な場合はどうすればよいですか?
クローン音声から合成されたオーディオに以下のいずれかの問題がある場合:
オーディオの再生が不完全で、テキストの一部しか話されない
合成品質が一定しない
オーディオに異常な間や無音部分が含まれる
考えられる原因:ソースオーディオが品質要件を満たしていません。
解決策:ソースオーディオが「音声クローニングのための録音ガイド」の要件を満たしているかどうかを確認します。録音ガイドラインに従ってオーディオを再録音してください。
Q:合成されたオーディオの実際の持続時間が、WAV ファイルに表示される持続時間と異なるのはなぜですか?
音声合成は、生成されたデータを返すストリーミングメカニズムを使用します。保存された WAV ファイルヘッダーの持続時間は推定値であり、不正確な場合があります。正確な持続時間を得るには、フォーマットを pcm に設定し、完全な合成結果を待ってから、WAV ファイルヘッダーを手動で追加します。
Q:オーディオファイルが再生できないのはなぜですか?
シナリオに基づいてトラブルシューティングします:
完全なファイルとして保存されたオーディオ (xx.mp3 など)
オーディオフォーマットの一貫性:リクエストパラメーターのオーディオフォーマットは、ファイル拡張子と一致する必要があります (例:パラメーターが wav の場合、ファイルは .wav である必要があります)。
プレーヤーの互換性:プレーヤーがオーディオフォーマットとサンプルレートをサポートしていることを確認します。
ストリーミングオーディオ再生
オーディオストリームを完全なファイルとして保存し、メディアプレーヤーで再生してみてください。ファイルが再生されない場合は、上記のシナリオ 1 をご参照ください。
ファイルが正しく再生される場合は、ストリーミング再生の実装に問題があります。プレーヤーがストリーミング再生をサポートしていることを確認します (ffmpeg、pyaudio、AudioFormat、MediaSource など)。
Q:オーディオの再生が途切れるのはなぜですか?
以下の手順でトラブルシューティングします:
テキスト送信レートの確認:前のオーディオセグメントが次のテキストが到着する前に終了しないように、送信間隔が合理的であることを確認します。
コールバック関数のパフォーマンスの確認:
コールバック関数にブロッキングロジックが存在しないことを確認します。
コールバックは WebSocket スレッドで実行されます。ブロッキング操作はデータ受信に影響します。オーディオデータを別のバッファに書き込み、別のスレッドで処理します。
ネットワークの安定性の確認:ネットワークの変動により、オーディオ伝送の中断や遅延が発生する可能性があります。
Q:音声合成に時間がかかるのはなぜですか?
以下の手順でトラブルシューティングします:
入力間隔の確認
ストリーミング合成の場合、テキストの送信間隔が長すぎないか確認します。間隔が長いと、合計合成時間が増加します。
パフォーマンスメトリクスの分析
初回パケットレイテンシー:通常は約 500 ms です。
RTF (リアルタイム係数 = 合計合成時間 / オーディオ持続時間):1.0 未満である必要があります。
Q:API キーを音声合成サービスのみに制限するにはどうすればよいですか (権限隔離)?
新しいワークスペースを作成し、特定のモデルのみへのアクセスを許可します。これにより、API キーの範囲が制限されます。詳細については、「ワークスペースの管理」をご参照ください。