Sintesis suara dengan Qwen-Audio-TTS/CosyVoice menggunakan DashScope Java SDK.
Panduan pengguna: Untuk pengenalan model dan rekomendasi pemilihan, lihat Sintesis suara.
Titik akhir layanan
Secara default, SDK terhubung ke titik akhir Wilayah Beijing. Untuk menggunakan wilayah lain, atur Constants.baseWebsocketApiUrl sebelum menginisialisasi SDK.
Singapura
wss://{WorkspaceId}.ap-southeast-1.maas.aliyuncs.com/api-ws/v1/inference
Ganti {WorkspaceId} dengan workspace ID Anda yang sebenarnya.
Tiongkok (Beijing)
wss://{WorkspaceId}.cn-beijing.maas.aliyuncs.com/api-ws/v1/inference
Ganti {WorkspaceId} dengan workspace ID Anda yang sebenarnya.
Beralih ke wilayah Singapura:
import com.alibaba.dashscope.utils.Constants;
// Atur ini sebelum inisialisasi SDK apa pun
Constants.baseWebsocketApiUrl = "wss://{WorkspaceId}.ap-southeast-1.maas.aliyuncs.com/api-ws/v1/inference";
PentingAlibaba Cloud Model Studio telah merilis domain khusus ruang kerja untuk wilayah Tiongkok (Beijing) dan Singapura. Domain khusus baru ini memberikan performa lebih unggul dan stabilitas lebih tinggi untuk permintaan inferensi. Kami merekomendasikan migrasi ke domain baru:
- Tiongkok (Beijing): dari
dashscope.aliyuncs.comke{WorkspaceId}.cn-beijing.maas.aliyuncs.com - Singapura: dari
dashscope-intl.aliyuncs.comke{WorkspaceId}.ap-southeast-1.maas.aliyuncs.com
Ganti {WorkspaceId} dengan Workspace ID Anda yang sebenarnya. Domain lama tetap berfungsi penuh.
SpeechSynthesizer
Package: com.alibaba.dashscope.audio.ttsv2.SpeechSynthesizer
Konstruktor
public SpeechSynthesizer(SpeechSynthesisParam param, ResultCallback<SpeechSynthesisResult> callback)
Parameter:
param: Parameter sintesis suara, dibuat dengan SpeechSynthesisParam.builder()callback: Callback untuk panggilan streaming. Berikan null untuk panggilan non-streaming.
call() - Sintesis non-streaming/unidirectional streaming
Signature metode:
public ByteBuffer call(String text)
Parameter:
Parameter | Type | Wajib | Deskripsi |
|---|---|---|---|
text | String | Ya | Teks yang akan disintesis. Panjang maksimum: 20.000 karakter. |
Nilai kembalian: ByteBuffer atau null. Untuk panggilan non-streaming, mengembalikan seluruh data audio. Untuk panggilan unidirectional streaming, metode ini mengembalikan null; audio dikirimkan melalui callback.
streamingCall() - Sintesis bidirectional streaming
Signature metode:
public void streamingCall(String text)
Parameter:
Parameter | Tipe | Wajib | Deskripsi |
|---|---|---|---|
text | String | Ya | Teks yang akan disintesis. Panjang maksimum: 20.000 karakter. Anda dapat memanggil metode ini beberapa kali untuk menambahkan teks. |
streamingComplete() - Mengakhiri bidirectional streaming
Signature metode:
public void streamingComplete()
Mengakhiri panggilan bidirectional streaming dan memberi tahu server bahwa seluruh teks telah dikirim.
streamingCancel() - Membatalkan bidirectional streaming
Signature metode:
public void streamingCancel()
Deskripsi: Membatalkan tugas sintesis suara bidirectional streaming saat ini. Setelah memanggil metode ini, SDK segera mengakhiri tugas tersebut. Anda dapat memulai tugas sintesis baru pada koneksi yang sama tanpa perlu menginisialisasi ulang instans SpeechSynthesizer.
PentingPersyaratan versi: Fitur ini memerlukan Java SDK versi 2.22.26 atau lebih baru.
PentingBatasan model:
- Tiongkok (Beijing): Semua model Qwen-Audio-TTS mendukung fitur ini. Model CosyVoice memerlukan versi v2 atau lebih baru.
- Singapura: Semua model Qwen-Audio-TTS mendukung fitur ini. Model CosyVoice tidak mendukung fitur ini.
callAsFlowable() - Sintesis unidirectional streaming (reaktif)
Signature metode:
public Flowable<SpeechSynthesisResult> callAsFlowable(String text)
Parameter:
Parameter | Type | Wajib | Deskripsi |
|---|---|---|---|
text | String | Ya | Teks yang akan disintesis. |
Nilai kembalian: Stream reaktif Flowable<SpeechSynthesisResult>.
streamingCallAsFlowable() - Sintesis bidirectional streaming (reaktif)
Signature metode:
public Flowable<SpeechSynthesisResult> streamingCallAsFlowable(Flowable<String> textStream)
Parameter:
Parameter | Tipe | Wajib | Deskripsi |
|---|---|---|---|
textStream | Flowable<String> | Ya | Stream reaktif teks. |
Nilai kembalian: Stream reaktif Flowable<SpeechSynthesisResult>.
getDuplexApi().close() - Menutup koneksi WebSocket
Signature metode:
public boolean getDuplexApi().close(int code, String reason)
Parameter:
Parameter | Tipe | Wajib | Deskripsi |
|---|---|---|---|
code | int | Ya | Kode penutupan. |
reason | String | Ya | Alasan penutupan. |
Nilai kembalian: boolean. Mengembalikan true jika koneksi berhasil ditutup, false jika tidak.
getLastRequestId() - Mendapatkan ID permintaan
Signature metode:
public String getLastRequestId()
Nilai kembalian: String, ID permintaan.
getFirstPackageDelay() - Mendapatkan latensi paket pertama
Signature metode:
public long getFirstPackageDelay()
Nilai kembalian: long. Latensi paket pertama dalam milidetik, diukur dari pengiriman segmen teks pertama hingga menerima paket audio pertama.
SpeechSynthesisParam
Package: com.alibaba.dashscope.audio.ttsv2.SpeechSynthesisParam
Contoh:
SpeechSynthesisParam param = SpeechSynthesisParam.builder()
.model("qwen-audio-3.0-tts-flash") // Model
.voice("longanhuan_v3.6") // Voice
.format(SpeechSynthesisAudioFormat.WAV_8000HZ_MONO_16BIT) // Format encoding audio dan laju sampel
.volume(50) // Volume. Rentang nilai: [0, 100]
.speechRate(1.0f) // Laju bicara. Rentang nilai: [0.5, 2]
.pitchRate(1.0f) // Pitch. Rentang nilai: [0.5, 2]
.build();
Metode builder
| Metode | Tipe parameter | Wajib | Deskripsi |
|---|---|---|---|
| String | Ya | Nama model. |
| String | Ya | voice Voice yang digunakan untuk sintesis suara.
|
| enum | Tidak | Format encoding audio dan laju sampel. Default: SpeechSynthesisAudioFormat.MP3_22050HZ_MONO_256KBPS. Package SpeechSynthesisAudioFormat: |
| int | Tidak | Tingkat volume. Nilai default: 50. Nilai valid: [0, 100]. |
| float | Tidak | Laju bicara. Nilai default: 1.0. Nilai valid: [0.5, 2.0]. |
| float | Tidak | Pitch. Nilai default: 1.0. Nilai valid: [0.5, 2.0]. |
| boolean | Tidak | Menentukan apakah akan mengaktifkan timestamp tingkat kata. Nilai default: false. Hanya tersedia dalam mode keluaran streaming. Voice yang didukung: voice hasil kloning dari cosyvoice-v3.5-plus, cosyvoice-v3.5-flash, cosyvoice-v3-flash, cosyvoice-v3-plus, dan cosyvoice-v2, serta voice sistem yang ditandai sebagai didukung dalam Daftar voice Qwen-Audio-TTS, Daftar Voice CosyVoice. Voice hasil kloning dari model lain tidak mendukung fitur ini. |
| int | Tidak | Seed acak untuk mengontrol variasi dalam output sintesis. Ketika versi model, teks, voice, dan parameter lainnya tidak berubah, penggunaan seed yang sama menghasilkan output identik. Nilai default: 0. Nilai valid: [0, 65535]. Untuk versi SDK sebelum 2.21.7, atur |
| List<String> | Tidak | Penting
Menentukan bahasa target untuk sintesis suara guna meningkatkan kualitas output. Ketika pelafalan angka, ekspansi singkatan, pembacaan simbol, atau sintesis bahasa minoritas tidak sesuai harapan, gunakan parameter ini. Contohnya:
Nilai valid:
|
| String | Tidak | Mengontrol karakteristik sintesis seperti dialek, emosi, atau gaya bicara. Untuk detail penggunaan, lihat Kontrol instruksi. |
| ParamHotFix | Tidak | Mengonfigurasi koreksi pelafalan dan penggantian teks yang diterapkan sebelum sintesis. Fitur ini tidak didukung oleh qwen-audio-3.0-tts-plus, qwen-audio-3.0-tts-flash, atau cosyvoice-v2. Parameter:
Contoh: |
| String, Object | Tidak | Mengatur Parameter tambahan. |
| Map | Tidak | Mengatur Parameter tambahan. |
Parameter tambahan
Diatur melalui parameter() atau parameters().
Contoh:
SpeechSynthesisParam param = SpeechSynthesisParam.builder()
.model("qwen-audio-3.0-tts-flash")
.voice("longanhuan_v3.6")
.parameter("enable_markdown_filter", true)
.build();
Parameter | Tipe | Wajib | Deskripsi |
|---|---|---|---|
| integer | Tidak | Laju bit audio dalam kbps. Ketika format audio adalah mp3 atau opus, gunakan Nilai default: 32. Nilai valid: [6, 510]. |
| boolean | Tidak | Menentukan apakah akan menyematkan watermark AIGC dalam audio yang dihasilkan. Ketika diatur ke true, watermark disematkan dalam file audio dengan format yang didukung (wav/mp3/opus). Nilai default: false. Hanya qwen-audio-3.0-tts-plus, qwen-audio-3.0-tts-flash, cosyvoice-v3-flash, cosyvoice-v3-plus, dan cosyvoice-v2 yang mendukung fitur ini. |
| String | Tidak | Mengatur bidang Nilai default: UID Alibaba Cloud. Hanya qwen-audio-3.0-tts-plus, qwen-audio-3.0-tts-flash, cosyvoice-v3-flash, cosyvoice-v3-plus, dan cosyvoice-v2 yang mendukung fitur ini. |
| String | Tidak | Mengatur bidang Nilai default: ID permintaan dari permintaan sintesis suara saat ini. Hanya qwen-audio-3.0-tts-plus, qwen-audio-3.0-tts-flash, cosyvoice-v3-flash, cosyvoice-v3-plus, dan cosyvoice-v2 yang mendukung fitur ini. |
| boolean | Tidak | Hanya voice hasil kloning dari cosyvoice-v3-flash yang mendukung fitur ini. Menentukan apakah akan mengaktifkan penyaringan Markdown. Ketika diaktifkan, sistem secara otomatis menghapus simbol markup Markdown dari teks input sebelum sintesis, sehingga simbol tersebut tidak dibacakan. Nilai default: false. Nilai valid:
|
ResultCallback
Package: com.alibaba.dashscope.common.ResultCallback
onEvent() - Menerima audio data
Signature metode:
public void onEvent(SpeechSynthesisResult result)
Parameter:
Parameter | Tipe | Wajib | Deskripsi |
|---|---|---|---|
result | Ya | Dipicu ketika event sintesis diterima. Berisi frame audio, informasi timestamp, dan informasi output (tipe event, teks asli, dll.). |
onComplete() - Sintesis selesai
Signature metode:
public void onComplete()
Dipicu ketika sintesis suara selesai.
onError() - Penanganan error
Signature metode:
public void onError(Exception e)
Parameter:
Parameter | Tipe | Wajib | Deskripsi |
|---|---|---|---|
e | Exception | Ya | Dipicu ketika terjadi error. Berisi informasi exception. |
SpeechSynthesisResult
Package: com.alibaba.dashscope.audio.tts.SpeechSynthesisResult
getAudioFrame() - Mendapatkan frame data audio
Signature metode:
public ByteBuffer getAudioFrame()
Nilai kembalian: ByteBuffer, frame data audio.
getTimestamp() - Mendapatkan informasi timestamp
Signature metode:
public Sentence getTimestamp()
Nilai kembalian: Sentence, informasi timestamp.
getOutput() - Mendapatkan informasi output
Signature metode:
public JsonObject getOutput()
Nilai kembalian: com.google.gson.JsonObject, informasi output dari event sintesis, berisi tipe event dan konten teks. Memerlukan versi SDK >= 2.22.0.
Informasi timestamp tingkat kalimat (Sentence)
Sentence membungkus informasi timestamp tingkat kalimat.
getBeginTime() - Mendapatkan waktu mulai kalimat
Signature metode:
public int getBeginTime()
Nilai kembalian: Waktu mulai kalimat dalam milidetik.
getEndTime() - Mendapatkan waktu akhir kalimat
Signature metode:
public int getEndTime()
Nilai kembalian: Waktu akhir kalimat dalam milidetik.
getWords() - Mendapatkan timestamp tingkat kata
Signature metode:
public List<Word> getWords()
Nilai kembalian: List objek Word yang berisi informasi timestamp tingkat kata. Bisa jadi kosong.
Informasi timestamp tingkat kata (Word)
Word membungkus informasi timestamp tingkat kata.
getBeginTime() - Mendapatkan waktu mulai kata
Signature metode:
public int getBeginTime()
Nilai kembalian: Waktu mulai kata dalam milidetik.
getEndTime() - Mendapatkan waktu akhir kata
Signature metode:
public int getEndTime()
Nilai kembalian: Waktu akhir kata dalam milidetik.
getText() - Mendapatkan teks
Signature metode:
public String getText()
Nilai kembalian: String, konten teks.
getPhonemes() - Mendapatkan timestamp tingkat fonem
Signature metode:
public List<Phoneme> getPhonemes()
Nilai kembalian: List objek Phoneme yang berisi informasi timestamp tingkat fonem. Bisa jadi kosong.
Informasi timestamp tingkat fonem (Phoneme)
Phoneme membungkus informasi timestamp tingkat fonem.
getBeginTime() - Mendapatkan waktu mulai fonem
Signature metode:
public int getBeginTime()
Nilai kembalian: Waktu mulai fonem dalam milidetik.
getEndTime() - Mendapatkan waktu akhir fonem
Signature metode:
public int getEndTime()
Nilai kembalian: Waktu akhir fonem dalam milidetik.
getText() - Mendapatkan teks
Signature metode:
public String getText()
Nilai kembalian: String, konten teks.
getTone() - Mendapatkan nada
Signature metode:
public int getTone()
Nilai kembalian: Nilai nada.
- Dalam bahasa Inggris, 0, 1, dan 2 masing-masing merepresentasikan unstressed, primary stress, dan secondary stress.
- Dalam pinyin Mandarin, 1, 2, 3, 4, dan 5 masing-masing merepresentasikan nada pertama, kedua, ketiga, keempat, dan netral.
Informasi output (output)
getOutput() mengembalikan JsonObject yang membungkus informasi output event sintesis. Ambil informasi ini dalam callback onEvent atau stream Flowable. Informasi ini berisi bidang-bidang berikut:
Bidang | Tipe | Deskripsi |
|---|---|---|
type | String | Tipe event. Nilai yang mungkin: |
original_text | String | Teks asli kalimat saat ini. Dikembalikan dalam event |
sentence | JsonObject | Informasi kalimat, berisi indeks kalimat ( |
Kode contoh
SDK mendukung mode sintesis berikut:
- Non-streaming: Panggilan blocking yang mengirimkan seluruh teks sekaligus dan langsung mengembalikan audio lengkap. Paling cocok untuk sintesis suara teks pendek.
- Unidirectional streaming: Panggilan non-blocking yang mengirimkan seluruh teks sekaligus dan mengirimkan data audio (mungkin dalam beberapa bagian) melalui fungsi callback. Paling cocok untuk skenario teks pendek yang memerlukan latensi rendah.
- Bidirectional streaming: Panggilan non-blocking yang mengirimkan teks dalam beberapa segmen dan mengirimkan audio yang disintesis secara inkremental melalui fungsi callback secara real time. Paling cocok untuk skenario teks panjang yang memerlukan latensi rendah.
Panggilan non-streaming
Panjang teks per permintaan tidak boleh melebihi 20.000 karakter.
PentingInisialisasi ulang instans SpeechSynthesizer sebelum setiap pemanggilan metode call.
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 {
// Model
private static String model = "qwen-audio-3.0-tts-flash";
// Voice
private static String voice = "longanhuan_v3.6";
public static void streamAudioDataToSpeaker() {
// Parameter permintaan
SpeechSynthesisParam param =
SpeechSynthesisParam.builder()
// Kunci API untuk wilayah Singapura dan Beijing berbeda. Dapatkan Kunci API: https://www.alibabacloud.com/help/en/model-studio/get-api-key
// Jika variabel lingkungan belum dikonfigurasi, ganti baris berikut dengan Kunci API Studio Model Anda: .apiKey("sk-xxx")
.apiKey(System.getenv("DASHSCOPE_API_KEY"))
.model(model) // Model
.voice(voice) // Voice
.build();
// Mode sinkron: nonaktifkan callback (parameter kedua adalah null)
SpeechSynthesizer synthesizer = new SpeechSynthesizer(param, null);
ByteBuffer audio = null;
try {
// Blokir hingga audio dikembalikan
audio = synthesizer.call("What's the weather like today?");
} catch (Exception e) {
throw new RuntimeException(e);
} finally {
// Tutup koneksi WebSocket setelah tugas selesai
synthesizer.getDuplexApi().close(1000, "bye");
}
if (audio != null) {
// Simpan data audio ke file lokal "output.mp3"
File file = new File("output.mp3");
// Transmisi teks pertama memerlukan pembentukan koneksi WebSocket, sehingga latensi paket pertama mencakup waktu pembentukan koneksi
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) {
// Konfigurasi berikut untuk wilayah Singapura. Ganti "{WorkspaceId}" dengan workspace ID Anda yang sebenarnya. Konfigurasi berbeda untuk setiap wilayah.
Constants.baseWebsocketApiUrl = "wss://{WorkspaceId}.ap-southeast-1.maas.aliyuncs.com/api-ws/v1/inference";
streamAudioDataToSpeaker();
System.exit(0);
}
}
Panggilan unidirectional streaming
Panjang teks per permintaan tidak boleh melebihi 20.000 karakter.
PentingInisialisasi ulang instans SpeechSynthesizer sebelum setiap pemanggilan metode call.
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 {
// Model
private static String model = "qwen-audio-3.0-tts-flash";
// Voice
private static String voice = "longanhuan_v3.6";
public static void streamAudioDataToSpeaker() {
CountDownLatch latch = new CountDownLatch(1);
// Implementasikan antarmuka ResultCallback
ResultCallback<SpeechSynthesisResult> callback = new ResultCallback<SpeechSynthesisResult>() {
@Override
public void onEvent(SpeechSynthesisResult result) {
if (result.getAudioFrame() != null) {
// Implementasikan logika untuk menyimpan data audio secara lokal di sini
System.out.println(TimeUtils.getTimestamp() + " Audio received");
}
// Dapatkan informasi output, termasuk tipe event dan teks asli
if (result.getOutput() != null && result.getOutput().has("type")) {
System.out.println("Event type: " + result.getOutput().get("type").getAsString()
+ ", Original text: " + (result.getOutput().has("original_text") ? result.getOutput().get("original_text").getAsString() : ""));
}
}
@Override
public void onComplete() {
System.out.println(TimeUtils.getTimestamp() + " Complete received, speech synthesis finished");
latch.countDown();
}
@Override
public void onError(Exception e) {
System.out.println("Exception occurred: " + e.toString());
latch.countDown();
}
};
// Parameter permintaan
SpeechSynthesisParam param =
SpeechSynthesisParam.builder()
// Kunci API untuk wilayah Singapura dan Beijing berbeda. Dapatkan Kunci API: https://www.alibabacloud.com/help/en/model-studio/get-api-key
// Jika variabel lingkungan belum dikonfigurasi, ganti baris berikut dengan Kunci API Studio Model Anda: .apiKey("sk-xxx")
.apiKey(System.getenv("DASHSCOPE_API_KEY"))
.model(model) // Model
.voice(voice) // Voice
.build();
// Memberikan "callback" sebagai parameter kedua mengaktifkan mode asinkron
SpeechSynthesizer synthesizer = new SpeechSynthesizer(param, callback);
// Panggilan non-blocking yang langsung mengembalikan null (hasil aktual dikirimkan secara asinkron melalui antarmuka callback). Audio biner diterima secara real time melalui metode onEvent antarmuka callback
try {
synthesizer.call("What's the weather like today?");
// Tunggu hingga sintesis selesai
latch.await();
// Tunggu hingga thread pemutaran selesai memutar
} catch (Exception e) {
throw new RuntimeException(e);
} finally {
// Tutup koneksi WebSocket setelah tugas selesai
synthesizer.getDuplexApi().close(1000, "bye");
}
// Transmisi teks pertama memerlukan pembentukan koneksi WebSocket, sehingga latensi paket pertama mencakup waktu pembentukan koneksi
System.out.println(
"[Metric] requestId: "
+ synthesizer.getLastRequestId()
+ ", first packet latency (ms): "
+ synthesizer.getFirstPackageDelay());
}
public static void main(String[] args) {
// Konfigurasi berikut untuk wilayah Singapura. Ganti "{WorkspaceId}" dengan workspace ID Anda yang sebenarnya. Konfigurasi berbeda untuk setiap wilayah.
Constants.baseWebsocketApiUrl = "wss://{WorkspaceId}.ap-southeast-1.maas.aliyuncs.com/api-ws/v1/inference";
streamAudioDataToSpeaker();
System.exit(0);
}
}
Panggilan bidirectional streaming
Panjang teks per pemanggilan individu tidak boleh melebihi 20.000 karakter, dan panjang teks kumulatif dari semua pemanggilan tidak boleh melebihi 200.000 karakter.
-
Saat input streaming, panggil
streamingCallbeberapa kali untuk mengirimkan segmen teks secara berurutan. Server secara otomatis membagi teks yang diterima menjadi kalimat:- Kalimat lengkap langsung disintesis.
- Kalimat tidak lengkap disimpan dalam buffer hingga membentuk kalimat lengkap.
Ketika
streamingCompletedipanggil, server memaksa mensintesis semua segmen teks yang diterima namun belum diproses, termasuk kalimat tidak lengkap. -
Interval antara segmen teks berturut-turut tidak boleh melebihi 23 detik; melebihi batas ini akan memicu exception "request timeout after 23 seconds".
Jika tidak ada teks yang tertunda, segera panggil
streamingCompleteuntuk mengakhiri tugas.PentingSelalu panggil metode
streamingComplete. Jika tidak, segmen teks akhir mungkin tidak dikonversi menjadi suara.Server menerapkan timeout 23 detik yang tidak dapat diubah di sisi klien.
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 = {"Streaming text-to-speech SDK, ",
"can convert input text ", "into audio binary data. ", "Compared to non-streaming speech synthesis, ",
"streaming synthesis offers better real-time performance. ", "While users input text, ",
"they can hear nearly synchronous audio output, ", "greatly enhancing the interactive experience ",
"and reducing user wait time. ", "It is suitable for calling large-scale ", "language models (LLMs) to ",
"perform speech synthesis ", "with streaming text input."};
private static String model = "qwen-audio-3.0-tts-flash"; // Model
private static String voice = "longanhuan_v3.6"; // Voice
public static void streamAudioDataToSpeaker() {
// Konfigurasikan fungsi callback
ResultCallback<SpeechSynthesisResult> callback = new ResultCallback<SpeechSynthesisResult>() {
@Override
public void onEvent(SpeechSynthesisResult result) {
// System.out.println("Message received: " + result);
if (result.getAudioFrame() != null) {
// Implementasikan logika untuk memproses data audio di sini
System.out.println(TimeUtils.getTimestamp() + " Audio received");
}
}
@Override
public void onComplete() {
System.out.println(TimeUtils.getTimestamp() + " Complete received, speech synthesis finished");
}
@Override
public void onError(Exception e) {
System.out.println("Exception occurred: " + e.toString());
}
};
// Parameter permintaan
SpeechSynthesisParam param =
SpeechSynthesisParam.builder()
// Kunci API untuk wilayah Singapura dan Beijing berbeda. Dapatkan Kunci API: https://www.alibabacloud.com/help/en/model-studio/get-api-key
// Jika variabel lingkungan belum dikonfigurasi, ganti baris berikut dengan Kunci API Studio Model Anda: .apiKey("sk-xxx")
.apiKey(System.getenv("DASHSCOPE_API_KEY"))
.model(model)
.voice(voice)
.format(SpeechSynthesisAudioFormat
.PCM_22050HZ_MONO_16BIT) // Gunakan PCM atau MP3 untuk sintesis streaming
.build();
SpeechSynthesizer synthesizer = new SpeechSynthesizer(param, callback);
// Metode call dengan Callback tidak akan memblokir thread saat ini
try {
for (String text : textArray) {
// Kirim fragmen teks dan terima audio biner secara real time melalui metode onEvent antarmuka callback
synthesizer.streamingCall(text);
}
// Tunggu hingga sintesis suara streaming selesai
synthesizer.streamingComplete();
} catch (Exception e) {
throw new RuntimeException(e);
} finally {
// Tutup koneksi WebSocket setelah tugas selesai
synthesizer.getDuplexApi().close(1000, "bye");
}
// Transmisi teks pertama memerlukan pembentukan koneksi WebSocket, sehingga latensi paket pertama mencakup waktu pembentukan koneksi
System.out.println(
"[Metric] requestId: "
+ synthesizer.getLastRequestId()
+ ", first packet latency (ms): "
+ synthesizer.getFirstPackageDelay());
}
public static void main(String[] args) {
// Konfigurasi berikut untuk wilayah Singapura. Ganti "{WorkspaceId}" dengan workspace ID Anda yang sebenarnya. Konfigurasi berbeda untuk setiap wilayah.
Constants.baseWebsocketApiUrl = "wss://{WorkspaceId}.ap-southeast-1.maas.aliyuncs.com/api-ws/v1/inference";
streamAudioDataToSpeaker();
System.exit(0);
}
}
Panggilan berbasis Flowable
Flowable adalah tipe RxJava yang merepresentasikan stream reaktif yang mendukung backpressure. Untuk informasi lebih lanjut, lihat dokumentasi RxJava Flowable.
Sebelum menggunakan Flowable, pastikan library RxJava telah diintegrasikan dan Anda memahami dasar-dasar pemrograman reaktif.
Panjang teks per pemanggilan individu tidak boleh melebihi 20.000 karakter, dan panjang teks kumulatif dari semua pemanggilan tidak boleh melebihi 200.000 karakter.
Panggilan unidirectional streaming
Contoh berikut menunjukkan cara menggunakan antarmuka blockingForEach objek Flowable untuk mengambil setiap objek SpeechSynthesisResult yang distream secara blocking.
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"; // Model
private static String voice = "longanhuan_v3.6"; // Voice
public static void streamAudioDataToSpeaker() throws NoApiKeyException {
// Parameter permintaan
SpeechSynthesisParam param =
SpeechSynthesisParam.builder()
// Kunci API untuk wilayah Singapura dan Beijing berbeda. Dapatkan Kunci API: https://www.alibabacloud.com/help/en/model-studio/get-api-key
// Jika variabel lingkungan belum dikonfigurasi, ganti baris berikut dengan Kunci API Studio Model Anda: .apiKey("sk-xxx")
.apiKey(System.getenv("DASHSCOPE_API_KEY"))
.model(model) // Model
.voice(voice) // Voice
.build();
SpeechSynthesizer synthesizer = new SpeechSynthesizer(param, null);
synthesizer.callAsFlowable("What's the weather like today?").blockingForEach(result -> {
if (result.getAudioFrame() != null) {
// Implementasikan logika untuk memproses data audio di sini
System.out.println(TimeUtils.getTimestamp() + " Audio received");
}
// Dapatkan informasi output, termasuk tipe event dan teks asli
if (result.getOutput() != null && result.getOutput().has("type")) {
System.out.println("Event type: " + result.getOutput().get("type").getAsString()
+ ", Original text: " + (result.getOutput().has("original_text") ? result.getOutput().get("original_text").getAsString() : ""));
}
});
// Tutup koneksi WebSocket setelah tugas selesai
synthesizer.getDuplexApi().close(1000, "bye");
// Transmisi teks pertama memerlukan pembentukan koneksi WebSocket, sehingga latensi paket pertama mencakup waktu pembentukan koneksi
System.out.println(
"[Metric] requestId: "
+ synthesizer.getLastRequestId()
+ ", first packet latency (ms): "
+ synthesizer.getFirstPackageDelay());
}
public static void main(String[] args) throws NoApiKeyException {
// Konfigurasi berikut untuk wilayah Singapura. Ganti "{WorkspaceId}" dengan workspace ID Anda yang sebenarnya. Konfigurasi berbeda untuk setiap wilayah.
Constants.baseWebsocketApiUrl = "wss://{WorkspaceId}.ap-southeast-1.maas.aliyuncs.com/api-ws/v1/inference";
streamAudioDataToSpeaker();
System.exit(0);
}
}
Panggilan bidirectional streaming
Contoh berikut menunjukkan cara menggunakan objek Flowable sebagai parameter input untuk streaming teks, dan menggunakan antarmuka blockingForEach objek Flowable yang dikembalikan untuk mengambil setiap objek SpeechSynthesisResult yang distream secara blocking.
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 = {"Streaming text-to-speech SDK, ",
"can convert input text ", "into audio binary data. ", "Compared to non-streaming speech synthesis, ",
"streaming synthesis offers better real-time performance. ", "While users input text, ",
"they can hear nearly synchronous audio output, ", "greatly enhancing the interactive experience ",
"and reducing user wait time. ", "It is suitable for calling large-scale ", "language models (LLMs) to ",
"perform speech synthesis ", "with streaming text input."};
private static String model = "qwen-audio-3.0-tts-flash";
private static String voice = "longanhuan_v3.6";
public static void streamAudioDataToSpeaker() throws NoApiKeyException {
// Simulasikan input streaming
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);
// Parameter permintaan
SpeechSynthesisParam param =
SpeechSynthesisParam.builder()
// Kunci API untuk wilayah Singapura dan Beijing berbeda. Dapatkan Kunci API: https://www.alibabacloud.com/help/en/model-studio/get-api-key
// Jika variabel lingkungan belum dikonfigurasi, ganti baris berikut dengan Kunci API Studio Model Anda: .apiKey("sk-xxx")
.apiKey(System.getenv("DASHSCOPE_API_KEY"))
.model(model) // Model
.voice(voice) // Voice
.build();
SpeechSynthesizer synthesizer = new SpeechSynthesizer(param, null);
synthesizer.streamingCallAsFlowable(textSource).blockingForEach(result -> {
if (result.getAudioFrame() != null) {
// Implementasikan logika untuk memutar audio di sini
System.out.println(
TimeUtils.getTimestamp() +
" Binary audio size: " + result.getAudioFrame().capacity());
}
// Dapatkan informasi output, termasuk tipe event dan teks asli
if (result.getOutput() != null && result.getOutput().has("type")) {
System.out.println("Event type: " + result.getOutput().get("type").getAsString()
+ ", Original text: " + (result.getOutput().has("original_text") ? result.getOutput().get("original_text").getAsString() : ""));
}
});
synthesizer.getDuplexApi().close(1000, "bye");
// Transmisi teks pertama memerlukan pembentukan koneksi WebSocket, sehingga latensi paket pertama mencakup waktu pembentukan koneksi
System.out.println(
"[Metric] requestId: "
+ synthesizer.getLastRequestId()
+ ", first packet latency (ms): "
+ synthesizer.getFirstPackageDelay());
}
public static void main(String[] args) throws NoApiKeyException {
// Konfigurasi berikut untuk wilayah Singapura. Ganti "{WorkspaceId}" dengan workspace ID Anda yang sebenarnya. Konfigurasi berbeda untuk setiap wilayah.
Constants.baseWebsocketApiUrl = "wss://{WorkspaceId}.ap-southeast-1.maas.aliyuncs.com/api-ws/v1/inference";
streamAudioDataToSpeaker();
System.exit(0);
}
}
Panggilan konkurensi tinggi
DashScope Java SDK menggunakan pooling koneksi OkHttp3 untuk mengurangi overhead pembentukan koneksi berulang. Untuk informasi lebih lanjut, lihat Praktik terbaik konkurensi tinggi.