DashScope Java SDK を用いて、 Qwen-Audio-TTS で音声合成を行います。
サービスエンドポイント
デフォルトでは、SDK はChina (Beijing) リージョンのエンドポイントに接続します。別のリージョンを使用するには、SDK を初期化する前にConstants.baseWebsocketApiUrl を設定します。
Singapore
wss://{WorkspaceId}.ap-southeast-1.maas.aliyuncs.com/api-ws/v1/inference
{WorkspaceId} を実際のワークスペース ID に置き換えてください。
China (Beijing)
wss://{WorkspaceId}.cn-beijing.maas.aliyuncs.com/api-ws/v1/inference
{WorkspaceId} を実際のワークスペース ID に置き換えてください。
Singapore リージョンへの切り替え:
import com.alibaba.dashscope.utils.Constants;
// SDK を初期化する前に設定します
Constants.baseWebsocketApiUrl = "wss://{WorkspaceId}.ap-southeast-1.maas.aliyuncs.com/api-ws/v1/inference";
重要Alibaba Cloud Model Studio では、China (Beijing) および Singapore リージョン向けに、ワークスペース固有のドメインをリリースしました。新しい専用ドメインでは、推論リクエストのパフォーマンスが向上し、安定性も高くなります。新しいドメインへの移行を推奨します:
- China (Beijing):
dashscope.aliyuncs.comから{WorkspaceId}.cn-beijing.maas.aliyuncs.comへ - Singapore:
dashscope-intl.aliyuncs.comから{WorkspaceId}.ap-southeast-1.maas.aliyuncs.comへ
SpeechSynthesizer
パッケージ: com.alibaba.dashscope.audio.ttsv2.SpeechSynthesizer
コンストラクター
public SpeechSynthesizer(SpeechSynthesisParam param, ResultCallback<SpeechSynthesisResult> callback)
パラメーター:
param:SpeechSynthesisParam.builder() で構築する音声合成パラメーター。callback:ストリーミング呼び出しのコールバック。非ストリーミング呼び出しの場合は null を渡します。
call() - 非ストリーミング/単方向ストリーミングの合成
メソッドシグネチャ:
public ByteBuffer call(String text)
パラメーター:
| パラメーター | 型 | 必須 | 説明 |
|---|---|---|---|
text | 文字列 | 必須 | 合成するテキスト。最大長:20,000 文字。 |
戻り値: ByteBuffer または null。非ストリーミング呼び出しの場合は、完全な音声データを返します。単方向ストリーミング呼び出しの場合、このメソッドは null を返し、音声はコールバック経由で配信されます。
streamingCall() - 双方向ストリーミングの合成
メソッドシグネチャ:
public void streamingCall(String text)
パラメーター:
| パラメーター | 型 | 必須 | 説明 |
|---|---|---|---|
text | 文字列 | 必須 | 合成するテキスト。最大長:20,000 文字。このメソッドは複数回呼び出して、テキストを追加できます。 |
streamingComplete() - 双方向ストリーミングの終了
メソッドシグネチャ:
public void streamingComplete()
双方向ストリーミング呼び出しを終了し、すべてのテキストを送信したことをサーバーに通知します。
streamingCancel() - 双方向ストリーミングのキャンセル
メソッドシグネチャ:
public void streamingCancel()
説明: 現在の双方向ストリーミング音声合成タスクをキャンセルします。このメソッドを呼び出すと、SDK は現在のタスクを直ちに終了します。SpeechSynthesizer インスタンスを再初期化せずに、同じ接続上で新しい合成タスクを開始できます。
重要バージョン要件: この機能を使用するには、Java SDK 2.22.26 以降が必要です。
callAsFlowable() - 単方向ストリーミングの合成 (リアクティブ)
メソッドシグネチャ:
public Flowable<SpeechSynthesisResult> callAsFlowable(String text)
パラメーター:
| パラメーター | 型 | 必須 | 説明 |
|---|---|---|---|
text | 文字列 | 必須 | 合成するテキスト。最大長:20,000 文字。 |
戻り値: Flowable<SpeechSynthesisResult> のリアクティブストリーム。
streamingCallAsFlowable() - 双方向ストリーミングの合成 (リアクティブ)
メソッドシグネチャ:
public Flowable<SpeechSynthesisResult> streamingCallAsFlowable(Flowable<String> textStream)
パラメーター:
| パラメーター | 型 | 必須 | 説明 |
|---|---|---|---|
textStream | Flowable<String> | 必須 | テキストのリアクティブストリーム。 |
戻り値: Flowable<SpeechSynthesisResult> のリアクティブストリーム。
getDuplexApi().close() - WebSocket 接続のクローズ
メソッドシグネチャ:
public boolean getDuplexApi().close(int code, String reason)
パラメーター:
| パラメーター | 型 | 必須 | 説明 |
|---|---|---|---|
code | int | 必須 | クローズコード。 |
reason | 文字列 | 必須 | クローズ理由。 |
戻り値: ブール値。接続を正常にクローズできた場合は true、それ以外の場合は false を返します。
getLastRequestId() - リクエスト ID の取得
メソッドシグネチャ:
public String getLastRequestId()
戻り値: 文字列。リクエスト ID を返します。
getFirstPackageDelay() - 初回パケットレイテンシーの取得
メソッドシグネチャ:
public long getFirstPackageDelay()
戻り値: long。最初のテキストセグメントを送信してから最初の音声パケットを受信するまでの時間をミリ秒単位で測定した、初回パケットレイテンシー。
SpeechSynthesisParam
パッケージ: com.alibaba.dashscope.audio.ttsv2.SpeechSynthesisParam
例:
SpeechSynthesisParam param = SpeechSynthesisParam.builder()
.model("qwen-audio-3.0-tts-flash") // モデル
.voice("longanhuan_v3.6") // 音声
.format(SpeechSynthesisAudioFormat.WAV_8000HZ_MONO_16BIT) // 音声エンコーディング形式とサンプリングレート
.volume(50) // 音量。有効な値: [0, 100]
.speechRate(1.0f) // スピーチレート。有効な値: [0.5, 2.0]
.pitchRate(1.0f) // ピッチ。有効な値: [0.5, 2.0]
.build();
Builderメソッド
| メソッド | パラメーターの型 | 必須 | 説明 |
|---|---|---|---|
model(String) | String | はい | モデル名です。 |
voice(String) | String | はい | 音声合成に使用する音声です。
|
format(SpeechSynthesisAudioFormat) | enum | いいえ | 音声エンコーディング形式とサンプリングレートです。 デフォルト値: SpeechSynthesisAudioFormat.MP3_22050HZ_MONO_256KBPS です。 SpeechSynthesisAudioFormat パッケージ: |
volume(int) | int | いいえ | 音量です。 デフォルト値: 50 です。 有効な値: [0, 100] です。 |
speechRate(float) | float | いいえ | スピーチレートです。 デフォルト値: 1.0 です。 有効な値: [0.5, 2.0] です。 |
pitchRate(float) | float | いいえ | ピッチです。 デフォルト値: 1.0 です。 有効な値: [0.5, 2.0] です。 |
enableWordTimestamp(boolean) | boolean | いいえ | 単語レベルのタイムスタンプを有効にするかどうかを指定します。 デフォルト値: false です。 ストリーミング出力モードでのみ使用できます。サポートされている音声は、クローン音声に対応しています。対応するシステム音声については、Qwen-Audio-TTS voice listをご参照ください。 |
seed(int) | int | いいえ | 合成出力のばらつきを制御するためのランダムシードです。モデルのバージョン、テキスト、音声、およびその他のパラメーターが変更されない場合、同じシードを使用すると同一の結果を生成します。 デフォルト値: 0 です。 有効な値: [0, 65535] です。 SDK バージョン 2.21.7 より前の場合、追加パラメーターを使用して |
languageHints(List<String>) | List<String> | いいえ | 重要
音声合成のターゲット言語を指定し、出力品質を向上させます。 数字の発音、略語の展開、記号の読み上げ、または少数言語の合成が期待どおりでない場合に、このパラメーターを使用します。例:
有効な値
|
instruction(String) | String | いいえ | 方言、感情、話し方などの合成特性を制御します。 使用方法の詳細については、「Instruction control」をご参照ください。 |
hotFix(ParamHotFix) | ParamHotFix | いいえ | 合成前に適用する発音の修正とテキスト置換を設定します。 パラメーター:
例: |
parameter(String key, Object value) | String, Object | いいえ | 追加パラメーターを設定します。 |
parameters(Map<String, Object>) | Map | いいえ | 追加パラメーターを設定します。 |
追加パラメーター
parameter() または parameters() を使用して設定します。
例:
SpeechSynthesisParam param = SpeechSynthesisParam.builder()
.model("qwen-audio-3.0-tts-flash")
.voice("longanhuan_v3.6")
.parameter("bit_rate", 32)
.build();
| パラメーター | 型 | 必須 | 説明 |
|---|---|---|---|
bit_rate | integer | いいえ | kbps 単位の音声ビットレートです。音声形式が mp3 または opus の場合、bit_rate を使用してビットレートを調整します。デフォルト値: 32 です。 有効な値: [6, 510] です。 |
enable_aigc_tag | boolean | いいえ | 生成された音声に AIGC ウォーターマークを埋め込むかどうかを指定します。true に設定すると、サポートされている形式 (wav/mp3/opus) の音声ファイルにウォーターマークが埋め込まれます。 デフォルト値: false です。 |
aigc_propagator | String | いいえ | AIGC ウォーターマークの ContentPropagator フィールドを設定し、コンテンツの伝播元を識別します。enable_aigc_tag が true の場合にのみ有効になります。デフォルト値: Alibaba Cloud の UID です。 |
aigc_propagate_id | String | いいえ | AIGC ウォーターマークの PropagateID フィールドを設定し、特定の伝播アクションを一意に識別します。enable_aigc_tag が true の場合にのみ有効になります。デフォルト値: 現在の音声合成リクエストのリクエスト ID です。 |
ResultCallback
パッケージ: com.alibaba.dashscope.common.ResultCallback
onEvent() - 音声データの受信
メソッドシグネチャ:
public void onEvent(SpeechSynthesisResult result)
パラメーター:
| パラメーター | 型 | 必須 | 説明 |
|---|---|---|---|
result | SpeechSynthesisResult | はい | 合成イベントを受信したときにトリガーされ、オーディオフレーム、タイムスタンプ情報、および出力情報 (イベントタイプ、元のテキストなど) が含まれます。 |
onComplete() - 合成完了
メソッドシグネチャ:
public void onComplete()
音声合成が完了したときにトリガーされます。
onError() - エラー処理
メソッドシグネチャ:
public void onError(Exception e)
パラメーター:
| パラメーター | 型 | 必須 | 説明 |
|---|---|---|---|
e | Exception | はい | エラーが発生したときにトリガーされ、例外情報が含まれます。 |
SpeechSynthesisResult
パッケージ: com.alibaba.dashscope.audio.tts.SpeechSynthesisResult
getAudioFrame() - オーディオデータフレームの取得
メソッドシグネチャ:
public ByteBuffer getAudioFrame()
戻り値: ByteBuffer。オーディオデータフレーム。
getTimestamp() - タイムスタンプ情報の取得
メソッドシグネチャ:
public Sentence getTimestamp()
戻り値: Sentence。タイムスタンプ情報。
getOutput() - 出力情報の取得
メソッドシグネチャ:
public JsonObject getOutput()
戻り値: com.google.gson.JsonObject。イベントタイプ、テキストコンテンツ、タイムスタンプ情報を含む、合成イベントの 出力情報 です。 SDK バージョン >= 2.22.0 が必要です。
文レベルのタイムスタンプ情報 (Sentence)
Sentence は、文レベルのタイムスタンプ情報をカプセル化します。
getBeginTime() - 文の開始時刻の取得
メソッドシグネチャ:
public int getBeginTime()
戻り値: ミリ秒単位の文の開始時刻。
getEndTime() - 文の終了時刻の取得
メソッドシグネチャ:
public int getEndTime()
戻り値: ミリ秒単位の文の終了時刻。
getWords() - 単語レベルのタイムスタンプの取得
メソッドシグネチャ:
public List<Word> getWords()
戻り値:単語レベルのタイムスタンプ情報を含む Word オブジェクトの List。空の場合があります。
単語レベルのタイムスタンプ情報 (Word)
Word は、単語レベルのタイムスタンプ情報をカプセル化します。
getBeginTime() - 単語の開始時刻の取得
メソッドシグネチャ:
public int getBeginTime()
戻り値: ミリ秒単位の単語の開始時刻。
getEndTime() - 単語の終了時刻の取得
メソッドシグネチャ:
public int getEndTime()
戻り値: ミリ秒単位の単語の終了時刻。
getText() - テキストの取得
メソッドシグネチャ:
public String getText()
戻り値: String。テキストコンテンツ。
getPhonemes() - 音素レベルのタイムスタンプの取得
メソッドシグネチャ:
public List<Phoneme> getPhonemes()
戻り値:音素レベルのタイムスタンプ情報を含む Phoneme オブジェクトの List。空の場合があります。
音素レベルのタイムスタンプ情報 (Phoneme)
Phoneme は、音素レベルのタイムスタンプ情報をカプセル化します。
getBeginTime() - 音素の開始時刻の取得
メソッドシグネチャ:
public int getBeginTime()
戻り値: ミリ秒単位の音素の開始時刻。
getEndTime() - 音素の終了時刻の取得
メソッドシグネチャ:
public int getEndTime()
戻り値: ミリ秒単位の音素の終了時刻。
getText() - テキストの取得
メソッドシグネチャ:
public String getText()
戻り値: String。テキストコンテンツ。
getTone() - トーンの取得
メソッドシグネチャ:
public int getTone()
戻り値:トーンの値。
- 英語では、0、1、2 はそれぞれ無強勢、第一強勢、第二強勢を表します。
- 中国語ピンインでは、1、2、3、4、5 はそれぞれ第一声、第二声、第三声、第四声、軽声を表します。
出力情報 (output)
getOutput() は、合成イベントの出力情報をカプセル化した JsonObject を返します。onEvent コールバック、または Flowable ストリームで取得します。これには次のフィールドが含まれます。
| フィールド | タイプ | 説明 |
|---|---|---|
type | 文字列 | イベントタイプ。有効な値: sentence-begin (文の開始。合成対象のテキストを返します)、sentence-synthesis (音声の合成中。オーディオデータチャンクを返します)、sentence-end (文の終了。テキストコンテンツと単語レベルのタイムスタンプを返します)。 |
original_text | 文字列 | 現在の文の元のテキスト。sentence-begin および sentence-end イベントで返されます。 |
sentence | JsonObject | 文の情報。文のインデックス (index) と単語レベルのタイムスタンプ (words) が含まれます。 sentence-end イベントには、単語レベルのタイムスタンプ情報がすべて含まれます。 |
サンプルコード
SDK は以下の合成モードをサポートしています。
- 非ストリーミング:完全なテキストを一度に送信し、完全な音声を直接返すブロッキングコールです。短いテキストの音声合成に最適です。
- 単方向ストリーミング:完全なテキストを一度に送信し、コールバック関数を通じて音声データ (チャンク単位で配信される場合があります) を配信する非ブロッキングコールです。低レイテンシーが必要な短いテキストのシナリオに最適です。
- 双方向ストリーミング:テキストを複数のセグメントで送信し、リアルタイムでコールバック関数を通じて段階的に合成された音声を配信する非ブロッキングコールです。低レイテンシーが必要な長いテキストのシナリオに最適です。
非ストリーミング呼び出し
リクエストごとのテキストの長さは 20,000 文字を超えないようにしてください。
重要call メソッドを呼び出す前に、毎回 SpeechSynthesizer インスタンスを再初期化してください。
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 {
// モデル
private static String model = "qwen-audio-3.0-tts-flash";
// 音声
private static String voice = "longanhuan_v3.6";
public static void streamAudioDataToSpeaker() {
// リクエストパラメーター
SpeechSynthesisParam param =
SpeechSynthesisParam.builder()
// Singapore リージョンと Beijing リージョンの 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 接続の確立が必要なため、初回パケットレイテンシーには接続セットアップ時間が含まれます
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) {
// 以下の設定は Singapore リージョン用です。"{WorkspaceId}" を実際のワークスペース ID に置き換えてください。設定はリージョンによって異なります。
Constants.baseWebsocketApiUrl = "wss://{WorkspaceId}.ap-southeast-1.maas.aliyuncs.com/api-ws/v1/inference";
streamAudioDataToSpeaker();
System.exit(0);
}
}
単方向ストリーミング呼び出し
リクエストごとのテキストの長さは 20,000 文字を超えないようにしてください。
重要call メソッドを呼び出す前に、毎回 SpeechSynthesizer インスタンスを再初期化してください。
import com.alibaba.dashscope.audio.tts.SpeechSynthesisResult;
import com.alibaba.dashscope.audio.ttsv2.SpeechSynthesisParam;
import com.alibaba.dashscope.audio.ttsv2.SpeechSynthesizer;
import com.alibaba.dashscope.common.ResultCallback;
import com.alibaba.dashscope.utils.Constants;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.concurrent.CountDownLatch;
class TimeUtils {
private static final DateTimeFormatter formatter =
DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSS");
public static String getTimestamp() {
return LocalDateTime.now().format(formatter);
}
}
public class Main {
// モデル
private static String model = "qwen-audio-3.0-tts-flash";
// 音声
private static String voice = "longanhuan_v3.6";
public static void streamAudioDataToSpeaker() {
CountDownLatch latch = new CountDownLatch(1);
// ResultCallback インターフェースを実装
ResultCallback<SpeechSynthesisResult> callback = new ResultCallback<SpeechSynthesisResult>() {
@Override
public void onEvent(SpeechSynthesisResult result) {
if (result.getAudioFrame() != null) {
// 音声データをローカルに保存するロジックをここに実装
System.out.println(TimeUtils.getTimestamp() + " 音声を受信");
}
// イベントタイプと元のテキストを含む出力情報を取得
if (result.getOutput() != null && result.getOutput().has("type")) {
System.out.println("イベントタイプ: " + result.getOutput().get("type").getAsString()
+ ", 元のテキスト: " + (result.getOutput().has("original_text") ? result.getOutput().get("original_text").getAsString() : ""));
}
}
@Override
public void onComplete() {
System.out.println(TimeUtils.getTimestamp() + " 完了通知を受信、音声合成が終了しました");
latch.countDown();
}
@Override
public void onError(Exception e) {
System.out.println("例外が発生しました: " + e.toString());
latch.countDown();
}
};
// リクエストパラメーター
SpeechSynthesisParam param =
SpeechSynthesisParam.builder()
// Singapore リージョンと Beijing リージョンの 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 番目のパラメーターとして "callback" を渡すと非同期モードが有効になります
SpeechSynthesizer synthesizer = new SpeechSynthesizer(param, callback);
// 非ブロッキングコールのため即座に null を返します (実際の結果はコールバックインターフェース経由で非同期に配信されます)。バイナリオーディオは、コールバックインターフェースの onEvent メソッドを通じてリアルタイムで受信されます。
try {
synthesizer.call("今日の天気はどうですか?");
// 合成が完了するまで待機
latch.await();
} catch (Exception e) {
throw new RuntimeException(e);
} finally {
// タスク完了後に WebSocket 接続をクローズ
synthesizer.getDuplexApi().close(1000, "bye");
}
// 最初のテキスト送信には WebSocket 接続の確立が必要なため、初回パケットレイテンシーには接続セットアップ時間が含まれます
System.out.println(
"[Metric] requestId: "
+ synthesizer.getLastRequestId()
+ ", first packet latency (ms): "
+ synthesizer.getFirstPackageDelay());
}
public static void main(String[] args) {
// 以下の設定は Singapore リージョン用です。"{WorkspaceId}" を実際のワークスペース ID に置き換えてください。設定はリージョンによって異なります。
Constants.baseWebsocketApiUrl = "wss://{WorkspaceId}.ap-southeast-1.maas.aliyuncs.com/api-ws/v1/inference";
streamAudioDataToSpeaker();
System.exit(0);
}
}
双方向ストリーミング呼び出し
個別の呼び出しのテキスト長は 20,000 文字、全呼び出しにわたる累積テキスト長は 200,000 文字を超えないようにしてください。
-
ストリーミング入力中、
streamingCallを複数回呼び出してテキストセグメントを順番に送信します。サーバーは受信したテキストを自動的に文に分割します。- 完全な文は即座に合成されます。
- 不完全な文は、完全な文になるまでバッファリングされます。
streamingCompleteが呼び出されると、サーバーは受信済みで未処理のすべてのテキストセグメント (不完全な文を含む) を強制的に合成します。 -
連続するテキストセグメント間の間隔は 23 秒を超えないようにしてください。この制限を超えると、「23 秒後にリクエストタイムアウト」例外がスローされます。
保留中のテキストがない場合は、
streamingCompleteを速やかに呼び出してタスクを終了してください。重要必ず
streamingCompleteメソッドを呼び出してください。そうしないと、末尾のテキストセグメントが音声に変換されない可能性があります。サーバーは 23 秒のタイムアウトを強制しており、クライアント側では変更できません。
import com.alibaba.dashscope.audio.tts.SpeechSynthesisResult;
import com.alibaba.dashscope.audio.ttsv2.SpeechSynthesisAudioFormat;
import com.alibaba.dashscope.audio.ttsv2.SpeechSynthesisParam;
import com.alibaba.dashscope.audio.ttsv2.SpeechSynthesizer;
import com.alibaba.dashscope.common.ResultCallback;
import com.alibaba.dashscope.utils.Constants;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
class TimeUtils {
private static final DateTimeFormatter formatter =
DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSS");
public static String getTimestamp() {
return LocalDateTime.now().format(formatter);
}
}
public class Main {
private static String[] textArray = {"ストリーミング音声合成 SDK は、",
"入力テキストを ", "バイナリオーディオデータに変換できます。", "非ストリーミング音声合成と比較して、",
"ストリーミング合成はより優れたリアルタイム性能を提供します。", "ユーザーがテキストを入力している間、",
"ほぼ同期した音声出力を聞くことができ、", "インタラクティブな体験を大幅に向上させ、",
"ユーザーの待ち時間を短縮します。", "大規模言語モデル (LLM) を呼び出して、",
"ストリーミングテキスト入力で ", "音声合成を実行するのに適しています。"};
private static String model = "qwen-audio-3.0-tts-flash"; // モデル
private static String voice = "longanhuan_v3.6"; // 音声
public static void streamAudioDataToSpeaker() {
// コールバック関数を設定
ResultCallback<SpeechSynthesisResult> callback = new ResultCallback<SpeechSynthesisResult>() {
@Override
public void onEvent(SpeechSynthesisResult result) {
// System.out.println("メッセージを受信: " + result);
if (result.getAudioFrame() != null) {
// 音声データを処理するロジックをここに実装
System.out.println(TimeUtils.getTimestamp() + " 音声を受信");
}
}
@Override
public void onComplete() {
System.out.println(TimeUtils.getTimestamp() + " 完了通知を受信、音声合成が終了しました");
}
@Override
public void onError(Exception e) {
System.out.println("例外が発生しました: " + e.toString());
}
};
// リクエストパラメーター
SpeechSynthesisParam param =
SpeechSynthesisParam.builder()
// Singapore リージョンと Beijing リージョンの 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)
.format(SpeechSynthesisAudioFormat
.PCM_22050HZ_MONO_16BIT) // ストリーミング合成には PCM または MP3 を使用
.build();
SpeechSynthesizer synthesizer = new SpeechSynthesizer(param, callback);
// Callback を使用した streamingCall メソッドは現在のスレッドをブロックしません
try {
for (String text : textArray) {
// テキストフラグメントを送信し、コールバックインターフェースの onEvent メソッドを通じてバイナリオーディオをリアルタイムで受信
synthesizer.streamingCall(text);
}
// ストリーミング送信の完了を通知します
synthesizer.streamingComplete();
} catch (Exception e) {
throw new RuntimeException(e);
} finally {
// タスク完了後に WebSocket 接続をクローズ
synthesizer.getDuplexApi().close(1000, "bye");
}
// 最初のテキスト送信には WebSocket 接続の確立が必要なため、初回パケットレイテンシーには接続セットアップ時間が含まれます
System.out.println(
"[Metric] requestId: "
+ synthesizer.getLastRequestId()
+ ", first packet latency (ms): "
+ synthesizer.getFirstPackageDelay());
}
public static void main(String[] args) {
// 以下の設定は Singapore リージョン用です。"{WorkspaceId}" を実際のワークスペース ID に置き換えてください。設定はリージョンによって異なります。
Constants.baseWebsocketApiUrl = "wss://{WorkspaceId}.ap-southeast-1.maas.aliyuncs.com/api-ws/v1/inference";
streamAudioDataToSpeaker();
System.exit(0);
}
}
Flowable ベースの呼び出し
Flowable は、バックプレッシャーをサポートするリアクティブストリームを表す RxJava の型です。詳細については、「RxJava Flowable ドキュメント」をご参照ください。
Flowable を使用する前に、RxJava ライブラリが統合されていること、およびリアクティブプログラミングの基礎を理解していることを確認してください。
個別の呼び出しのテキスト長は 20,000 文字、全呼び出しにわたる累積テキスト長は 200,000 文字を超えないようにしてください。
単方向ストリーミング呼び出し
次の例では、Flowable オブジェクトの blockingForEach メソッドを使用して、ストリーミングされた各 SpeechSynthesisResult オブジェクトをブロッキング方式で取得する方法を示します。
import com.alibaba.dashscope.audio.ttsv2.SpeechSynthesisParam;
import com.alibaba.dashscope.audio.ttsv2.SpeechSynthesizer;
import com.alibaba.dashscope.exception.NoApiKeyException;
import com.alibaba.dashscope.utils.Constants;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
class TimeUtils {
private static final DateTimeFormatter formatter =
DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSS");
public static String getTimestamp() {
return LocalDateTime.now().format(formatter);
}
}
public class Main {
private static String model = "qwen-audio-3.0-tts-flash"; // モデル
private static String voice = "longanhuan_v3.6"; // 音声
public static void streamAudioDataToSpeaker() throws NoApiKeyException {
// リクエストパラメーター
SpeechSynthesisParam param =
SpeechSynthesisParam.builder()
// Singapore リージョンと Beijing リージョンの 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();
SpeechSynthesizer synthesizer = new SpeechSynthesizer(param, null);
synthesizer.callAsFlowable("今日の天気はどうですか?").blockingForEach(result -> {
if (result.getAudioFrame() != null) {
// 音声データを処理するロジックをここに実装
System.out.println(TimeUtils.getTimestamp() + " 音声を受信");
}
// イベントタイプと元のテキストを含む出力情報を取得
if (result.getOutput() != null && result.getOutput().has("type")) {
System.out.println("イベントタイプ: " + result.getOutput().get("type").getAsString()
+ ", 元のテキスト: " + (result.getOutput().has("original_text") ? result.getOutput().get("original_text").getAsString() : ""));
}
});
// タスク完了後に WebSocket 接続をクローズ
synthesizer.getDuplexApi().close(1000, "bye");
// 最初のテキスト送信には WebSocket 接続の確立が必要なため、初回パケットレイテンシーには接続セットアップ時間が含まれます
System.out.println(
"[Metric] requestId: "
+ synthesizer.getLastRequestId()
+ ", first packet latency (ms): "
+ synthesizer.getFirstPackageDelay());
}
public static void main(String[] args) throws NoApiKeyException {
// 以下の設定は Singapore リージョン用です。"{WorkspaceId}" を実際のワークスペース ID に置き換えてください。設定はリージョンによって異なります。
Constants.baseWebsocketApiUrl = "wss://{WorkspaceId}.ap-southeast-1.maas.aliyuncs.com/api-ws/v1/inference";
streamAudioDataToSpeaker();
System.exit(0);
}
}
双方向ストリーミング呼び出し
次の例では、Flowable オブジェクトをテキストストリーミングの入力パラメーターとして使用し、返された Flowable オブジェクトの blockingForEach メソッドを使用して、ストリーミングされた各 SpeechSynthesisResult オブジェクトをブロッキング方式で取得する方法を示します。
import com.alibaba.dashscope.audio.ttsv2.SpeechSynthesisParam;
import com.alibaba.dashscope.audio.ttsv2.SpeechSynthesizer;
import com.alibaba.dashscope.exception.NoApiKeyException;
import com.alibaba.dashscope.utils.Constants;
import io.reactivex.BackpressureStrategy;
import io.reactivex.Flowable;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
class TimeUtils {
private static final DateTimeFormatter formatter =
DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSS");
public static String getTimestamp() {
return LocalDateTime.now().format(formatter);
}
}
public class Main {
private static String[] textArray = {"ストリーミング音声合成 SDK は、",
"入力テキストを ", "バイナリオーディオデータに変換できます。", "非ストリーミング音声合成と比較して、",
"ストリーミング合成はより優れたリアルタイム性能を提供します。", "ユーザーがテキストを入力している間、",
"ほぼ同期した音声出力を聞くことができ、", "インタラクティブな体験を大幅に向上させ、",
"ユーザーの待ち時間を短縮します。", "大規模言語モデル (LLM) を呼び出して、",
"ストリーミングテキスト入力で ", "音声合成を実行するのに適しています。"};
private static String model = "qwen-audio-3.0-tts-flash";
private static String voice = "longanhuan_v3.6";
public static void streamAudioDataToSpeaker() throws NoApiKeyException {
// ストリーミング入力をシミュレート
Flowable<String> textSource = Flowable.create(emitter -> {
new Thread(() -> {
for (int i = 0; i < textArray.length; i++) {
emitter.onNext(textArray[i]);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
emitter.onComplete();
}).start();
}, BackpressureStrategy.BUFFER);
// リクエストパラメーター
SpeechSynthesisParam param =
SpeechSynthesisParam.builder()
// Singapore リージョンと Beijing リージョンの 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();
SpeechSynthesizer synthesizer = new SpeechSynthesizer(param, null);
synthesizer.streamingCallAsFlowable(textSource).blockingForEach(result -> {
if (result.getAudioFrame() != null) {
// 音声を再生するロジックをここに実装
System.out.println(
TimeUtils.getTimestamp() +
" バイナリオーディオサイズ: " + result.getAudioFrame().capacity());
}
// イベントタイプと元のテキストを含む出力情報を取得
if (result.getOutput() != null && result.getOutput().has("type")) {
System.out.println("イベントタイプ: " + result.getOutput().get("type").getAsString()
+ ", 元のテキスト: " + (result.getOutput().has("original_text") ? result.getOutput().get("original_text").getAsString() : ""));
}
});
synthesizer.getDuplexApi().close(1000, "bye");
// 最初のテキスト送信には WebSocket 接続の確立が必要なため、初回パケットレイテンシーには接続セットアップ時間が含まれます
System.out.println(
"[Metric] requestId: "
+ synthesizer.getLastRequestId()
+ ", first packet latency (ms): "
+ synthesizer.getFirstPackageDelay());
}
public static void main(String[] args) throws NoApiKeyException {
// 以下の設定は Singapore リージョン用です。"{WorkspaceId}" を実際のワークスペース ID に置き換えてください。設定はリージョンによって異なります。
Constants.baseWebsocketApiUrl = "wss://{WorkspaceId}.ap-southeast-1.maas.aliyuncs.com/api-ws/v1/inference";
streamAudioDataToSpeaker();
System.exit(0);
}
}
高同時実行呼び出し
DashScope Java SDK は、OkHttp3 コネクションプーリングを使用して、繰り返しの接続確立によるオーバーヘッドを削減します。詳細については、「高同時実行のベストプラクティス」をご参照ください。