All Products
Search
Document Center

Alibaba Cloud Model Studio:SDK Java sintesis suara Qwen-Audio-TTS/CosyVoice

Last Updated:Sep 02, 2026

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.com ke {WorkspaceId}.cn-beijing.maas.aliyuncs.com
  • Singapura: dari dashscope-intl.aliyuncs.com ke {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

MetodeTipe parameterWajibDeskripsi

model(String)

String

Ya

Nama model.

voice(String)

String

Ya

voicestring(wajib)

Voice yang digunakan untuk sintesis suara.

format(SpeechSynthesisAudioFormat)

enum

Tidak

Format encoding audio dan laju sampel.

Default: SpeechSynthesisAudioFormat.MP3_22050HZ_MONO_256KBPS.

Package SpeechSynthesisAudioFormat: com.alibaba.dashscope.audio.ttsv2.SpeechSynthesisAudioFormat.

volume(int)

int

Tidak

Tingkat volume.

Nilai default: 50.

Nilai valid: [0, 100].

speechRate(float)

float

Tidak

Laju bicara.

Nilai default: 1.0.

Nilai valid: [0.5, 2.0].

pitchRate(float)

float

Tidak

Pitch.

Nilai default: 1.0.

Nilai valid: [0.5, 2.0].

enableWordTimestamp(boolean)

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.

seed(int)

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 seed melalui parameter tambahan.

languageHints(List<String>)

List<String>

Tidak

Penting

  • Parameter ini berupa array, tetapi versi saat ini hanya memproses elemen pertama. Berikan satu nilai saja.
  • Parameter ini menentukan bahasa target untuk sintesis suara. Tidak terkait dengan bahasa sampel audio yang digunakan dalam voice cloning. Untuk mengatur bahasa sumber dalam tugas kloning, lihat referensi API voice cloning.

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:

  • Pelafalan angka tidak sesuai: "hello, this is 110" dibaca sebagai "hello, this is one zero" alih-alih pelafalan Mandarin yang diharapkan
  • Pelafalan simbol tidak akurat: "@" dibaca sebagai ekuivalen Mandarin alih-alih "at"
  • Kualitas sintesis bahasa minoritas buruk dengan hasil tidak alami

Nilai valid:

  • zh: Tionghoa
  • en: Inggris
  • fr: Prancis
  • de: Jerman
  • ja: Jepang
  • ko: Korea
  • ru: Rusia
  • pt: Portugis
  • th: Thai
  • id: Bahasa Indonesia
  • vi: Bahasa Vietnam
  • es: Spanyol
  • it: Italia
  • ms: Bahasa Melayu
  • fil: Filipino
  • ar: Arab

instruction(String)

String

Tidak

Mengontrol karakteristik sintesis seperti dialek, emosi, atau gaya bicara.

Untuk detail penggunaan, lihat Kontrol instruksi.

hotFix(ParamHotFix)

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:

  • pronunciation: Pelafalan kustom. Menentukan anotasi pinyin untuk kata-kata guna memperbaiki pelafalan default yang tidak akurat.
  • replace: Penggantian teks. Mengganti kata-kata tertentu dengan teks target sebelum sintesis. Teks yang diganti digunakan sebagai input sintesis sebenarnya.

Contoh:

List<ParamHotFix.PronunciationItem> pronunciationItems = new ArrayList<>();
pronunciationItems.add(new ParamHotFix.PronunciationItem("weather", "tian1 qi4"));

List<ParamHotFix.ReplaceItem> replaceItems = new ArrayList<>();
replaceItems.add(new ParamHotFix.ReplaceItem("today", "gold day"));

ParamHotFix paramHotFix = new ParamHotFix();
paramHotFix.setPronunciation(pronunciationItems);
paramHotFix.setReplace(replaceItems);

SpeechSynthesisParam param = SpeechSynthesisParam.builder()
                        .model("qwen-audio-3.0-tts-flash") // Model
                        .voice("longanhuan_v3.6") // Voice
                        .hotFix(paramHotFix)
                        .build();

parameter(String key, Object value)

String, Object

Tidak

Mengatur Parameter tambahan.

parameters(Map<String, Object>)

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

bit_rate

integer

Tidak

Laju bit audio dalam kbps. Ketika format audio adalah mp3 atau opus, gunakan bit_rate untuk menyesuaikan laju bit.

Nilai default: 32.

Nilai valid: [6, 510].

enable_aigc_tag

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.

aigc_propagator

String

Tidak

Mengatur bidang ContentPropagator dalam watermark AIGC, mengidentifikasi propagator konten. Hanya berlaku ketika enable_aigc_tag bernilai true.

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.

aigc_propagate_id

String

Tidak

Mengatur bidang PropagateID dalam watermark AIGC, mengidentifikasi secara unik aksi propagasi tertentu. Hanya berlaku ketika enable_aigc_tag bernilai true.

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.

enable_markdown_filter

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:

  • true: Aktifkan penyaringan Markdown

  • false: Nonaktifkan penyaringan Markdown

ResultCallback

Package: com.alibaba.dashscope.common.ResultCallback

onEvent() - Menerima audio data

Signature metode:

public void onEvent(SpeechSynthesisResult result)

Parameter:

Parameter

Tipe

Wajib

Deskripsi

result

SpeechSynthesisResult

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: sentence-begin (awal kalimat; mengembalikan teks yang akan disintesis), sentence-synthesis (sedang berlangsung sintesis audio; mengembalikan chunk data audio), sentence-end (akhir kalimat; mengembalikan konten teks dan timestamp tingkat kata).

original_text

String

Teks asli kalimat saat ini. Dikembalikan dalam event sentence-begin dan sentence-end.

sentence

JsonObject

Informasi kalimat, berisi indeks kalimat (index) dan timestamp tingkat kata (words). Event sentence-end mencakup informasi timestamp tingkat kata lengkap.

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 streamingCall beberapa 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 streamingComplete dipanggil, 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 streamingComplete untuk 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.