All Products
Search
Document Center

Alibaba Cloud Model Studio:Sintesis suara real-time - Qwen

Last Updated:May 09, 2026

Qwen TTS real-time melakukan streaming input teks dan output audio melalui WebSocket, menyediakan berbagai suara alami dalam berbagai bahasa dan dialek—bahkan dalam satu suara yang sama—serta menyesuaikan intonasi secara otomatis untuk menangani teks kompleks secara natural.

Fitur utama

  • Menghasilkan suara berkualitas tinggi secara real-time dengan output multibahasa alami, termasuk Mandarin dan Inggris

  • Menyediakan dua metode kustomisasi suara: kloning suara Qwen dan desain suara Qwen

  • Streaming input dan output untuk interaksi real-time berlatensi rendah

  • Kontrol detail halus atas laju ucapan, pitch, volume, dan bitrate

  • Format PCM, WAV, MP3, dan Opus dengan laju sampel hingga 48 kHz

  • Mendukung kontrol instruksi, yang membentuk ekspresivitas suara melalui instruksi bahasa alami

Model yang didukung

Tiongkok daratan

Jika Anda memilih cakupan penerapan Tiongkok daratan, sumber daya komputasi inferensi model hanya tersedia di Tiongkok daratan. Data statis disimpan di wilayah yang Anda pilih. Wilayah yang didukung: Tiongkok (Beijing).

Untuk memanggil model berikut, gunakan Kunci API dari wilayah Beijing:

  • Qwen3-TTS-Instruct-Flash-Realtime: qwen3-tts-instruct-flash-realtime (versi stabil, saat ini setara dengan qwen3-tts-instruct-flash-realtime-2026-01-22), qwen3-tts-instruct-flash-realtime-2026-01-22 (snapshot terbaru)

  • Qwen3-TTS-VD-Realtime: qwen3-tts-vd-realtime-2026-01-15 (snapshot terbaru), qwen3-tts-vd-realtime-2025-12-16 (snapshot)

  • Qwen3-TTS-VC-Realtime: qwen3-tts-vc-realtime-2026-01-15 (snapshot terbaru), qwen3-tts-vc-realtime-2025-11-27 (snapshot)

  • Qwen3-TTS-Flash-Realtime: qwen3-tts-flash-realtime (versi stabil, saat ini setara dengan qwen3-tts-flash-realtime-2025-11-27), qwen3-tts-flash-realtime-2025-11-27 (snapshot terbaru), qwen3-tts-flash-realtime-2025-09-18 (snapshot)

  • Qwen-TTS-Realtime: qwen-tts-realtime (versi stabil, saat ini setara dengan qwen-tts-realtime-2025-07-15), qwen-tts-realtime-latest (versi terbaru, saat ini setara dengan qwen-tts-realtime-2025-07-15), qwen-tts-realtime-2025-07-15 (snapshot)

Internasional

Jika Anda memilih cakupan penerapan Internasional, sumber daya komputasi inferensi model dialokasikan secara dinamis di seluruh dunia, tidak termasuk Tiongkok daratan. Data statis disimpan di wilayah yang Anda pilih. Wilayah yang didukung: Singapura.

Untuk memanggil model berikut, gunakan Kunci API dari wilayah Singapura:

  • Qwen3-TTS-Instruct-Flash-Realtime: qwen3-tts-instruct-flash-realtime (versi stabil, saat ini setara dengan qwen3-tts-instruct-flash-realtime-2026-01-22), qwen3-tts-instruct-flash-realtime-2026-01-22 (snapshot terbaru)

  • Qwen3-TTS-VD-Realtime: qwen3-tts-vd-realtime-2026-01-15 (snapshot terbaru), qwen3-tts-vd-realtime-2025-12-16 (snapshot)

  • Qwen3-TTS-VC-Realtime: qwen3-tts-vc-realtime-2026-01-15 (snapshot terbaru), qwen3-tts-vc-realtime-2025-11-27 (snapshot)

  • Qwen3-TTS-Flash-Realtime: qwen3-tts-flash-realtime (versi stabil, saat ini setara dengan qwen3-tts-flash-realtime-2025-11-27), qwen3-tts-flash-realtime-2025-11-27 (snapshot terbaru), qwen3-tts-flash-realtime-2025-09-18 (snapshot)

Internasional

Jika Anda memilih cakupan penerapan Internasional, sumber daya komputasi inferensi model dijadwalkan secara dinamis di seluruh dunia, kecuali di Tiongkok daratan. Data statis disimpan di wilayah yang Anda pilih. Wilayah yang didukung: Singapura.

Untuk memanggil model berikut, gunakan Kunci API dari wilayah Singapura:

  • Qwen3-TTS-Instruct-Flash-Realtime: qwen3-tts-instruct-flash-realtime (versi stabil, saat ini setara dengan qwen3-tts-instruct-flash-realtime-2026-01-22), qwen3-tts-instruct-flash-realtime-2026-01-22 (versi snapshot terbaru)

  • Qwen3-TTS-VD-Realtime: qwen3-tts-vd-realtime-2026-01-15 (versi snapshot terbaru), qwen3-tts-vd-realtime-2025-12-16 (versi snapshot)

  • Qwen3-TTS-VC-Realtime: qwen3-tts-vc-realtime-2026-01-15 (versi snapshot terbaru), qwen3-tts-vc-realtime-2025-11-27 (versi snapshot)

  • Qwen3-TTS-Flash-Realtime: qwen3-tts-flash-realtime (versi stabil, saat ini setara dengan qwen3-tts-flash-realtime-2025-11-27), qwen3-tts-flash-realtime-2025-11-27 (versi snapshot terbaru), qwen3-tts-flash-realtime-2025-09-18 (versi snapshot)

Tiongkok daratan

Jika Anda memilih cakupan penerapan Tiongkok daratan, sumber daya komputasi inferensi model hanya tersedia di Tiongkok daratan. Data statis disimpan di wilayah yang Anda pilih. Wilayah yang didukung: Tiongkok (Beijing).

Untuk memanggil model berikut, gunakan Kunci API dari wilayah Beijing:

  • Qwen3-TTS-Instruct-Flash-Realtime: qwen3-tts-instruct-flash-realtime (versi stabil, saat ini setara dengan qwen3-tts-instruct-flash-realtime-2026-01-22), qwen3-tts-instruct-flash-realtime-2026-01-22 (versi snapshot terbaru)

  • Qwen3-TTS-VD-Realtime: qwen3-tts-vd-realtime-2026-01-15 (versi snapshot terbaru), qwen3-tts-vd-realtime-2025-12-16 (versi snapshot)

  • Qwen3-TTS-VC-Realtime: qwen3-tts-vc-realtime-2026-01-15 (versi snapshot terbaru), qwen3-tts-vc-realtime-2025-11-27 (versi snapshot)

  • Qwen3-TTS-Flash-Realtime: qwen3-tts-flash-realtime (versi stabil, saat ini setara dengan qwen3-tts-flash-realtime-2025-11-27), qwen3-tts-flash-realtime-2025-11-27 (versi snapshot terbaru), qwen3-tts-flash-realtime-2025-09-18 (versi snapshot)

  • Qwen-TTS-Realtime: qwen-tts-realtime (versi stabil, saat ini setara dengan qwen-tts-realtime-2025-07-15), qwen-tts-realtime-latest (versi terbaru, saat ini setara dengan qwen-tts-realtime-2025-07-15), qwen-tts-realtime-2025-07-15 (versi snapshot)

Pilih model

Kasus penggunaan

Model yang direkomendasikan

Mengapa

Suara kustom untuk identitas merek, suara eksklusif, atau perluasan suara sistem (berbasis teks)

qwen3-tts-vd-realtime-2026-01-15

Desain suara: membuat suara kustom dari deskripsi teks tanpa sampel audio — ideal untuk membangun suara merek dari awal.

Suara kustom untuk identitas merek, suara eksklusif, atau perluasan suara sistem (berbasis audio)

qwen3-tts-vc-realtime-2026-01-15

Kloning suara: mereplikasi suara secara cepat dari sampel audio nyata untuk menghasilkan suara merek yang konsisten dan mirip manusia.

Produksi konten ekspresif (audiobook, drama radio, pengisi suara game atau animasi)

qwen3-tts-instruct-flash-realtime

Kontrol instruksi: menentukan pitch, laju ucapan, emosi, dan karakteristik tokoh melalui bahasa alami untuk ekspresivitas kaya dan permainan peran.

Narasi profesional (berita, dokumenter, iklan)

qwen3-tts-instruct-flash-realtime

Kontrol instruksi: mendeskripsikan gaya narasi dan nada (misalnya, "otoritatif dan formal" atau "kasual dan ramah") untuk produksi berkualitas profesional.

Layanan pelanggan cerdas dan chatbot

qwen3-tts-flash-realtime, qwen3-tts-instruct-flash-realtime

Input/output streaming dengan laju ucapan dan pitch yang dapat disesuaikan. Varian Instruct memungkinkan penyesuaian nada dinamis (menenangkan, antusias, profesional) berdasarkan konteks percakapan.

Pengiriman konten multibahasa

qwen3-tts-flash-realtime, qwen3-tts-instruct-flash-realtime

Banyak bahasa dan dialek Mandarin untuk distribusi konten global.

Pembacaan audio dan produksi konten umum

qwen3-tts-flash-realtime, qwen3-tts-instruct-flash-realtime

Volume, laju ucapan, dan pitch yang dapat disesuaikan untuk produksi audiobook dan podcast.

Livestreaming E-dagang dan pengisi suara video pendek

qwen3-tts-flash-realtime, qwen3-tts-instruct-flash-realtime

Format terkompresi MP3 dan Opus untuk skenario dengan bandwidth terbatas.

Untuk informasi lebih lanjut, lihat Perbandingan fitur model

Memulai dengan cepat

Sebelum menjalankan kode, dapatkan dan konfigurasikan Kunci API Anda. Untuk integrasi berbasis SDK, instal SDK DashScope versi terbaru.

Sintesis suara dengan suara sistem

Contoh berikut mensintesis suara dengan suara sistem (lihat Suara yang didukung).

Untuk mengaktifkan kontrol instruksi, ganti model dengan qwen3-tts-instruct-flash-realtime dan atur parameter instructions.

SDK DashScope

Python

Mode commit server

import os
import base64
import threading
import time
import dashscope
from dashscope.audio.qwen_tts_realtime import *


qwen_tts_realtime: QwenTtsRealtime = None
text_to_synthesize = [
    'Right? I love supermarkets like this.',
    'Especially during Chinese New Year,',
    'I go shopping at supermarkets.',
    'And I feel',
    'absolutely thrilled!',
    'I want to buy so many things!'
]

DO_VIDEO_TEST = False

def init_dashscope_api_key():
    """
        Setel Kunci API DashScope Anda. Informasi lebih lanjut:
        https://github.com/aliyun/alibabacloud-bailian-speech-demo/blob/master/PREREQUISITES.md
    """

    # Kunci API berbeda antara wilayah Singapura dan Beijing. Dapatkan Kunci API: https://www.alibabacloud.com/help/zh/model-studio/get-api-key
    if 'DASHSCOPE_API_KEY' in os.environ:
        dashscope.api_key = os.environ[
            'DASHSCOPE_API_KEY']  # Muat Kunci API dari variabel lingkungan DASHSCOPE_API_KEY
    else:
        dashscope.api_key = 'your-dashscope-api-key'  # Setel Kunci API secara manual



class MyCallback(QwenTtsRealtimeCallback):
    def __init__(self):
        self.complete_event = threading.Event()
        self.file = open('result_24k.pcm', 'wb')

    def on_open(self) -> None:
        print('koneksi dibuka, inisialisasi pemutar')

    def on_close(self, close_status_code, close_msg) -> None:
        self.file.close()
        print('koneksi ditutup dengan kode: {}, pesan: {}, hancurkan pemutar'.format(close_status_code, close_msg))

    def on_event(self, response: str) -> None:
        try:
            global qwen_tts_realtime
            type = response['type']
            if 'session.created' == type:
                print('mulai sesi: {}'.format(response['session']['id']))
            if 'response.audio.delta' == type:
                recv_audio_b64 = response['delta']
                self.file.write(base64.b64decode(recv_audio_b64))
            if 'response.done' == type:
                print(f'respons {qwen_tts_realtime.get_last_response_id()} selesai')
            if 'session.finished' == type:
                print('sesi selesai')
                self.complete_event.set()
        except Exception as e:
            print('[Error] {}'.format(e))
            return

    def wait_for_finished(self):
        self.complete_event.wait()


if __name__  == '__main__':
    init_dashscope_api_key()

    print('Menginisialisasi ...')

    callback = MyCallback()

    qwen_tts_realtime = QwenTtsRealtime(
        # Untuk menggunakan kontrol instruksi, ganti model dengan qwen3-tts-instruct-flash-realtime
        model='qwen3-tts-flash-realtime',
        callback=callback,
        # URL ini untuk wilayah Singapura. Jika Anda menggunakan wilayah Beijing, ganti dengan: wss://dashscope.aliyuncs.com/api-ws/v1/realtime
        url='wss://dashscope-intl.aliyuncs.com/api-ws/v1/realtime'
        )

    qwen_tts_realtime.connect()
    qwen_tts_realtime.update_session(
        voice = 'Cherry',
        response_format = AudioFormat.PCM_24000HZ_MONO_16BIT,
        # Untuk menggunakan kontrol instruksi, hapus komentar baris berikut dan ganti model dengan qwen3-tts-instruct-flash-realtime
        # instructions='Berbicara cepat dengan intonasi naik, cocok untuk memperkenalkan produk fesyen.',
        # optimize_instructions=True,
        mode = 'server_commit'        
    )
    for text_chunk in text_to_synthesize:
        print(f'kirim teks: {text_chunk}')
        qwen_tts_realtime.append_text(text_chunk)
        time.sleep(0.1)
    qwen_tts_realtime.finish()
    callback.wait_for_finished()
    print('[Metric] sesi: {}, delay audio pertama: {}'.format(
                    qwen_tts_realtime.get_session_id(), 
                    qwen_tts_realtime.get_first_audio_delay(),
                    ))

Mode commit

import base64
import os
import threading
import dashscope
from dashscope.audio.qwen_tts_realtime import *


qwen_tts_realtime: QwenTtsRealtime = None
text_to_synthesize = [
    'This is the first sentence.',
    'This is the second sentence.',
    'This is the third sentence.',
]

DO_VIDEO_TEST = False

def init_dashscope_api_key():
    """
        Setel Kunci API DashScope Anda. Informasi lebih lanjut:
        https://github.com/aliyun/alibabacloud-bailian-speech-demo/blob/master/PREREQUISITES.md
    """

    # Kunci API berbeda antara wilayah Singapura dan Beijing. Dapatkan Kunci API: https://www.alibabacloud.com/help/zh/model-studio/get-api-key
    if 'DASHSCOPE_API_KEY' in os.environ:
        dashscope.api_key = os.environ[
            'DASHSCOPE_API_KEY']  # Muat Kunci API dari variabel lingkungan DASHSCOPE_API_KEY
    else:
        dashscope.api_key = 'your-dashscope-api-key'  # Setel Kunci API secara manual



class MyCallback(QwenTtsRealtimeCallback):
    def __init__(self):
        super().__init__()
        self.response_counter = 0
        self.complete_event = threading.Event()
        self.file = open(f'result_{self.response_counter}_24k.pcm', 'wb')

    def reset_event(self):
        self.response_counter += 1
        self.file = open(f'result_{self.response_counter}_24k.pcm', 'wb')
        self.complete_event = threading.Event()

    def on_open(self) -> None:
        print('koneksi dibuka, inisialisasi pemutar')

    def on_close(self, close_status_code, close_msg) -> None:
        print('koneksi ditutup dengan kode: {}, pesan: {}, hancurkan pemutar'.format(close_status_code, close_msg))

    def on_event(self, response: str) -> None:
        try:
            global qwen_tts_realtime
            type = response['type']
            if 'session.created' == type:
                print('mulai sesi: {}'.format(response['session']['id']))
            if 'response.audio.delta' == type:
                recv_audio_b64 = response['delta']
                self.file.write(base64.b64decode(recv_audio_b64))
            if 'response.done' == type:
                print(f'respons {qwen_tts_realtime.get_last_response_id()} selesai')
                self.complete_event.set()
                self.file.close()
            if 'session.finished' == type:
                print('sesi selesai')
                self.complete_event.set()
        except Exception as e:
            print('[Error] {}'.format(e))
            return

    def wait_for_response_done(self):
        self.complete_event.wait()


if __name__  == '__main__':
    init_dashscope_api_key()

    print('Menginisialisasi ...')

    callback = MyCallback()

    qwen_tts_realtime = QwenTtsRealtime(
        # Untuk menggunakan kontrol instruksi, ganti model dengan qwen3-tts-instruct-flash-realtime
        model='qwen3-tts-flash-realtime',
        callback=callback, 
        # URL ini untuk wilayah Singapura. Jika Anda menggunakan wilayah Beijing, ganti dengan: wss://dashscope.aliyuncs.com/api-ws/v1/realtime
        url='wss://dashscope-intl.aliyuncs.com/api-ws/v1/realtime'
        )

    qwen_tts_realtime.connect()
    qwen_tts_realtime.update_session(
        voice = 'Cherry',
        response_format = AudioFormat.PCM_24000HZ_MONO_16BIT,
        # Untuk menggunakan kontrol instruksi, hapus komentar baris berikut dan ganti model dengan qwen3-tts-instruct-flash-realtime
        # instructions='Berbicara cepat dengan intonasi naik, cocok untuk memperkenalkan produk fesyen.',
        # optimize_instructions=True,
        mode = 'commit'        
    )
    print(f'kirim teks: {text_to_synthesize[0]}')
    qwen_tts_realtime.append_text(text_to_synthesize[0])
    qwen_tts_realtime.commit()
    callback.wait_for_response_done()
    callback.reset_event()
    
    print(f'kirim teks: {text_to_synthesize[1]}')
    qwen_tts_realtime.append_text(text_to_synthesize[1])
    qwen_tts_realtime.commit()
    callback.wait_for_response_done()
    callback.reset_event()

    print(f'kirim teks: {text_to_synthesize[2]}')
    qwen_tts_realtime.append_text(text_to_synthesize[2])
    qwen_tts_realtime.commit()
    callback.wait_for_response_done()
    
    qwen_tts_realtime.finish()
    print('[Metric] sesi: {}, delay audio pertama: {}'.format(
                    qwen_tts_realtime.get_session_id(), 
                    qwen_tts_realtime.get_first_audio_delay(),
                    ))

Java

Mode server commit

appendText()

import com.alibaba.dashscope.audio.qwen_tts_realtime.*;
import com.alibaba.dashscope.exception.NoApiKeyException;
import com.google.gson.JsonObject;
import javax.sound.sampled.LineUnavailableException;
import javax.sound.sampled.SourceDataLine;
import javax.sound.sampled.AudioFormat;
import javax.sound.sampled.DataLine;
import javax.sound.sampled.AudioSystem;
import java.io.*;
import java.util.Base64;
import java.util.Queue;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicReference;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.atomic.AtomicBoolean;

public class Main {
    static String[] textToSynthesize = {
            "Right? I really love this kind of supermarket.",
            "Especially during the Chinese New Year.",
            "Going to the supermarket.",
            "It just makes me feel.",
            "Super, super happy!",
            "I want to buy so many things!"
    };
    public static QwenTtsRealtimeAudioFormat ttsFormat = QwenTtsRealtimeAudioFormat.PCM_24000HZ_MONO_16BIT;

    // Pemutar audio PCM real-time
    public static class RealtimePcmPlayer {
        private int sampleRate;
        private SourceDataLine line;
        private AudioFormat audioFormat;
        private Thread decoderThread;
        private Thread playerThread;
        private AtomicBoolean stopped = new AtomicBoolean(false);
        private Queue<String> b64AudioBuffer = new ConcurrentLinkedQueue<>();
        private Queue<byte[]> RawAudioBuffer = new ConcurrentLinkedQueue<>();
        private ByteArrayOutputStream totalAudioStream = new ByteArrayOutputStream();

        // Inisialisasi format audio dan saluran audio.
        public RealtimePcmPlayer(int sampleRate) throws LineUnavailableException {
            this.sampleRate = sampleRate;
            this.audioFormat = new AudioFormat(this.sampleRate, 16, 1, true, false);
            DataLine.Info info = new DataLine.Info(SourceDataLine.class, audioFormat);
            line = (SourceDataLine) AudioSystem.getLine(info);
            line.open(audioFormat);
            line.start();
            decoderThread = new Thread(new Runnable() {
                @Override
                public void run() {
                    while (!stopped.get()) {
                        String b64Audio = b64AudioBuffer.poll();
                        if (b64Audio != null) {
                            byte[] rawAudio = Base64.getDecoder().decode(b64Audio);
                            RawAudioBuffer.add(rawAudio);
                            // Tulis data audio ke totalAudioStream.
                            try {
                                totalAudioStream.write(rawAudio);
                            } catch (IOException e) {
                                throw new RuntimeException(e);
                            }
                        } else {
                            try {
                                Thread.sleep(100);
                            } catch (InterruptedException e) {
                                throw new RuntimeException(e);
                            }
                        }
                    }
                }
            });
            playerThread = new Thread(new Runnable() {
                @Override
                public void run() {
                    while (!stopped.get()) {
                        byte[] rawAudio = RawAudioBuffer.poll();
                        if (rawAudio != null) {
                            try {
                                playChunk(rawAudio);
                            } catch (IOException e) {
                                throw new RuntimeException(e);
                            } catch (InterruptedException e) {
                                throw new RuntimeException(e);
                            }
                        } else {
                            try {
                                Thread.sleep(100);
                            } catch (InterruptedException e) {
                                throw new RuntimeException(e);
                            }
                        }
                    }
                }
            });
            decoderThread.start();
            playerThread.start();
        }

        // Putar potongan audio dan blokir hingga pemutaran selesai.
        private void playChunk(byte[] chunk) throws IOException, InterruptedException {
            if (chunk == null || chunk.length == 0) return;

            int bytesWritten = 0;
            while (bytesWritten < chunk.length) {
                bytesWritten += line.write(chunk, bytesWritten, chunk.length - bytesWritten);
            }
            int audioLength = chunk.length / (this.sampleRate*2/1000);
            // Tunggu hingga audio dalam buffer selesai diputar.
            Thread.sleep(audioLength - 10);
        }

        public void write(String b64Audio) {
            b64AudioBuffer.add(b64Audio);
        }

        public void cancel() {
            b64AudioBuffer.clear();
            RawAudioBuffer.clear();
        }

        public void waitForComplete() throws InterruptedException {
            while (!b64AudioBuffer.isEmpty() || !RawAudioBuffer.isEmpty()) {
                Thread.sleep(100);
            }
            line.drain();
        }

        public void shutdown() throws InterruptedException, IOException {
            stopped.set(true);
            decoderThread.join();
            playerThread.join();

            // Simpan file audio lengkap.
            File file = new File("TotalAudio_"+ttsFormat.getSampleRate()+"."+ttsFormat.getFormat());
            try (FileOutputStream fos = new FileOutputStream(file)) {
                fos.write(totalAudioStream.toByteArray());
            }

            if (line != null && line.isRunning()) {
                line.drain();
                line.close();
            }
        }
    }

    public static void main(String[] args) throws InterruptedException, LineUnavailableException, IOException {
        QwenTtsRealtimeParam param = QwenTtsRealtimeParam.builder()
                // Untuk menggunakan kontrol instruksi, ganti model dengan qwen3-tts-instruct-flash-realtime.
                .model("qwen3-tts-flash-realtime")
                // Titik akhir Singapura. Untuk Tiongkok (Beijing), gunakan wss://dashscope.aliyuncs.com/api-ws/v1/realtime.
                .url("wss://dashscope-intl.aliyuncs.com/api-ws/v1/realtime")
                // Kunci API berbeda antara Singapura dan Tiongkok (Beijing). Lihat https://www.alibabacloud.com/help/zh/model-studio/get-api-key.
                .apikey(System.getenv("DASHSCOPE_API_KEY"))
                .build();
        AtomicReference<CountDownLatch> completeLatch = new AtomicReference<>(new CountDownLatch(1));
        final AtomicReference<QwenTtsRealtime> qwenTtsRef = new AtomicReference<>(null);

        // Buat instance pemutar audio real-time.
        RealtimePcmPlayer audioPlayer = new RealtimePcmPlayer(24000);

        QwenTtsRealtime qwenTtsRealtime = new QwenTtsRealtime(param, new QwenTtsRealtimeCallback() {
            @Override
            public void onOpen() {
                // Tangani pembentukan koneksi.
            }
            @Override
            public void onEvent(JsonObject message) {
                String type = message.get("type").getAsString();
                switch(type) {
                    case "session.created":
                        // Tangani pembuatan sesi.
                        if (message.has("session")) {
                            String eventId = message.get("event_id").getAsString();
                            String sessionId = message.get("session").getAsJsonObject().get("id").getAsString();
                            System.out.println("[onEvent] session.created, session_id: "
                                    + sessionId + ", event_id: " + eventId);
                        }
                        break;
                    case "response.audio.delta":
                        String recvAudioB64 = message.get("delta").getAsString();
                        // Putar audio secara real-time.
                        audioPlayer.write(recvAudioB64);
                        break;
                    case "response.done":
                        // Tangani penyelesaian respons.
                        break;
                    case "session.finished":
                        // Tangani penghentian sesi.
                        completeLatch.get().countDown();
                    default:
                        break;
                }
            }
            @Override
            public void onClose(int code, String reason) {
                // Tangani penutupan koneksi.
            }
        });
        qwenTtsRef.set(qwenTtsRealtime);
        try {
            qwenTtsRealtime.connect();
        } catch (NoApiKeyException e) {
            throw new RuntimeException(e);
        }
        QwenTtsRealtimeConfig config = QwenTtsRealtimeConfig.builder()
                .voice("Cherry")
                .responseFormat(ttsFormat)
                .mode("server_commit")
                // Untuk menggunakan kontrol instruksi, hapus komentar baris berikut dan ganti model dengan qwen3-tts-instruct-flash-realtime.
                // .instructions("")
                // .optimizeInstructions(true)
                .build();
        qwenTtsRealtime.updateSession(config);
        for (String text:textToSynthesize) {
            qwenTtsRealtime.appendText(text);
            Thread.sleep(100);
        }
        qwenTtsRealtime.finish();
        completeLatch.get().await();
        qwenTtsRealtime.close();

        // Tunggu hingga pemutaran audio selesai, lalu matikan pemutar.
        audioPlayer.waitForComplete();
        audioPlayer.shutdown();
        System.exit(0);
    }
}

Mode commit

commit()

import com.alibaba.dashscope.audio.qwen_tts_realtime.*;
import com.alibaba.dashscope.exception.NoApiKeyException;
import com.google.gson.JsonObject;
import javax.sound.sampled.LineUnavailableException;
import javax.sound.sampled.SourceDataLine;
import javax.sound.sampled.AudioFormat;
import javax.sound.sampled.DataLine;
import javax.sound.sampled.AudioSystem;
import java.io.*;
import java.util.Base64;
import java.util.Queue;
import java.util.Scanner;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicReference;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.atomic.AtomicBoolean;

public class Main {
    public static QwenTtsRealtimeAudioFormat ttsFormat = QwenTtsRealtimeAudioFormat.PCM_24000HZ_MONO_16BIT;
    // Pemutar audio PCM real-time
    public static class RealtimePcmPlayer {
        private int sampleRate;
        private SourceDataLine line;
        private AudioFormat audioFormat;
        private Thread decoderThread;
        private Thread playerThread;
        private AtomicBoolean stopped = new AtomicBoolean(false);
        private Queue<String> b64AudioBuffer = new ConcurrentLinkedQueue<>();
        private Queue<byte[]> RawAudioBuffer = new ConcurrentLinkedQueue<>();
        private ByteArrayOutputStream totalAudioStream = new ByteArrayOutputStream();


        // Inisialisasi format audio dan saluran audio.
        public RealtimePcmPlayer(int sampleRate) throws LineUnavailableException {
            this.sampleRate = sampleRate;
            this.audioFormat = new AudioFormat(this.sampleRate, 16, 1, true, false);
            DataLine.Info info = new DataLine.Info(SourceDataLine.class, audioFormat);
            line = (SourceDataLine) AudioSystem.getLine(info);
            line.open(audioFormat);
            line.start();
            decoderThread = new Thread(new Runnable() {
                @Override
                public void run() {
                    while (!stopped.get()) {
                        String b64Audio = b64AudioBuffer.poll();
                        if (b64Audio != null) {
                            byte[] rawAudio = Base64.getDecoder().decode(b64Audio);
                            RawAudioBuffer.add(rawAudio);
                            // Tulis data audio ke totalAudioStream.
                            try {
                                totalAudioStream.write(rawAudio);
                            } catch (IOException e) {
                                throw new RuntimeException(e);
                            }
                        } else {
                            try {
                                Thread.sleep(100);
                            } catch (InterruptedException e) {
                                throw new RuntimeException(e);
                            }
                        }
                    }
                }
            });
            playerThread = new Thread(new Runnable() {
                @Override
                public void run() {
                    while (!stopped.get()) {
                        byte[] rawAudio = RawAudioBuffer.poll();
                        if (rawAudio != null) {
                            try {
                                playChunk(rawAudio);
                            } catch (IOException e) {
                                throw new RuntimeException(e);
                            } catch (InterruptedException e) {
                                throw new RuntimeException(e);
                            }
                        } else {
                            try {
                                Thread.sleep(100);
                            } catch (InterruptedException e) {
                                throw new RuntimeException(e);
                            }
                        }
                    }
                }
            });
            decoderThread.start();
            playerThread.start();
        }

        // Putar potongan audio dan blokir hingga pemutaran selesai.
        private void playChunk(byte[] chunk) throws IOException, InterruptedException {
            if (chunk == null || chunk.length == 0) return;

            int bytesWritten = 0;
            while (bytesWritten < chunk.length) {
                bytesWritten += line.write(chunk, bytesWritten, chunk.length - bytesWritten);
            }
            int audioLength = chunk.length / (this.sampleRate*2/1000);
            // Tunggu hingga audio dalam buffer selesai diputar.
            Thread.sleep(audioLength - 10);
        }

        public void write(String b64Audio) {
            b64AudioBuffer.add(b64Audio);
        }

        public void cancel() {
            b64AudioBuffer.clear();
            RawAudioBuffer.clear();
        }

        public void waitForComplete() throws InterruptedException {
            // Tunggu hingga semua data audio dalam buffer selesai diputar.
            while (!b64AudioBuffer.isEmpty() || !RawAudioBuffer.isEmpty()) {
                Thread.sleep(100);
            }
            // Tunggu hingga saluran audio dikosongkan.
            line.drain();
        }

        public void shutdown() throws InterruptedException {
            stopped.set(true);
            decoderThread.join();
            playerThread.join();
            // Simpan file audio lengkap.
            File file = new File("TotalAudio_"+ttsFormat.getSampleRate()+"."+ttsFormat.getFormat());
            try (FileOutputStream fos = new FileOutputStream(file)) {
                fos.write(totalAudioStream.toByteArray());
            } catch (FileNotFoundException e) {
                throw new RuntimeException(e);
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
            if (line != null && line.isRunning()) {
                line.drain();
                line.close();
            }
        }
    }

    public static void main(String[] args) throws InterruptedException, LineUnavailableException, FileNotFoundException {
        Scanner scanner = new Scanner(System.in);

        QwenTtsRealtimeParam param = QwenTtsRealtimeParam.builder()
                // Untuk menggunakan kontrol instruksi, ganti model dengan qwen3-tts-instruct-flash-realtime.
                .model("qwen3-tts-flash-realtime")
                // Titik akhir Singapura. Untuk Tiongkok (Beijing), gunakan wss://dashscope.aliyuncs.com/api-ws/v1/realtime.
                .url("wss://dashscope-intl.aliyuncs.com/api-ws/v1/realtime")
                // Kunci API berbeda antara Singapura dan Tiongkok (Beijing). Lihat https://www.alibabacloud.com/help/zh/model-studio/get-api-key.
                .apikey(System.getenv("DASHSCOPE_API_KEY"))
                .build();

        AtomicReference<CountDownLatch> completeLatch = new AtomicReference<>(new CountDownLatch(1));

        // Buat instance pemutar real-time.
        RealtimePcmPlayer audioPlayer = new RealtimePcmPlayer(24000);

        final AtomicReference<QwenTtsRealtime> qwenTtsRef = new AtomicReference<>(null);
        QwenTtsRealtime qwenTtsRealtime = new QwenTtsRealtime(param, new QwenTtsRealtimeCallback() {
            @Override
            public void onOpen() {
                System.out.println("koneksi dibuka");
                System.out.println("Masukkan teks dan tekan Enter untuk mengirim. Masukkan 'quit' untuk keluar dari program.");
            }
            @Override
            public void onEvent(JsonObject message) {
                String type = message.get("type").getAsString();
                switch(type) {
                    case "session.created":
                        System.out.println("mulai sesi: " + message.get("session").getAsJsonObject().get("id").getAsString());
                        break;
                    case "response.audio.delta":
                        String recvAudioB64 = message.get("delta").getAsString();
                        byte[] rawAudio = Base64.getDecoder().decode(recvAudioB64);
                        // Putar audio secara real-time.
                        audioPlayer.write(recvAudioB64);
                        break;
                    case "response.done":
                        System.out.println("respons selesai");
                        // Tunggu hingga pemutaran audio selesai.
                        try {
                            audioPlayer.waitForComplete();
                        } catch (InterruptedException e) {
                            throw new RuntimeException(e);
                        }
                        // Siapkan untuk input berikutnya.
                        completeLatch.get().countDown();
                        break;
                    case "session.finished":
                        System.out.println("sesi selesai");
                        if (qwenTtsRef.get() != null) {
                            System.out.println("[Metric] respons: " + qwenTtsRef.get().getResponseId() +
                                    ", delay audio pertama: " + qwenTtsRef.get().getFirstAudioDelay() + " ms");
                        }
                        completeLatch.get().countDown();
                    default:
                        break;
                }
            }
            @Override
            public void onClose(int code, String reason) {
                System.out.println("koneksi ditutup kode: " + code + ", alasan: " + reason);
                try {
                    // Tunggu hingga pemutaran selesai, lalu matikan pemutar.
                    audioPlayer.waitForComplete();
                    audioPlayer.shutdown();
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        });
        qwenTtsRef.set(qwenTtsRealtime);
        try {
            qwenTtsRealtime.connect();
        } catch (NoApiKeyException e) {
            throw new RuntimeException(e);
        }
        QwenTtsRealtimeConfig config = QwenTtsRealtimeConfig.builder()
                .voice("Cherry")
                .responseFormat(ttsFormat)
                .mode("commit")
                // Untuk menggunakan kontrol instruksi, hapus komentar baris berikut dan ganti model dengan qwen3-tts-instruct-flash-realtime.
                // .instructions("")
                // .optimizeInstructions(true)
                .build();
        qwenTtsRealtime.updateSession(config);

        // Baca input pengguna dalam loop.
        while (true) {
            System.out.print("Masukkan teks untuk disintesis: ");
            String text = scanner.nextLine();

            // Keluar ketika pengguna memasukkan 'quit'.
            if ("quit".equalsIgnoreCase(text.trim())) {
                System.out.println("Menutup koneksi...");
                qwenTtsRealtime.finish();
                completeLatch.get().await();
                break;
            }

            // Lewati input kosong.
            if (text.trim().isEmpty()) {
                continue;
            }

            // Inisialisasi ulang latch countdown.
            completeLatch.set(new CountDownLatch(1));

            // Kirim teks.
            qwenTtsRealtime.appendText(text);
            qwenTtsRealtime.commit();

            // Tunggu hingga sintesis saat ini selesai.
            completeLatch.get().await();
        }

        // Bersihkan sumber daya.
        audioPlayer.waitForComplete();
        audioPlayer.shutdown();
        scanner.close();
        System.exit(0);
    }
}

WebSocket API

  1. Siapkan lingkungan

    Python

    Instal pyaudio sesuai sistem operasi Anda.

    macOS

    brew install portaudio && pip install pyaudio

    Debian/Ubuntu

    sudo apt-get install python3-pyaudio
    
    # atau
    
    pip install pyaudio

    CentOS

    sudo yum install -y portaudio portaudio-devel && pip install pyaudio

    Windows

    pip install pyaudio

    Setelah instalasi, instal dependensi WebSocket melalui pip:

    pip install websocket-client==1.8.0 websockets

    Java

    Tambahkan dependensi berikut ke proyek Anda:

    Maven

    Tambahkan berikut ke pom.xml:

    <!-- Library Java-WebSocket -->
    <dependency>
        <groupId>org.java-websocket</groupId>
        <artifactId>Java-WebSocket</artifactId>
        <version>1.5.7</version>
    </dependency>
    <!-- Gson untuk pemrosesan JSON -->
    <dependency>
        <groupId>com.google.code.gson</groupId>
        <artifactId>gson</artifactId>
        <version>2.13.1</version>
    </dependency>

    Gradle

    Tambahkan berikut ke build.gradle:

    // Library Java-WebSocket
    implementation("org.java-websocket:Java-WebSocket:1.5.7")
    // Gson untuk pemrosesan JSON
    implementation("com.google.code.gson:gson:2.13.1")
  2. Buat klien

    Python

    Buat file Python bernama tts_realtime_client.py dan salin kode berikut ke dalamnya:

    tts_realtime_client.py

    # -- coding: utf-8 --
    
    import asyncio
    import websockets
    import json
    import base64
    import time
    from typing import Optional, Callable, Dict, Any
    from enum import Enum
    
    class SessionMode(Enum):
        SERVER_COMMIT = "server_commit"
        COMMIT = "commit"
    
    class TTSRealtimeClient:
        """
        Klien untuk berinteraksi dengan API TTS Realtime.
    
        Kelas ini menyediakan metode untuk terhubung ke API TTS Realtime, mengirim data teks, menerima output audio, dan mengelola koneksi WebSocket.
    
        Atribut:
            base_url (str):
                URL dasar API Realtime.
            api_key (str):
                Kunci API yang digunakan untuk otentikasi.
            voice (str):
                Suara yang digunakan oleh server untuk sintesis ucapan.
            mode (SessionMode):
                Mode sesi, baik server_commit maupun commit.
            audio_callback (Callable[[bytes], None]):
                Fungsi callback untuk menerima data audio.
            language_type(str)
                Bahasa dari ucapan yang disintesis. Nilai yang valid: Chinese, English, German, Italian, Portuguese, Spanish, Japanese, Korean, French, Russian, Auto
        """
    
        def __init__(
                self,
                base_url: str,
                api_key: str,
                voice: str = "Cherry",
                mode: SessionMode = SessionMode.SERVER_COMMIT,
                audio_callback: Optional[Callable[[bytes], None]] = None,
            language_type: str = "Auto"):
            self.base_url = base_url
            self.api_key = api_key
            self.voice = voice
            self.mode = mode
            self.ws = None
            self.audio_callback = audio_callback
            self.language_type = language_type
    
            # Status respons saat ini
            self._current_response_id = None
            self._current_item_id = None
            self._is_responding = False
            self._response_done_future = None
    
        async def connect(self) -> None:
            """Membuat koneksi WebSocket ke API TTS Realtime."""
            headers = {
                "Authorization": f"Bearer {self.api_key}"
            }
    
            self.ws = await websockets.connect(self.base_url, additional_headers=headers)
    
            # Atur konfigurasi sesi default
            await self.update_session({
                "mode": self.mode.value,
                "voice": self.voice,
                # Untuk menggunakan fitur kontrol instruksi, hapus komentar baris di bawah dan ganti model di server_commit.py atau commit.py dengan qwen3-tts-instruct-flash-realtime
                # "instructions": "Berbicara cepat dengan intonasi naik yang terlihat jelas, cocok untuk memperkenalkan produk fesyen.",
                # "optimize_instructions": true
                "language_type": self.language_type,
                "response_format": "pcm",
                "sample_rate": 24000
            })
    
        async def send_event(self, event) -> None:
            """Mengirim event ke server."""
            event['event_id'] = "event_" + str(int(time.time() * 1000))
            print(f"Mengirim event: type={event['type']}, event_id={event['event_id']}")
            await self.ws.send(json.dumps(event))
    
        async def update_session(self, config: Dict[str, Any]) -> None:
            """Memperbarui konfigurasi sesi."""
            event = {
                "type": "session.update",
                "session": config
            }
            print("Memperbarui konfigurasi sesi: ", event)
            await self.send_event(event)
    
        async def append_text(self, text: str) -> None:
            """Mengirim data teks ke API."""
            event = {
                "type": "input_text_buffer.append",
                "text": text
            }
            await self.send_event(event)
    
        async def commit_text_buffer(self) -> None:
            """Commit buffer teks untuk memicu pemrosesan."""
            event = {
                "type": "input_text_buffer.commit"
            }
            await self.send_event(event)
    
        async def clear_text_buffer(self) -> None:
            """Menghapus buffer teks."""
            event = {
                "type": "input_text_buffer.clear"
            }
            await self.send_event(event)
    
        async def finish_session(self) -> None:
            """Mengakhiri sesi."""
            event = {
                "type": "session.finish"
            }
            await self.send_event(event)
    
        async def wait_for_response_done(self):
            """Menunggu event response.done"""
            if self._response_done_future:
                await self._response_done_future
    
        async def handle_messages(self) -> None:
            """Menangani pesan dari server."""
            try:
                async for message in self.ws:
                    event = json.loads(message)
                    event_type = event.get("type")
    
                    if event_type != "response.audio.delta":
                        print(f"Menerima event: {event_type}")
    
                    if event_type == "error":
                        print("Error: ", event.get('error', {}))
                        continue
                    elif event_type == "session.created":
                        print("Sesi dibuat, ID: ", event.get('session', {}).get('id'))
                    elif event_type == "session.updated":
                        print("Sesi diperbarui, ID: ", event.get('session', {}).get('id'))
                    elif event_type == "input_text_buffer.committed":
                        print("Buffer teks dikomit, ID item: ", event.get('item_id'))
                    elif event_type == "input_text_buffer.cleared":
                        print("Buffer teks dihapus")
                    elif event_type == "response.created":
                        self._current_response_id = event.get("response", {}).get("id")
                        self._is_responding = True
                        # Buat future baru untuk menunggu response.done
                        self._response_done_future = asyncio.Future()
                        print("Respons dibuat, ID: ", self._current_response_id)
                    elif event_type == "response.output_item.added":
                        self._current_item_id = event.get("item", {}).get("id")
                        print("Item output ditambahkan, ID: ", self._current_item_id)
                    # Tangani delta audio
                    elif event_type == "response.audio.delta" and self.audio_callback:
                        audio_bytes = base64.b64decode(event.get("delta", ""))
                        self.audio_callback(audio_bytes)
                    elif event_type == "response.audio.done":
                        print("Generasi audio selesai")
                    elif event_type == "response.done":
                        self._is_responding = False
                        self._current_response_id = None
                        self._current_item_id = None
                        # Tandai future sebagai selesai
                        if self._response_done_future and not self._response_done_future.done():
                            self._response_done_future.set_result(True)
                        print("Respons selesai")
                    elif event_type == "session.finished":
                        print("Sesi berakhir")
    
            except websockets.exceptions.ConnectionClosed:
                print("Koneksi ditutup")
            except Exception as e:
                print("Error menangani pesan: ", str(e))
    
        async def close(self) -> None:
            """Menutup koneksi WebSocket."""
            if self.ws:
                await self.ws.close()

    Java

    Buat file Java bernama TTSRealtimeClient.java dan salin kode berikut ke dalamnya:

    import com.google.gson.Gson;
    import com.google.gson.JsonObject;
    import org.java_websocket.client.WebSocketClient;
    import org.java_websocket.handshake.ServerHandshake;
    
    import java.net.URI;
    import java.util.Base64;
    import java.util.HashMap;
    import java.util.Map;
    import java.util.concurrent.CountDownLatch;
    import java.util.function.Consumer;
    
    /**
     * Klien untuk berinteraksi dengan API TTS Realtime.
     *
     * Kelas ini menyediakan metode untuk terhubung ke API TTS Realtime, mengirim data teks, menerima output audio, dan mengelola koneksi WebSocket.
     */
    public class TTSRealtimeClient {
    
        public enum SessionMode {
            SERVER_COMMIT("server_commit"),
            COMMIT("commit");
            private final String value;
            SessionMode(String value) { this.value = value; }
            public String getValue() { return value; }
        }
    
        /**
         * Antarmuka callback audio
         */
        public interface AudioCallback {
            void onAudio(byte[] audioData);
        }
    
        private final String baseUrl;
        private final String apiKey;
        private final String voice;
        private final SessionMode mode;
        private final String languageType;
        private final AudioCallback audioCallback;
        private final Gson gson = new Gson();
    
        private WebSocketClient ws;
        private CountDownLatch responseDoneLatch;
        private CountDownLatch sessionFinishedLatch;
    
        public TTSRealtimeClient(String baseUrl, String apiKey, String voice,
                                 SessionMode mode, AudioCallback audioCallback,
                                 String languageType) {
            this.baseUrl = baseUrl;
            this.apiKey = apiKey;
            this.voice = voice;
            this.mode = mode;
            this.audioCallback = audioCallback;
            this.languageType = languageType;
        }
    
        public TTSRealtimeClient(String baseUrl, String apiKey, String voice,
                                 SessionMode mode, AudioCallback audioCallback) {
            this(baseUrl, apiKey, voice, mode, audioCallback, "Auto");
        }
    
        /**
         * Membuat koneksi WebSocket ke API TTS Realtime.
         */
        public void connect() throws Exception {
            Map<String, String> headers = new HashMap<>();
            headers.put("Authorization", "Bearer " + apiKey);
    
            responseDoneLatch = new CountDownLatch(0);
            sessionFinishedLatch = new CountDownLatch(1);
    
            ws = new WebSocketClient(new URI(baseUrl), headers) {
                @Override
                public void onOpen(ServerHandshake handshake) {
                    System.out.println("Koneksi WebSocket dibentuk");
                    // Kirim konfigurasi sesi default
                    JsonObject session = new JsonObject();
                    session.addProperty("mode", mode.getValue());
                    session.addProperty("voice", TTSRealtimeClient.this.voice);
                    // Untuk menggunakan fitur kontrol instruksi, hapus komentar baris di bawah dan ganti model dengan qwen3-tts-instruct-flash-realtime
                    // session.addProperty("instructions", "Berbicara cepat dengan intonasi naik yang terlihat jelas, cocok untuk memperkenalkan produk fesyen.");
                    // session.addProperty("optimize_instructions", true);
                    session.addProperty("language_type", languageType);
                    session.addProperty("response_format", "pcm");
                    session.addProperty("sample_rate", 24000);
                    updateSession(session);
                }
    
                @Override
                public void onMessage(String message) {
                    JsonObject event = gson.fromJson(message, JsonObject.class);
                    String eventType = event.has("type") ? event.get("type").getAsString() : "";
    
                    if (!"response.audio.delta".equals(eventType)) {
                        System.out.println("Menerima event: " + eventType);
                    }
    
                    switch (eventType) {
                        case "error":
                            System.err.println("Error: " + event.get("error"));
                            break;
                        case "session.created":
                            System.out.println("Sesi dibuat, ID: " +
                                event.getAsJsonObject("session").get("id").getAsString());
                            break;
                        case "session.updated":
                            System.out.println("Sesi diperbarui, ID: " +
                                event.getAsJsonObject("session").get("id").getAsString());
                            break;
                        case "input_text_buffer.committed":
                            System.out.println("Buffer teks dikomit, ID item: " + event.get("item_id"));
                            break;
                        case "input_text_buffer.cleared":
                            System.out.println("Buffer teks dihapus");
                            break;
                        case "response.created":
                            System.out.println("Respons dibuat, ID: " +
                                event.getAsJsonObject("response").get("id").getAsString());
                            responseDoneLatch = new CountDownLatch(1);
                            break;
                        case "response.output_item.added":
                            System.out.println("Item output ditambahkan, ID: " +
                                event.getAsJsonObject("item").get("id").getAsString());
                            break;
                        case "response.audio.delta":
                            if (audioCallback != null) {
                                byte[] audioBytes = Base64.getDecoder().decode(
                                    event.get("delta").getAsString());
                                audioCallback.onAudio(audioBytes);
                            }
                            break;
                        case "response.audio.done":
                            System.out.println("Generasi audio selesai");
                            break;
                        case "response.done":
                            System.out.println("Respons selesai");
                            responseDoneLatch.countDown();
                            break;
                        case "session.finished":
                            System.out.println("Sesi berakhir");
                            sessionFinishedLatch.countDown();
                            break;
                    }
                }
    
                @Override
                public void onClose(int code, String reason, boolean remote) {
                    System.out.println("Koneksi ditutup: " + reason);
                }
    
                @Override
                public void onError(Exception ex) {
                    System.err.println("Error WebSocket: " + ex.getMessage());
                }
            };
            ws.connectBlocking();
        }
    
        /**
         * Mengirim event ke server.
         */
        public void sendEvent(JsonObject event) {
            String eventId = "event_" + System.currentTimeMillis();
            event.addProperty("event_id", eventId);
            System.out.println("Mengirim event: type=" + event.get("type").getAsString()
                + ", event_id=" + eventId);
            ws.send(gson.toJson(event));
        }
    
        /**
         * Memperbarui konfigurasi sesi.
         */
        public void updateSession(JsonObject config) {
            JsonObject event = new JsonObject();
            event.addProperty("type", "session.update");
            event.add("session", config);
            System.out.println("Memperbarui konfigurasi sesi: " + event);
            sendEvent(event);
        }
    
        /**
         * Mengirim data teks ke API.
         */
        public void appendText(String text) {
            JsonObject event = new JsonObject();
            event.addProperty("type", "input_text_buffer.append");
            event.addProperty("text", text);
            sendEvent(event);
        }
    
        /**
         * Commit buffer teks untuk memicu pemrosesan.
         */
        public void commitTextBuffer() {
            JsonObject event = new JsonObject();
            event.addProperty("type", "input_text_buffer.commit");
            sendEvent(event);
        }
    
        /**
         * Menghapus buffer teks.
         */
        public void clearTextBuffer() {
            JsonObject event = new JsonObject();
            event.addProperty("type", "input_text_buffer.clear");
            sendEvent(event);
        }
    
        /**
         * Mengakhiri sesi.
         */
        public void finishSession() {
            JsonObject event = new JsonObject();
            event.addProperty("type", "session.finish");
            sendEvent(event);
        }
    
        /**
         * Menunggu event response.done.
         */
        public void waitForResponseDone() throws InterruptedException {
            responseDoneLatch.await();
        }
    
        /**
         * Menunggu event session.finished.
         */
        public void waitForSessionFinished() throws InterruptedException {
            sessionFinishedLatch.await();
        }
    
        /**
         * Menutup koneksi WebSocket.
         */
        public void close() {
            if (ws != null) {
                ws.close();
            }
        }
    }
  3. Pilih mode sintesis

    API Realtime mendukung dua mode:

    • server_commit mode

      Klien hanya mengirim teks. Server melakukan segmentasi dan menentukan waktu sintesis secara otomatis. Mode ini paling cocok untuk skenario latensi rendah tanpa kontrol pacing manual, seperti navigasi GPS.

    • commit mode

      Klien menambahkan teks ke buffer, lalu secara eksplisit melakukan commit untuk memicu sintesis. Mode ini paling cocok untuk skenario yang membutuhkan kontrol detail halus atas segmentasi kalimat dan jeda, seperti siaran berita.

    server_commit mode

    Python

    Buat file Python bernama server_commit.py di direktori yang sama dengan tts_realtime_client.py, lalu salin kode berikut ke dalamnya:

    server_commit.py

    import os
    import asyncio
    import logging
    import wave
    from tts_realtime_client import TTSRealtimeClient, SessionMode
    import pyaudio
    
    # Konfigurasi layanan QwenTTS
    # Untuk menggunakan fitur kontrol instruksi, ganti model dengan qwen3-tts-instruct-flash-realtime dan hapus komentar baris instructions di tts_realtime_client.py
    # Berikut ini adalah URL untuk wilayah Singapura. Jika Anda menggunakan model di wilayah Beijing, ganti URL dengan: wss://dashscope.aliyuncs.com/api-ws/v1/realtime?model=qwen3-tts-flash-realtime
    URL = "wss://dashscope-intl.aliyuncs.com/api-ws/v1/realtime?model=qwen3-tts-flash-realtime"
    # Kunci API untuk wilayah Singapura dan Beijing berbeda. Dapatkan Kunci API: https://www.alibabacloud.com/help/zh/model-studio/get-api-key
    # Jika Anda belum mengonfigurasi variabel lingkungan, ganti baris berikut dengan Kunci API Model Studio Anda: API_KEY="sk-xxx"
    API_KEY = os.getenv("DASHSCOPE_API_KEY")
    
    if not API_KEY:
        raise ValueError("Harap setel variabel lingkungan DASHSCOPE_API_KEY")
    
    # Kumpulkan data audio
    _audio_chunks = []
    # Terkait pemutaran real-time
    _AUDIO_SAMPLE_RATE = 24000
    _audio_pyaudio = pyaudio.PyAudio()
    _audio_stream = None  # Akan dibuka saat runtime
    
    def _audio_callback(audio_bytes: bytes):
        """Callback audio TTSRealtimeClient: pemutaran real-time dan caching"""
        global _audio_stream
        if _audio_stream is not None:
            try:
                _audio_stream.write(audio_bytes)
            except Exception as exc:
                logging.error(f"Error pemutaran PyAudio: {exc}")
        _audio_chunks.append(audio_bytes)
        logging.info(f"Menerima potongan audio: {len(audio_bytes)} byte")
    
    def _save_audio_to_file(filename: str = "output.wav", sample_rate: int = 24000) -> bool:
        """Simpan data audio yang dikumpulkan ke file WAV"""
        if not _audio_chunks:
            logging.warning("Tidak ada data audio untuk disimpan")
            return False
    
        try:
            audio_data = b"".join(_audio_chunks)
            with wave.open(filename, 'wb') as wav_file:
                wav_file.setnchannels(1)  # Mono
                wav_file.setsampwidth(2)  # 16-bit
                wav_file.setframerate(sample_rate)
                wav_file.writeframes(audio_data)
            logging.info(f"Audio disimpan ke: {filename}")
            return True
        except Exception as exc:
            logging.error(f"Gagal menyimpan audio: {exc}")
            return False
    
    async def _produce_text(client: TTSRealtimeClient):
        """Kirim fragmen teks ke server"""
        text_fragments = [
            "Platform model bahasa besar Alibaba Cloud, Model Studio, adalah platform all-in-one untuk mengembangkan dan membangun aplikasi model bahasa besar.",
            "Baik pengembang maupun pengguna bisnis dapat berpartisipasi secara mendalam dalam desain dan pengembangan aplikasi model bahasa besar.",
            "Anda dapat mengembangkan aplikasi model bahasa besar dalam lima menit menggunakan antarmuka sederhana,",
            "atau melatih model khusus dalam beberapa jam, sehingga Anda dapat lebih fokus pada inovasi aplikasi.",
        ]
    
        logging.info("Mengirim fragmen teks…")
        for text in text_fragments:
            logging.info(f"Mengirim fragmen: {text}")
            await client.append_text(text)
            await asyncio.sleep(0.1)  # Jeda singkat antar fragmen
    
        # Tunggu hingga server menyelesaikan pemrosesan internal sebelum mengakhiri sesi
        await asyncio.sleep(1.0)
        await client.finish_session()
    
    async def _run_demo():
        """Jalankan demo lengkap"""
        global _audio_stream
        # Buka stream output PyAudio
        _audio_stream = _audio_pyaudio.open(
            format=pyaudio.paInt16,
            channels=1,
            rate=_AUDIO_SAMPLE_RATE,
            output=True,
            frames_per_buffer=1024
        )
    
        client = TTSRealtimeClient(
            base_url=URL,
            api_key=API_KEY,
            voice="Cherry",
            mode=SessionMode.SERVER_COMMIT,
            audio_callback=_audio_callback
        )
    
        # Bentuk koneksi
        await client.connect()
    
        # Jalankan penanganan pesan dan pengiriman teks secara paralel
        consumer_task = asyncio.create_task(client.handle_messages())
        producer_task = asyncio.create_task(_produce_text(client))
    
        await producer_task  # Tunggu hingga pengiriman teks selesai
    
        # Tunggu response.done
        await client.wait_for_response_done()
    
        # Tutup koneksi dan batalkan tugas consumer
        await client.close()
        consumer_task.cancel()
    
        # Tutup stream audio
        if _audio_stream is not None:
            _audio_stream.stop_stream()
            _audio_stream.close()
        _audio_pyaudio.terminate()
    
        # Simpan data audio
        os.makedirs("outputs", exist_ok=True)
        _save_audio_to_file(os.path.join("outputs", "qwen_tts_output.wav"))
    
    def main():
        """Titik masuk sinkron"""
        logging.basicConfig(
            level=logging.INFO,
            format='%(asctime)s [%(levelname)s] %(message)s',
            datefmt='%Y-%m-%d %H:%M:%S'
        )
        logging.info("Memulai demo Klien Realtime QwenTTS…")
        asyncio.run(_run_demo())
    
    if __name__ == "__main__":
        main()

    Jalankan server_commit.py untuk mendengar audio yang dihasilkan oleh API Realtime secara real-time.

    Java

    Buat file Java bernama ServerCommit.java di direktori yang sama dengan TTSRealtimeClient.java, lalu salin kode berikut ke dalamnya:

    import javax.sound.sampled.*;
    import java.io.*;
    import java.util.ArrayList;
    import java.util.List;
    import java.util.concurrent.ConcurrentLinkedQueue;
    import java.util.concurrent.atomic.AtomicBoolean;
    
    public class ServerCommit {
        // Berikut ini adalah URL untuk wilayah Singapura. Jika Anda menggunakan model di wilayah Beijing, ganti URL dengan: wss://dashscope.aliyuncs.com/api-ws/v1/realtime?model=qwen3-tts-flash-realtime
        private static final String URL = "wss://dashscope-intl.aliyuncs.com/api-ws/v1/realtime?model=qwen3-tts-flash-realtime";
        // Kunci API untuk wilayah Singapura dan Beijing berbeda. Dapatkan Kunci API: https://www.alibabacloud.com/help/zh/model-studio/get-api-key
        // Jika Anda belum mengonfigurasi variabel lingkungan, ganti baris berikut dengan Kunci API Model Studio Anda: private static final String API_KEY = "sk-xxx";
        private static final String API_KEY = System.getenv("DASHSCOPE_API_KEY");
        private static final int SAMPLE_RATE = 24000;
    
        // Cache data audio
        private static final List<byte[]> audioChunks = new ArrayList<>();
        // Antrian pemutaran real-time
        private static final ConcurrentLinkedQueue<byte[]> playbackQueue = new ConcurrentLinkedQueue<>();
        private static final AtomicBoolean playing = new AtomicBoolean(true);
    
        public static void main(String[] args) throws Exception {
            if (API_KEY == null || API_KEY.isEmpty()) {
                throw new IllegalStateException("Harap setel variabel lingkungan DASHSCOPE_API_KEY");
            }
    
            // Inisialisasi pemutaran audio
            AudioFormat format = new AudioFormat(SAMPLE_RATE, 16, 1, true, false);
            DataLine.Info info = new DataLine.Info(SourceDataLine.class, format);
            SourceDataLine audioLine = (SourceDataLine) AudioSystem.getLine(info);
            audioLine.open(format);
            audioLine.start();
    
            // Mulai thread pemutaran
            Thread playerThread = new Thread(() -> {
                while (playing.get() || !playbackQueue.isEmpty()) {
                    byte[] chunk = playbackQueue.poll();
                    if (chunk != null) {
                        audioLine.write(chunk, 0, chunk.length);
                    } else {
                        try { Thread.sleep(10); } catch (InterruptedException ignored) {}
                    }
                }
            });
            playerThread.start();
    
            // Buat klien TTS
            // Untuk menggunakan fitur kontrol instruksi, ganti model dengan qwen3-tts-instruct-flash-realtime dan hapus komentar baris instructions di TTSRealtimeClient.java
            TTSRealtimeClient client = new TTSRealtimeClient(
                URL, API_KEY, "Cherry",
                TTSRealtimeClient.SessionMode.SERVER_COMMIT,
                audioData -> {
                    playbackQueue.add(audioData);
                    audioChunks.add(audioData);
                    System.out.println("Menerima data audio: " + audioData.length + " byte");
                }
            );
    
            client.connect();
    
            // Kirim fragmen teks
            String[] textFragments = {
                "Platform model bahasa besar Alibaba Cloud, Model Studio, adalah platform all-in-one untuk mengembangkan dan membangun aplikasi model bahasa besar.",
                "Baik pengembang maupun pengguna bisnis dapat berpartisipasi secara mendalam dalam desain dan pengembangan aplikasi model bahasa besar.",
                "Anda dapat mengembangkan aplikasi model bahasa besar dalam lima menit menggunakan antarmuka sederhana,",
                "atau melatih model khusus dalam beberapa jam, sehingga Anda dapat lebih fokus pada inovasi aplikasi.",
            };
    
            System.out.println("Mulai mengirim teks...");
            for (String text : textFragments) {
                System.out.println("Mengirim fragmen: " + text);
                client.appendText(text);
                Thread.sleep(100);
            }
    
            Thread.sleep(1000);
            client.finishSession();
    
            // Tunggu hingga respons selesai
            client.waitForResponseDone();
            client.waitForSessionFinished();
            client.close();
    
            // Tunggu hingga pemutaran selesai
            playing.set(false);
            playerThread.join();
            audioLine.drain();
            audioLine.close();
    
            // Simpan file audio
            saveWav("output.wav");
            System.out.println("Selesai");
        }
    
        private static void saveWav(String filename) throws IOException {
            if (audioChunks.isEmpty()) {
                System.out.println("Tidak ada data audio untuk disimpan");
                return;
            }
            ByteArrayOutputStream bos = new ByteArrayOutputStream();
            for (byte[] chunk : audioChunks) {
                bos.write(chunk);
            }
            byte[] allAudio = bos.toByteArray();
            AudioFormat format = new AudioFormat(SAMPLE_RATE, 16, 1, true, false);
            AudioInputStream ais = new AudioInputStream(
                new ByteArrayInputStream(allAudio), format, allAudio.length / 2);
            new File("outputs").mkdirs();
            AudioSystem.write(ais, AudioFileFormat.Type.WAVE,
                new File("outputs/" + filename));
            System.out.println("Audio disimpan ke: outputs/" + filename);
        }
    }

    Kompilasi dan jalankan ServerCommit.java untuk mendengar audio yang dihasilkan oleh API Realtime secara real-time.

    commit mode

    Python

    Buat file Python bernama commit.py di direktori yang sama dengan tts_realtime_client.py, lalu salin kode berikut ke dalamnya:

    commit.py

    import os
    import asyncio
    import logging
    import wave
    from tts_realtime_client import TTSRealtimeClient, SessionMode
    import pyaudio
    
    # Konfigurasi layanan QwenTTS
    # Untuk menggunakan fitur kontrol instruksi, ganti model dengan qwen3-tts-instruct-flash-realtime dan hapus komentar baris instructions di tts_realtime_client.py
    # Berikut ini adalah URL untuk wilayah Singapura. Jika Anda menggunakan model di wilayah Beijing, ganti URL dengan: wss://dashscope.aliyuncs.com/api-ws/v1/realtime?model=qwen3-tts-flash-realtime
    URL = "wss://dashscope-intl.aliyuncs.com/api-ws/v1/realtime?model=qwen3-tts-flash-realtime"
    # Kunci API untuk wilayah Singapura dan Beijing berbeda. Dapatkan Kunci API: https://www.alibabacloud.com/help/zh/model-studio/get-api-key
    # Jika Anda belum mengonfigurasi variabel lingkungan, ganti baris berikut dengan Kunci API Model Studio Anda: API_KEY="sk-xxx"
    API_KEY = os.getenv("DASHSCOPE_API_KEY")
    
    if not API_KEY:
        raise ValueError("Harap setel variabel lingkungan DASHSCOPE_API_KEY")
    
    # Kumpulkan data audio
    _audio_chunks = []
    _AUDIO_SAMPLE_RATE = 24000
    _audio_pyaudio = pyaudio.PyAudio()
    _audio_stream = None
    
    def _audio_callback(audio_bytes: bytes):
        """Callback audio TTSRealtimeClient: pemutaran real-time dan caching"""
        global _audio_stream
        if _audio_stream is not None:
            try:
                _audio_stream.write(audio_bytes)
            except Exception as exc:
                logging.error(f"Error pemutaran PyAudio: {exc}")
        _audio_chunks.append(audio_bytes)
        logging.info(f"Menerima potongan audio: {len(audio_bytes)} byte")
    
    def _save_audio_to_file(filename: str = "output.wav", sample_rate: int = 24000) -> bool:
        """Simpan data audio yang dikumpulkan ke file WAV"""
        if not _audio_chunks:
            logging.warning("Tidak ada data audio untuk disimpan")
            return False
    
        try:
            audio_data = b"".join(_audio_chunks)
            with wave.open(filename, 'wb') as wav_file:
                wav_file.setnchannels(1)  # Mono
                wav_file.setsampwidth(2)  # 16-bit
                wav_file.setframerate(sample_rate)
                wav_file.writeframes(audio_data)
            logging.info(f"Audio disimpan ke: {filename}")
            return True
        except Exception as exc:
            logging.error(f"Gagal menyimpan audio: {exc}")
            return False
    
    async def _user_input_loop(client: TTSRealtimeClient):
        """Baca input pengguna secara terus-menerus dan kirim teks. Saat pengguna memasukkan teks kosong, kirim event commit dan akhiri sesi saat ini"""
        print("Masukkan teks (tekan Enter langsung untuk mengirim event commit dan mengakhiri sesi saat ini, tekan Ctrl+C atau Ctrl+D untuk keluar dari program):")
        
        while True:
            try:
                user_text = input("> ")
                if not user_text:  # Pengguna memasukkan teks kosong
                    # Input kosong dianggap sebagai akhir percakapan: commit buffer -> akhiri sesi -> keluar dari loop
                    logging.info("Input kosong, mengirim event commit dan mengakhiri sesi saat ini")
                    await client.commit_text_buffer()
                    # Tunggu sebentar agar server memproses commit, mencegah pengakhiran sesi prematur yang menyebabkan kehilangan audio
                    await asyncio.sleep(0.3)
                    await client.finish_session()
                    break  # Keluar dari loop input pengguna langsung, tidak perlu menekan Enter lagi
                else:
                    logging.info(f"Mengirim teks: {user_text}")
                    await client.append_text(user_text)
                    
            except EOFError:  # Pengguna menekan Ctrl+D
                break
            except KeyboardInterrupt:  # Pengguna menekan Ctrl+C
                break
        
        # Akhiri sesi
        logging.info("Mengakhiri sesi...")
    async def _run_demo():
        """Jalankan demo lengkap"""
        global _audio_stream
        # Buka stream output PyAudio
        _audio_stream = _audio_pyaudio.open(
            format=pyaudio.paInt16,
            channels=1,
            rate=_AUDIO_SAMPLE_RATE,
            output=True,
            frames_per_buffer=1024
        )
    
        client = TTSRealtimeClient(
            base_url=URL,
            api_key=API_KEY,
            voice="Cherry",
            mode=SessionMode.COMMIT,  # Diubah ke mode COMMIT
            audio_callback=_audio_callback
        )
    
        # Bentuk koneksi
        await client.connect()
    
        # Jalankan penanganan pesan dan input pengguna secara paralel
        consumer_task = asyncio.create_task(client.handle_messages())
        producer_task = asyncio.create_task(_user_input_loop(client))
    
        await producer_task  # Tunggu hingga input pengguna selesai
    
        # Tunggu response.done
        await client.wait_for_response_done()
    
        # Tutup koneksi dan batalkan tugas consumer
        await client.close()
        consumer_task.cancel()
    
        # Tutup stream audio
        if _audio_stream is not None:
            _audio_stream.stop_stream()
            _audio_stream.close()
        _audio_pyaudio.terminate()
    
        # Simpan data audio
        os.makedirs("outputs", exist_ok=True)
        _save_audio_to_file(os.path.join("outputs", "qwen_tts_output.wav"))
    
    def main():
        logging.basicConfig(
            level=logging.INFO,
            format='%(asctime)s [%(levelname)s] %(message)s',
            datefmt='%Y-%m-%d %H:%M:%S'
        )
        logging.info("Memulai demo Klien Realtime QwenTTS…")
        asyncio.run(_run_demo())
    
    if __name__ == "__main__":
        main()

    Jalankan commit.py. Anda dapat memasukkan teks untuk disintesis beberapa kali. Tekan Enter tanpa mengetik teks apa pun untuk mendengar audio yang dikembalikan oleh API Realtime melalui speaker Anda.

    Java

    Buat file Java bernama Commit.java di direktori yang sama dengan TTSRealtimeClient.java, lalu salin kode berikut ke dalamnya:

    import javax.sound.sampled.*;
    import java.io.*;
    import java.util.ArrayList;
    import java.util.List;
    import java.util.Scanner;
    import java.util.concurrent.ConcurrentLinkedQueue;
    import java.util.concurrent.atomic.AtomicBoolean;
    
    public class Commit {
        // Berikut ini adalah URL untuk wilayah Singapura. Jika Anda menggunakan model di wilayah Beijing, ganti URL dengan: wss://dashscope.aliyuncs.com/api-ws/v1/realtime?model=qwen3-tts-flash-realtime
        private static final String URL = "wss://dashscope-intl.aliyuncs.com/api-ws/v1/realtime?model=qwen3-tts-flash-realtime";
        // Kunci API untuk wilayah Singapura dan Beijing berbeda. Dapatkan Kunci API: https://www.alibabacloud.com/help/zh/model-studio/get-api-key
        // Jika Anda belum mengonfigurasi variabel lingkungan, ganti baris berikut dengan Kunci API Model Studio Anda: private static final String API_KEY = "sk-xxx";
        private static final String API_KEY = System.getenv("DASHSCOPE_API_KEY");
        private static final int SAMPLE_RATE = 24000;
    
        private static final List<byte[]> audioChunks = new ArrayList<>();
        private static final ConcurrentLinkedQueue<byte[]> playbackQueue = new ConcurrentLinkedQueue<>();
        private static final AtomicBoolean playing = new AtomicBoolean(true);
    
        public static void main(String[] args) throws Exception {
            if (API_KEY == null || API_KEY.isEmpty()) {
                throw new IllegalStateException("Harap setel variabel lingkungan DASHSCOPE_API_KEY");
            }
    
            // Inisialisasi pemutaran audio
            AudioFormat format = new AudioFormat(SAMPLE_RATE, 16, 1, true, false);
            DataLine.Info info = new DataLine.Info(SourceDataLine.class, format);
            SourceDataLine audioLine = (SourceDataLine) AudioSystem.getLine(info);
            audioLine.open(format);
            audioLine.start();
    
            // Mulai thread pemutaran
            Thread playerThread = new Thread(() -> {
                while (playing.get() || !playbackQueue.isEmpty()) {
                    byte[] chunk = playbackQueue.poll();
                    if (chunk != null) {
                        audioLine.write(chunk, 0, chunk.length);
                    } else {
                        try { Thread.sleep(10); } catch (InterruptedException ignored) {}
                    }
                }
            });
            playerThread.start();
    
            // Buat klien TTS (mode commit)
            // Untuk menggunakan fitur kontrol instruksi, ganti model dengan qwen3-tts-instruct-flash-realtime dan hapus komentar baris instructions di TTSRealtimeClient.java
            TTSRealtimeClient client = new TTSRealtimeClient(
                URL, API_KEY, "Cherry",
                TTSRealtimeClient.SessionMode.COMMIT,
                audioData -> {
                    playbackQueue.add(audioData);
                    audioChunks.add(audioData);
                    System.out.println("Menerima data audio: " + audioData.length + " byte");
                }
            );
    
            client.connect();
    
            // Input interaktif
            System.out.println("Masukkan teks (tekan Enter langsung untuk mengirim event commit dan mengakhiri sesi saat ini, tekan Ctrl+D untuk keluar dari program):");
            Scanner scanner = new Scanner(System.in);
            while (true) {
                System.out.print("> ");
                if (!scanner.hasNextLine()) {
                    client.finishSession();
                    break;
                }
                String userText = scanner.nextLine();
                if (userText.isEmpty()) {
                    // Input kosong: commit buffer dan akhiri sesi
                    System.out.println("Input kosong, mengirim event commit dan mengakhiri sesi saat ini");
                    client.commitTextBuffer();
                    Thread.sleep(300);
                    client.finishSession();
                    break;
                } else {
                    System.out.println("Mengirim teks: " + userText);
                    client.appendText(userText);
                }
            }
            scanner.close();
    
            // Tunggu hingga respons selesai
            client.waitForResponseDone();
            client.waitForSessionFinished();
            client.close();
    
            // Tunggu hingga pemutaran selesai
            playing.set(false);
            playerThread.join();
            audioLine.drain();
            audioLine.close();
    
            // Simpan file audio
            saveWav("output.wav");
            System.out.println("Selesai");
        }
    
        private static void saveWav(String filename) throws IOException {
            if (audioChunks.isEmpty()) {
                System.out.println("Tidak ada data audio untuk disimpan");
                return;
            }
            ByteArrayOutputStream bos = new ByteArrayOutputStream();
            for (byte[] chunk : audioChunks) {
                bos.write(chunk);
            }
            byte[] allAudio = bos.toByteArray();
            AudioFormat format = new AudioFormat(SAMPLE_RATE, 16, 1, true, false);
            AudioInputStream ais = new AudioInputStream(
                new ByteArrayInputStream(allAudio), format, allAudio.length / 2);
            new File("outputs").mkdirs();
            AudioSystem.write(ais, AudioFileFormat.Type.WAVE,
                new File("outputs/" + filename));
            System.out.println("Audio disimpan ke: outputs/" + filename);
        }
    }

    Kompilasi dan jalankan Commit.java. Masukkan teks untuk disintesis beberapa kali, lalu tekan Enter tanpa teks untuk memutar audio melalui speaker Anda.

Sintesis suara dengan suara yang dikloning

Kloning suara tidak menyediakan audio pratinjau. Gunakan API sintesis suara untuk mengevaluasi output. Mulailah dengan teks pendek untuk pengujian awal.

Contoh berikut mensintesis suara menggunakan suara yang dikloning dari sampel audio. Contoh ini memperluas kode mode server_commit SDK DashScope dari tab suara sistem, dengan voice diatur ke suara yang dikloning.

  • Prinsip utama: Model kloning suara (target_model) harus sesuai dengan model sintesis suara (model). Ketidakcocokan menyebabkan sintesis gagal.

  • Contoh ini menggunakan file audio lokal voice.mp3 untuk kloning suara. Gantilah dengan file audio Anda sendiri saat menjalankan kode.

Python

# coding=utf-8
# Petunjuk instalasi pyaudio:
# APPLE Mac OS X
#   brew install portaudio
#   pip install pyaudio
# Debian/Ubuntu
#   sudo apt-get install python-pyaudio python3-pyaudio
#   atau
#   pip install pyaudio
# CentOS
#   sudo yum install -y portaudio portaudio-devel && pip install pyaudio
# Microsoft Windows
#   python -m pip install pyaudio

import pyaudio
import os
import requests
import base64
import pathlib
import threading
import time
import dashscope  # Versi SDK Python DashScope harus 1.23.9 atau lebih baru
from dashscope.audio.qwen_tts_realtime import QwenTtsRealtime, QwenTtsRealtimeCallback, AudioFormat

# ======= Konfigurasi konstan =======
DEFAULT_TARGET_MODEL = "qwen3-tts-vc-realtime-2026-01-15"  # Kloning suara dan sintesis suara harus menggunakan model yang sama
DEFAULT_PREFERRED_NAME = "guanyu"
DEFAULT_AUDIO_MIME_TYPE = "audio/mpeg"
VOICE_FILE_PATH = "voice.mp3"  # Jalur relatif ke file audio lokal yang digunakan untuk kloning suara

TEXT_TO_SYNTHESIZE = [
    'Right? I love supermarkets like this.',
    'Especially during Chinese New Year',
    'When I go shopping',
    'I feel',
    'Extremely happy!',
    'And want to buy so many things!'
]

def create_voice(file_path: str,
                 target_model: str = DEFAULT_TARGET_MODEL,
                 preferred_name: str = DEFAULT_PREFERRED_NAME,
                 audio_mime_type: str = DEFAULT_AUDIO_MIME_TYPE) -> str:
    """
    Buat suara dan kembalikan parameter suara
    """
    # Kunci API untuk wilayah Singapura dan Beijing berbeda. Dapatkan Kunci API: https://www.alibabacloud.com/help/zh/model-studio/get-api-key
    # Jika Anda belum mengonfigurasi variabel lingkungan, ganti baris berikut dengan Kunci API Model Studio Anda: api_key = "sk-xxx"
    api_key = os.getenv("DASHSCOPE_API_KEY")

    file_path_obj = pathlib.Path(file_path)
    if not file_path_obj.exists():
        raise FileNotFoundError(f"File audio tidak ditemukan: {file_path}")

    base64_str = base64.b64encode(file_path_obj.read_bytes()).decode()
    data_uri = f"data:{audio_mime_type};base64,{base64_str}"

    # Berikut ini adalah URL untuk wilayah Singapura. Jika Anda menggunakan model di wilayah Beijing, ganti URL dengan: https://dashscope.aliyuncs.com/api/v1/services/audio/tts/customization
    url = "https://dashscope-intl.aliyuncs.com/api/v1/services/audio/tts/customization"
    payload = {
        "model": "qwen-voice-enrollment", # Jangan ubah nilai ini
        "input": {
            "action": "create",
            "target_model": target_model,
            "preferred_name": preferred_name,
            "audio": {"data": data_uri}
        }
    }
    headers = {
        "Authorization": f"Bearer {api_key}",
        "Content-Type": "application/json"
    }

    resp = requests.post(url, json=payload, headers=headers)
    if resp.status_code != 200:
        raise RuntimeError(f"Gagal membuat suara: {resp.status_code}, {resp.text}")

    try:
        return resp.json()["output"]["voice"]
    except (KeyError, ValueError) as e:
        raise RuntimeError(f"Gagal mengurai respons suara: {e}")

def init_dashscope_api_key():
    """
    Inisialisasi Kunci API SDK DashScope
    """
    # Kunci API untuk wilayah Singapura dan Beijing berbeda. Dapatkan Kunci API: https://www.alibabacloud.com/help/zh/model-studio/get-api-key
    # Jika Anda belum mengonfigurasi variabel lingkungan, ganti baris berikut dengan Kunci API Model Studio Anda: dashscope.api_key = "sk-xxx"
    dashscope.api_key = os.getenv("DASHSCOPE_API_KEY")

# ======= Kelas callback =======
class MyCallback(QwenTtsRealtimeCallback):
    """
    Callback streaming TTS kustom
    """
    def __init__(self):
        self.complete_event = threading.Event()
        self._player = pyaudio.PyAudio()
        self._stream = self._player.open(
            format=pyaudio.paInt16, channels=1, rate=24000, output=True
        )

    def on_open(self) -> None:
        print('[TTS] Koneksi dibentuk')

    def on_close(self, close_status_code, close_msg) -> None:
        self._stream.stop_stream()
        self._stream.close()
        self._player.terminate()
        print(f'[TTS] Koneksi ditutup kode={close_status_code}, pesan={close_msg}')

    def on_event(self, response: dict) -> None:
        try:
            event_type = response.get('type', '')
            if event_type == 'session.created':
                print(f'[TTS] Sesi dimulai: {response["session"]["id"]}')
            elif event_type == 'response.audio.delta':
                audio_data = base64.b64decode(response['delta'])
                self._stream.write(audio_data)
            elif event_type == 'response.done':
                print(f'[TTS] Respons selesai, ID Respons: {qwen_tts_realtime.get_last_response_id()}')
            elif event_type == 'session.finished':
                print('[TTS] Sesi berakhir')
                self.complete_event.set()
        except Exception as e:
            print(f'[Error] Pengecualian menangani event callback: {e}')

    def wait_for_finished(self):
        self.complete_event.wait()

# ======= Logika eksekusi utama =======
if __name__ == '__main__':
    init_dashscope_api_key()
    print('[Sistem] Menginisialisasi Qwen TTS Realtime ...')

    callback = MyCallback()
    qwen_tts_realtime = QwenTtsRealtime(
        model=DEFAULT_TARGET_MODEL,
        callback=callback,
        # Berikut ini adalah URL untuk wilayah Singapura. Jika Anda menggunakan model di wilayah Beijing, ganti URL dengan: wss://dashscope.aliyuncs.com/api-ws/v1/realtime
        url='wss://dashscope-intl.aliyuncs.com/api-ws/v1/realtime'
    )
    qwen_tts_realtime.connect()
    
    qwen_tts_realtime.update_session(
        voice=create_voice(VOICE_FILE_PATH), # Ganti parameter suara dengan suara kustom yang dihasilkan oleh kloning suara
        response_format=AudioFormat.PCM_24000HZ_MONO_16BIT,
        mode='server_commit'
    )

    for text_chunk in TEXT_TO_SYNTHESIZE:
        print(f'[Mengirim teks]: {text_chunk}')
        qwen_tts_realtime.append_text(text_chunk)
        time.sleep(0.1)

    qwen_tts_realtime.finish()
    callback.wait_for_finished()

    print(f'[Metric] session_id={qwen_tts_realtime.get_session_id()}, '
          f'first_audio_delay={qwen_tts_realtime.get_first_audio_delay()}s')

Java

Impor dependensi Gson. Jika Anda menggunakan Maven atau Gradle, tambahkan dependensi sebagai berikut:

Maven

Tambahkan berikut ke pom.xml:

<!-- https://mvnrepository.com/artifact/com.google.code.gson/gson -->
<dependency>
    <groupId>com.google.code.gson</groupId>
    <artifactId>gson</artifactId>
    <version>2.13.1</version>
</dependency>

Gradle

Tambahkan berikut ke build.gradle:

// https://mvnrepository.com/artifact/com.google.code.gson/gson
implementation("com.google.code.gson:gson:2.13.1")
import com.alibaba.dashscope.audio.qwen_tts_realtime.*;
import com.alibaba.dashscope.exception.NoApiKeyException;
import com.google.gson.Gson;
import com.google.gson.JsonObject;

import javax.sound.sampled.*;
import java.io.*;
import java.net.HttpURLConnection;
import java.net.URL;
import java.nio.file.*;
import java.nio.charset.StandardCharsets;
import java.util.Base64;
import java.util.Queue;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicReference;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.atomic.AtomicBoolean;

public class Main {
    // ===== Definisi konstanta =====
    // Kloning suara dan sintesis suara harus menggunakan model yang sama
    private static final String TARGET_MODEL = "qwen3-tts-vc-realtime-2026-01-15";
    private static final String PREFERRED_NAME = "guanyu";
    // Jalur relatif ke file audio lokal yang digunakan untuk kloning suara
    private static final String AUDIO_FILE = "voice.mp3";
    private static final String AUDIO_MIME_TYPE = "audio/mpeg";
    private static String[] textToSynthesize = {
            "Right? I love supermarkets like this.",
            "Especially during Chinese New Year",
            "When I go shopping",
            "I feel",
            "Extremely happy!",
            "And want to buy so many things!"
    };

    // Hasilkan URI data
    public static String toDataUrl(String filePath) throws IOException {
        byte[] bytes = Files.readAllBytes(Paths.get(filePath));
        String encoded = Base64.getEncoder().encodeToString(bytes);
        return "data:" + AUDIO_MIME_TYPE + ";base64," + encoded;
    }

    // Panggil API untuk membuat suara
    public static String createVoice() throws Exception {
        // Kunci API untuk wilayah Singapura dan Beijing berbeda. Dapatkan Kunci API: https://www.alibabacloud.com/help/zh/model-studio/get-api-key
        // Jika Anda belum mengonfigurasi variabel lingkungan, ganti baris berikut dengan Kunci API Model Studio Anda: String apiKey = "sk-xxx"
        String apiKey = System.getenv("DASHSCOPE_API_KEY");

        String jsonPayload =
                "{"
                        + "\"model\": \"qwen-voice-enrollment\"," // Jangan ubah nilai ini
                        + "\"input\": {"
                        +     "\"action\": \"create\","
                        +     "\"target_model\": \"" + TARGET_MODEL + "\","
                        +     "\"preferred_name\": \"" + PREFERRED_NAME + "\","
                        +     "\"audio\": {"
                        +         "\"data\": \"" + toDataUrl(AUDIO_FILE) + "\""
                        +     "}"
                        + "}"
                        + "}";

        HttpURLConnection con = (HttpURLConnection) new URL("https://dashscope.aliyuncs.com/api/v1/services/audio/tts/customization").openConnection();
        con.setRequestMethod("POST");
        con.setRequestProperty("Authorization", "Bearer " + apiKey);
        con.setRequestProperty("Content-Type", "application/json");
        con.setDoOutput(true);

        try (OutputStream os = con.getOutputStream()) {
            os.write(jsonPayload.getBytes(StandardCharsets.UTF_8));
        }

        int status = con.getResponseCode();
        System.out.println("Kode status HTTP: " + status);

        try (BufferedReader br = new BufferedReader(
                new InputStreamReader(status >= 200 && status < 300 ? con.getInputStream() : con.getErrorStream(),
                        StandardCharsets.UTF_8))) {
            StringBuilder response = new StringBuilder();
            String line;
            while ((line = br.readLine()) != null) {
                response.append(line);
            }
            System.out.println("Isi respons: " + response);

            if (status == 200) {
                JsonObject jsonObj = new Gson().fromJson(response.toString(), JsonObject.class);
                return jsonObj.getAsJsonObject("output").get("voice").getAsString();
            }
            throw new IOException("Gagal membuat suara: " + status + " - " + response);
        }
    }

    // Kelas pemutar audio PCM real-time
    public static class RealtimePcmPlayer {
        private int sampleRate;
        private SourceDataLine line;
        private AudioFormat audioFormat;
        private Thread decoderThread;
        private Thread playerThread;
        private AtomicBoolean stopped = new AtomicBoolean(false);
        private Queue<String> b64AudioBuffer = new ConcurrentLinkedQueue<>();
        private Queue<byte[]> RawAudioBuffer = new ConcurrentLinkedQueue<>();

        // Konstruktor: inisialisasi format audio dan saluran audio
        public RealtimePcmPlayer(int sampleRate) throws LineUnavailableException {
            this.sampleRate = sampleRate;
            this.audioFormat = new AudioFormat(this.sampleRate, 16, 1, true, false);
            DataLine.Info info = new DataLine.Info(SourceDataLine.class, audioFormat);
            line = (SourceDataLine) AudioSystem.getLine(info);
            line.open(audioFormat);
            line.start();
            decoderThread = new Thread(new Runnable() {
                @Override
                public void run() {
                    while (!stopped.get()) {
                        String b64Audio = b64AudioBuffer.poll();
                        if (b64Audio != null) {
                            byte[] rawAudio = Base64.getDecoder().decode(b64Audio);
                            RawAudioBuffer.add(rawAudio);
                        } else {
                            try {
                                Thread.sleep(100);
                            } catch (InterruptedException e) {
                                throw new RuntimeException(e);
                            }
                        }
                    }
                }
            });
            playerThread = new Thread(new Runnable() {
                @Override
                public void run() {
                    while (!stopped.get()) {
                        byte[] rawAudio = RawAudioBuffer.poll();
                        if (rawAudio != null) {
                            try {
                                playChunk(rawAudio);
                            } catch (IOException e) {
                                throw new RuntimeException(e);
                            } catch (InterruptedException e) {
                                throw new RuntimeException(e);
                            }
                        } else {
                            try {
                                Thread.sleep(100);
                            } catch (InterruptedException e) {
                                throw new RuntimeException(e);
                            }
                        }
                    }
                }
            });
            decoderThread.start();
            playerThread.start();
        }

        // Putar potongan audio dan blokir hingga pemutaran selesai
        private void playChunk(byte[] chunk) throws IOException, InterruptedException {
            if (chunk == null || chunk.length == 0) return;

            int bytesWritten = 0;
            while (bytesWritten < chunk.length) {
                bytesWritten += line.write(chunk, bytesWritten, chunk.length - bytesWritten);
            }
            int audioLength = chunk.length / (this.sampleRate*2/1000);
            // Tunggu hingga audio dalam buffer selesai diputar
            Thread.sleep(audioLength - 10);
        }

        public void write(String b64Audio) {
            b64AudioBuffer.add(b64Audio);
        }

        public void cancel() {
            b64AudioBuffer.clear();
            RawAudioBuffer.clear();
        }

        public void waitForComplete() throws InterruptedException {
            while (!b64AudioBuffer.isEmpty() || !RawAudioBuffer.isEmpty()) {
                Thread.sleep(100);
            }
            line.drain();
        }

        public void shutdown() throws InterruptedException {
            stopped.set(true);
            decoderThread.join();
            playerThread.join();
            if (line != null && line.isRunning()) {
                line.drain();
                line.close();
            }
        }
    }

    public static void main(String[] args) throws Exception {
        QwenTtsRealtimeParam param = QwenTtsRealtimeParam.builder()
                .model(TARGET_MODEL)
                // Berikut ini adalah URL untuk wilayah Singapura. Jika Anda menggunakan model di wilayah Beijing, ganti URL dengan: wss://dashscope.aliyuncs.com/api-ws/v1/realtime
                .url("wss://dashscope-intl.aliyuncs.com/api-ws/v1/realtime")
                // Kunci API untuk wilayah Singapura dan Beijing berbeda. Dapatkan Kunci API: https://www.alibabacloud.com/help/zh/model-studio/get-api-key
                // Jika Anda belum mengonfigurasi variabel lingkungan, ganti baris berikut dengan Kunci API Model Studio Anda: .apikey("sk-xxx")
                .apikey(System.getenv("DASHSCOPE_API_KEY"))
                .build();
        AtomicReference<CountDownLatch> completeLatch = new AtomicReference<>(new CountDownLatch(1));
        final AtomicReference<QwenTtsRealtime> qwenTtsRef = new AtomicReference<>(null);

        // Buat instance pemutar audio real-time
        RealtimePcmPlayer audioPlayer = new RealtimePcmPlayer(24000);

        QwenTtsRealtime qwenTtsRealtime = new QwenTtsRealtime(param, new QwenTtsRealtimeCallback() {
            @Override
            public void onOpen() {
                // Tangani pembentukan koneksi
            }
            @Override
            public void onEvent(JsonObject message) {
                String type = message.get("type").getAsString();
                switch(type) {
                    case "session.created":
                        // Tangani pembuatan sesi
                        break;
                    case "response.audio.delta":
                        String recvAudioB64 = message.get("delta").getAsString();
                        // Putar audio secara real-time
                        audioPlayer.write(recvAudioB64);
                        break;
                    case "response.done":
                        // Tangani penyelesaian respons
                        break;
                    case "session.finished":
                        // Tangani penghentian sesi
                        completeLatch.get().countDown();
                    default:
                        break;
                }
            }
            @Override
            public void onClose(int code, String reason) {
                // Tangani penutupan koneksi
            }
        });
        qwenTtsRef.set(qwenTtsRealtime);
        try {
            qwenTtsRealtime.connect();
        } catch (NoApiKeyException e) {
            throw new RuntimeException(e);
        }
        QwenTtsRealtimeConfig config = QwenTtsRealtimeConfig.builder()
                .voice(createVoice()) // Ganti parameter suara dengan suara kustom yang dihasilkan oleh kloning suara
                .responseFormat(QwenTtsRealtimeAudioFormat.PCM_24000HZ_MONO_16BIT)
                .mode("server_commit")
                .build();
        qwenTtsRealtime.updateSession(config);
        for (String text:textToSynthesize) {
            qwenTtsRealtime.appendText(text);
            Thread.sleep(100);
        }
        qwenTtsRealtime.finish();
        completeLatch.get().await();

        // Tunggu hingga pemutaran audio selesai dan matikan pemutar
        audioPlayer.waitForComplete();
        audioPlayer.shutdown();
        System.exit(0);
    }
}

Sintesis suara dengan suara yang dirancang

Fitur desain suara menghasilkan pratinjau audio. Dengarkan pratinjau tersebut dan pastikan outputnya sesuai harapan sebelum melakukan sintesis suara—langkah ini membantu menghindari panggilan API yang tidak perlu.

  1. Buat suara kustom dan dengarkan pratinjaunya. Jika hasilnya memuaskan, lanjutkan. Jika tidak, buat ulang suara tersebut.

    Python

    import requests
    import base64
    import os
    
    def create_voice_and_play():
        # Kunci API berbeda antara wilayah Singapura dan Beijing. Dapatkan Kunci API: https://www.alibabacloud.com/help/zh/model-studio/get-api-key
        # Jika variabel lingkungan tidak disetel, ganti baris berikut dengan Kunci API Model Studio Anda: api_key = "sk-xxx"
        api_key = os.getenv("DASHSCOPE_API_KEY")
        
        if not api_key:
            print("Error: Variabel lingkungan DASHSCOPE_API_KEY tidak ditemukan. Harap setel Kunci API terlebih dahulu.")
            return None, None, None
        
        # Siapkan data permintaan
        headers = {
            "Authorization": f"Bearer {api_key}",
            "Content-Type": "application/json"
        }
        
        data = {
            "model": "qwen-voice-design",
            "input": {
                "action": "create",
                "target_model": "qwen3-tts-vd-realtime-2026-01-15",
                "voice_prompt": "A composed middle-aged male announcer with a deep, rich and magnetic voice, a steady speaking speed and clear articulation, is suitable for news broadcasting or documentary commentary.",
                "preview_text": "Dear listeners, hello everyone. Welcome to the evening news.",
                "preferred_name": "announcer",
                "language": "en"
            },
            "parameters": {
                "sample_rate": 24000,
                "response_format": "wav"
            }
        }
        
        # Berikut ini adalah URL untuk wilayah Singapura. Jika Anda menggunakan model di wilayah Beijing, ganti URL dengan: https://dashscope.aliyuncs.com/api/v1/services/audio/tts/customization
        url = "https://dashscope-intl.aliyuncs.com/api/v1/services/audio/tts/customization"
        
        try:
            # Kirim permintaan
            response = requests.post(
                url,
                headers=headers,
                json=data,
                timeout=60  # Tambahkan pengaturan timeout
            )
            
            if response.status_code == 200:
                result = response.json()
                
                # Dapatkan nama suara
                voice_name = result["output"]["voice"]
                print(f"Nama suara: {voice_name}")
                
                # Dapatkan data audio pratinjau
                base64_audio = result["output"]["preview_audio"]["data"]
                
                # Dekode data audio Base64
                audio_bytes = base64.b64decode(base64_audio)
                
                # Simpan file audio secara lokal
                filename = f"{voice_name}_preview.wav"
                
                # Tulis data audio ke file lokal
                with open(filename, 'wb') as f:
                    f.write(audio_bytes)
                
                print(f"Audio disimpan ke file lokal: {filename}")
                print(f"Jalur file: {os.path.abspath(filename)}")
                
                return voice_name, audio_bytes, filename
            else:
                print(f"Permintaan gagal dengan kode status: {response.status_code}")
                print(f"Isi respons: {response.text}")
                return None, None, None
                
        except requests.exceptions.RequestException as e:
            print(f"Terjadi error permintaan jaringan: {e}")
            return None, None, None
        except KeyError as e:
            print(f"Error format data respons, bidang yang diperlukan tidak ada: {e}")
            print(f"Isi respons: {response.text if 'response' in locals() else 'Tidak ada respons'}")
            return None, None, None
        except Exception as e:
            print(f"Terjadi error tidak dikenal: {e}")
            return None, None, None
    
    if __name__ == "__main__":
        print("Mulai membuat suara...")
        voice_name, audio_data, saved_filename = create_voice_and_play()
        
        if voice_name:
            print(f"\nBerhasil membuat suara '{voice_name}'")
            print(f"File audio disimpan sebagai: '{saved_filename}'")
            print(f"Ukuran file: {os.path.getsize(saved_filename)} byte")
        else:
            print("\nPembuatan suara gagal")

    Java

    Tambahkan dependensi Gson ke proyek Anda:

    Maven

    Tambahkan berikut ke pom.xml:

    <!-- https://mvnrepository.com/artifact/com.google.code.gson/gson -->
    <dependency>
        <groupId>com.google.code.gson</groupId>
        <artifactId>gson</artifactId>
        <version>2.13.1</version>
    </dependency>

    Gradle

    Tambahkan berikut ke build.gradle:

    // https://mvnrepository.com/artifact/com.google.code.gson/gson
    implementation("com.google.code.gson:gson:2.13.1")
    import com.google.gson.JsonObject;
    import com.google.gson.JsonParser;
    import java.io.*;
    import java.net.HttpURLConnection;
    import java.net.URL;
    import java.util.Base64;
    
    public class Main {
        public static void main(String[] args) {
            Main example = new Main();
            example.createVoice();
        }
    
        public void createVoice() {
            // Kunci API berbeda antara wilayah Singapura dan Beijing. Dapatkan Kunci API: https://www.alibabacloud.com/help/zh/model-studio/get-api-key
            // Jika variabel lingkungan tidak disetel, ganti baris berikut dengan Kunci API Model Studio Anda: String apiKey = "sk-xxx"
            String apiKey = System.getenv("DASHSCOPE_API_KEY");
    
            // Buat string body permintaan JSON
            String jsonBody = "{\n" +
                    "    \"model\": \"qwen-voice-design\",\n" +
                    "    \"input\": {\n" +
                    "        \"action\": \"create\",\n" +
                    "        \"target_model\": \"qwen3-tts-vd-realtime-2026-01-15\",\n" +
                    "        \"voice_prompt\": \"A composed middle-aged male announcer with a deep, rich and magnetic voice, a steady speaking speed and clear articulation, is suitable for news broadcasting or documentary commentary.\",\n" +
                    "        \"preview_text\": \"Dear listeners, hello everyone. Welcome to the evening news.\",\n" +
                    "        \"preferred_name\": \"announcer\",\n" +
                    "        \"language\": \"en\"\n" +
                    "    },\n" +
                    "    \"parameters\": {\n" +
                    "        \"sample_rate\": 24000,\n" +
                    "        \"response_format\": \"wav\"\n" +
                    "    }\n" +
                    "}";
    
            HttpURLConnection connection = null;
            try {
                // Berikut ini adalah URL untuk wilayah Singapura. Jika Anda menggunakan model di wilayah Beijing, ganti URL dengan: https://dashscope.aliyuncs.com/api/v1/services/audio/tts/customization
                URL url = new URL("https://dashscope-intl.aliyuncs.com/api/v1/services/audio/tts/customization");
                connection = (HttpURLConnection) url.openConnection();
    
                // Atur metode permintaan dan header
                connection.setRequestMethod("POST");
                connection.setRequestProperty("Authorization", "Bearer " + apiKey);
                connection.setRequestProperty("Content-Type", "application/json");
                connection.setDoOutput(true);
                connection.setDoInput(true);
    
                // Kirim body permintaan
                try (OutputStream os = connection.getOutputStream()) {
                    byte[] input = jsonBody.getBytes("UTF-8");
                    os.write(input, 0, input.length);
                    os.flush();
                }
    
                // Dapatkan respons
                int responseCode = connection.getResponseCode();
                if (responseCode == HttpURLConnection.HTTP_OK) {
                    // Baca isi respons
                    StringBuilder response = new StringBuilder();
                    try (BufferedReader br = new BufferedReader(
                            new InputStreamReader(connection.getInputStream(), "UTF-8"))) {
                        String responseLine;
                        while ((responseLine = br.readLine()) != null) {
                            response.append(responseLine.trim());
                        }
                    }
    
                    // Uraikan respons JSON
                    JsonObject jsonResponse = JsonParser.parseString(response.toString()).getAsJsonObject();
                    JsonObject outputObj = jsonResponse.getAsJsonObject("output");
                    JsonObject previewAudioObj = outputObj.getAsJsonObject("preview_audio");
    
                    // Dapatkan nama suara
                    String voiceName = outputObj.get("voice").getAsString();
                    System.out.println("Nama suara: " + voiceName);
    
                    // Dapatkan data audio yang dienkripsi Base64
                    String base64Audio = previewAudioObj.get("data").getAsString();
    
                    // Dekode data audio Base64
                    byte[] audioBytes = Base64.getDecoder().decode(base64Audio);
    
                    // Simpan audio ke file lokal
                    String filename = voiceName + "_preview.wav";
                    saveAudioToFile(audioBytes, filename);
    
                    System.out.println("Audio disimpan ke file lokal: " + filename);
    
                } else {
                    // Baca respons error
                    StringBuilder errorResponse = new StringBuilder();
                    try (BufferedReader br = new BufferedReader(
                            new InputStreamReader(connection.getErrorStream(), "UTF-8"))) {
                        String responseLine;
                        while ((responseLine = br.readLine()) != null) {
                            errorResponse.append(responseLine.trim());
                        }
                    }
    
                    System.out.println("Permintaan gagal dengan kode status: " + responseCode);
                    System.out.println("Respons error: " + errorResponse.toString());
                }
    
            } catch (Exception e) {
                System.err.println("Terjadi error selama permintaan: " + e.getMessage());
                e.printStackTrace();
            } finally {
                if (connection != null) {
                    connection.disconnect();
                }
            }
        }
    
        private void saveAudioToFile(byte[] audioBytes, String filename) {
            try {
                File file = new File(filename);
                try (FileOutputStream fos = new FileOutputStream(file)) {
                    fos.write(audioBytes);
                }
                System.out.println("Audio disimpan ke: " + file.getAbsolutePath());
            } catch (IOException e) {
                System.err.println("Terjadi error saat menyimpan file audio: " + e.getMessage());
                e.printStackTrace();
            }
        }
    }
  2. Gunakan suara kustom yang dibuat pada langkah sebelumnya untuk sintesis suara.

    Contoh ini mengikuti kode contoh mode "server commit" untuk suara sistem di SDK DashScope. Ganti parameter voice dengan suara kustom yang dihasilkan melalui desain suara.

    Prinsip utama: Model yang digunakan untuk desain suara (target_model) harus sama dengan model yang digunakan untuk sintesis suara selanjutnya (model). Jika tidak, sintesis akan gagal.

    Python

    # coding=utf-8
    # Instruksi instalasi untuk pyaudio:
    # APPLE Mac OS X
    #   brew install portaudio
    #   pip install pyaudio
    # Debian/Ubuntu
    #   sudo apt-get install python-pyaudio python3-pyaudio
    #   atau
    #   pip install pyaudio
    # CentOS
    #   sudo yum install -y portaudio portaudio-devel && pip install pyaudio
    # Microsoft Windows
    #   python -m pip install pyaudio
    
    import pyaudio
    import os
    import base64
    import threading
    import time
    import dashscope  # Versi DashScope Python SDK harus 1.23.9 atau yang lebih baru
    from dashscope.audio.qwen_tts_realtime import QwenTtsRealtime, QwenTtsRealtimeCallback, AudioFormat
    
    # ======= Konfigurasi konstan =======
    TEXT_TO_SYNTHESIZE = [
        'Benar kan? Saya sangat suka supermarket seperti ini,',
        'terutama saat Tahun Baru.',
        'Pergi ke supermarket',
        'benar-benar membuat saya merasa',
        'sangat, sangat bahagia!',
        'Saya ingin membeli banyak sekali barang!'
    ]
    
    def init_dashscope_api_key():
        """
        Inisialisasi kunci API untuk SDK DashScope.
        """
        # Kunci API berbeda antara wilayah Singapura dan Beijing. Dapatkan kunci API: https://www.alibabacloud.com/help/zh/model-studio/get-api-key
        # Jika variabel lingkungan tidak diatur, ganti baris berikut dengan kunci API Model Studio Anda: dashscope.api_key = "sk-xxx"
        dashscope.api_key = os.getenv("DASHSCOPE_API_KEY")
    
    # ======= Kelas callback =======
    class MyCallback(QwenTtsRealtimeCallback):
        """
        Callback streaming TTS kustom.
        """
        def __init__(self):
            self.complete_event = threading.Event()
            self._player = pyaudio.PyAudio()
            self._stream = self._player.open(
                format=pyaudio.paInt16, channels=1, rate=24000, output=True
            )
    
        def on_open(self) -> None:
            print('[TTS] Koneksi berhasil dibuat')
    
        def on_close(self, close_status_code, close_msg) -> None:
            self._stream.stop_stream()
            self._stream.close()
            self._player.terminate()
            print(f'[TTS] Koneksi ditutup, kode={close_status_code}, pesan={close_msg}')
    
        def on_event(self, response: dict) -> None:
            try:
                event_type = response.get('type', '')
                if event_type == 'session.created':
                    print(f'[TTS] Sesi dimulai: {response["session"]["id"]}')
                elif event_type == 'response.audio.delta':
                    audio_data = base64.b64decode(response['delta'])
                    self._stream.write(audio_data)
                elif event_type == 'response.done':
                    print(f'[TTS] Tanggapan selesai, ID Tanggapan: {qwen_tts_realtime.get_last_response_id()}')
                elif event_type == 'session.finished':
                    print('[TTS] Sesi selesai')
                    self.complete_event.set()
            except Exception as e:
                print(f'[Error] Pengecualian saat memproses event callback: {e}')
    
        def wait_for_finished(self):
            self.complete_event.wait()
    
    # ======= Logika eksekusi utama =======
    if __name__ == '__main__':
        init_dashscope_api_key()
        print('[Sistem] Menginisialisasi Qwen TTS Realtime ...')
    
        callback = MyCallback()
        qwen_tts_realtime = QwenTtsRealtime(
            # Gunakan model yang sama untuk desain suara dan sintesis suara
            model="qwen3-tts-vd-realtime-2026-01-15",
            callback=callback,
            # Berikut ini adalah URL untuk wilayah Singapura. Jika Anda menggunakan model di wilayah Beijing, ganti URL dengan: wss://dashscope.aliyuncs.com/api-ws/v1/realtime
            url='wss://dashscope-intl.aliyuncs.com/api-ws/v1/realtime'
        )
        qwen_tts_realtime.connect()
        
        qwen_tts_realtime.update_session(
            voice="myvoice", # Ganti parameter voice dengan suara kustom yang dihasilkan oleh desain suara
            response_format=AudioFormat.PCM_24000HZ_MONO_16BIT,
            mode='server_commit'
        )
    
        for text_chunk in TEXT_TO_SYNTHESIZE:
            print(f'[Mengirim teks]: {text_chunk}')
            qwen_tts_realtime.append_text(text_chunk)
            time.sleep(0.1)
    
        qwen_tts_realtime.finish()
        callback.wait_for_finished()
    
        print(f'[Metrik] session_id={qwen_tts_realtime.get_session_id()}, '
              f'first_audio_delay={qwen_tts_realtime.get_first_audio_delay()}s')

    Java

    import com.alibaba.dashscope.audio.qwen_tts_realtime.*;
    import com.alibaba.dashscope.exception.NoApiKeyException;
    import com.google.gson.JsonObject;
    
    import javax.sound.sampled.*;
    import java.io.*;
    import java.util.Base64;
    import java.util.Queue;
    import java.util.concurrent.CountDownLatch;
    import java.util.concurrent.atomic.AtomicReference;
    import java.util.concurrent.ConcurrentLinkedQueue;
    import java.util.concurrent.atomic.AtomicBoolean;
    
    public class Main {
        // ===== Definisi konstanta =====
        private static String[] textToSynthesize = {
                "Right? I really like this kind of supermarket,",
                "especially during the New Year.",
                "Going to the supermarket",
                "just makes me feel",
                "super, super happy!",
                "I want to buy so many things!"
        };
    
        // Kelas pemutar audio real-time
        public static class RealtimePcmPlayer {
            private int sampleRate;
            private SourceDataLine line;
            private AudioFormat audioFormat;
            private Thread decoderThread;
            private Thread playerThread;
            private AtomicBoolean stopped = new AtomicBoolean(false);
            private Queue<String> b64AudioBuffer = new ConcurrentLinkedQueue<>();
            private Queue<byte[]> RawAudioBuffer = new ConcurrentLinkedQueue<>();
    
            // Konstruktor menginisialisasi format audio dan saluran audio
            public RealtimePcmPlayer(int sampleRate) throws LineUnavailableException {
                this.sampleRate = sampleRate;
                this.audioFormat = new AudioFormat(this.sampleRate, 16, 1, true, false);
                DataLine.Info info = new DataLine.Info(SourceDataLine.class, audioFormat);
                line = (SourceDataLine) AudioSystem.getLine(info);
                line.open(audioFormat);
                line.start();
                decoderThread = new Thread(new Runnable() {
                    @Override
                    public void run() {
                        while (!stopped.get()) {
                            String b64Audio = b64AudioBuffer.poll();
                            if (b64Audio != null) {
                                byte[] rawAudio = Base64.getDecoder().decode(b64Audio);
                                RawAudioBuffer.add(rawAudio);
                            } else {
                                try {
                                    Thread.sleep(100);
                                } catch (InterruptedException e) {
                                    throw new RuntimeException(e);
                                }
                            }
                        }
                    }
                });
                playerThread = new Thread(new Runnable() {
                    @Override
                    public void run() {
                        while (!stopped.get()) {
                            byte[] rawAudio = RawAudioBuffer.poll();
                            if (rawAudio != null) {
                                try {
                                    playChunk(rawAudio);
                                } catch (IOException e) {
                                    throw new RuntimeException(e);
                                } catch (InterruptedException e) {
                                    throw new RuntimeException(e);
                                }
                            } else {
                                try {
                                    Thread.sleep(100);
                                } catch (InterruptedException e) {
                                    throw new RuntimeException(e);
                                }
                            }
                        }
                    }
                });
                decoderThread.start();
                playerThread.start();
            }
    
            // Memutar potongan audio dan memblokir hingga pemutaran selesai
            private void playChunk(byte[] chunk) throws IOException, InterruptedException {
                if (chunk == null || chunk.length == 0) return;
    
                int bytesWritten = 0;
                while (bytesWritten < chunk.length) {
                    bytesWritten += line.write(chunk, bytesWritten, chunk.length - bytesWritten);
                }
                int audioLength = chunk.length / (this.sampleRate*2/1000);
                // Tunggu hingga audio dalam buffer selesai diputar
                Thread.sleep(audioLength - 10);
            }
    
            public void write(String b64Audio) {
                b64AudioBuffer.add(b64Audio);
            }
    
            public void cancel() {
                b64AudioBuffer.clear();
                RawAudioBuffer.clear();
            }
    
            public void waitForComplete() throws InterruptedException {
                while (!b64AudioBuffer.isEmpty() || !RawAudioBuffer.isEmpty()) {
                    Thread.sleep(100);
                }
                line.drain();
            }
    
            public void shutdown() throws InterruptedException {
                stopped.set(true);
                decoderThread.join();
                playerThread.join();
                if (line != null && line.isRunning()) {
                    line.drain();
                    line.close();
                }
            }
        }
    
        public static void main(String[] args) throws Exception {
            QwenTtsRealtimeParam param = QwenTtsRealtimeParam.builder()
                    // Gunakan model yang sama untuk desain suara dan sintesis suara
                    .model("qwen3-tts-vd-realtime-2026-01-15")
                    // Berikut ini adalah URL untuk wilayah Singapura. Jika Anda menggunakan model di wilayah Beijing, ganti URL dengan: wss://dashscope.aliyuncs.com/api-ws/v1/realtime
                    .url("wss://dashscope-intl.aliyuncs.com/api-ws/v1/realtime")
                    // Kunci API berbeda antara wilayah Singapura dan Beijing. Dapatkan Kunci API: https://www.alibabacloud.com/help/zh/model-studio/get-api-key
                    // Jika variabel lingkungan tidak disetel, ganti baris berikut dengan Kunci API Model Studio Anda: .apikey("sk-xxx")
                    .apikey(System.getenv("DASHSCOPE_API_KEY"))
                    .build();
            AtomicReference<CountDownLatch> completeLatch = new AtomicReference<>(new CountDownLatch(1));
            final AtomicReference<QwenTtsRealtime> qwenTtsRef = new AtomicReference<>(null);
    
            // Buat instance pemutar audio real-time
            RealtimePcmPlayer audioPlayer = new RealtimePcmPlayer(24000);
    
            QwenTtsRealtime qwenTtsRealtime = new QwenTtsRealtime(param, new QwenTtsRealtimeCallback() {
                @Override
                public void onOpen() {
                    // Penanganan saat koneksi dibentuk
                }
                @Override
                public void onEvent(JsonObject message) {
                    String type = message.get("type").getAsString();
                    switch(type) {
                        case "session.created":
                            // Penanganan saat sesi dibuat
                            break;
                        case "response.audio.delta":
                            String recvAudioB64 = message.get("delta").getAsString();
                            // Putar audio secara real-time
                            audioPlayer.write(recvAudioB64);
                            break;
                        case "response.done":
                            // Penanganan saat respons selesai
                            break;
                        case "session.finished":
                            // Penanganan saat sesi selesai
                            completeLatch.get().countDown();
                        default:
                            break;
                    }
                }
                @Override
                public void onClose(int code, String reason) {
                    // Penanganan saat koneksi ditutup
                }
            });
            qwenTtsRef.set(qwenTtsRealtime);
            try {
                qwenTtsRealtime.connect();
            } catch (NoApiKeyException e) {
                throw new RuntimeException(e);
            }
            QwenTtsRealtimeConfig config = QwenTtsRealtimeConfig.builder()
                    .voice("myvoice") // Ganti parameter suara dengan suara kustom yang dihasilkan oleh desain suara
                    .responseFormat(QwenTtsRealtimeAudioFormat.PCM_24000HZ_MONO_16BIT)
                    .mode("server_commit")
                    .build();
            qwenTtsRealtime.updateSession(config);
            for (String text:textToSynthesize) {
                qwenTtsRealtime.appendText(text);
                Thread.sleep(100);
            }
            qwenTtsRealtime.finish();
            completeLatch.get().await();
    
            // Tunggu hingga pemutaran audio selesai dan matikan pemutar
            audioPlayer.waitForComplete();
            audioPlayer.shutdown();
            System.exit(0);
        }
    }

Untuk contoh kode lainnya, lihat alibabacloud-bailian-speech-demo di GitHub.

Alur interaksi

Mode server_commit

Atur session.mode ke "server_commit" dalam event session.update. Dalam mode ini, server secara otomatis menangani segmentasi teks dan penentuan waktu sintesis.

  1. Klien mengirim event session.update. Server merespons dengan event session.created dan session.updated.

  2. Klien mengirim event input_text_buffer.append untuk menambahkan teks ke buffer sisi server.

  3. Server secara otomatis melakukan segmentasi teks dan menentukan waktu sintesis, lalu mengembalikan event response.created, response.output_item.added, response.content_part.added, dan response.audio.delta.

  4. Setelah respons selesai, server mengirim event response.audio.done, response.content_part.done, response.output_item.done, dan response.done.

  5. Server mengirim event session.finished untuk mengakhiri sesi.

Siklus hidup

Event klien

Event server

Inisialisasi sesi

session.update

Konfigurasi sesi

session.created

Sesi dibuat

session.updated

Konfigurasi sesi diperbarui

Input teks pengguna

input_text_buffer.append

Tambahkan teks ke buffer sisi server

input_text_buffer.commit

Sintesis langsung teks dalam buffer di server

session.finish

Beritahu server bahwa tidak ada input teks lagi

input_text_buffer.committed

Server mengonfirmasi teks yang dikomit

Output audio server

Tidak ada

response.created

Server mulai menghasilkan respons

response.output_item.added

Konten output baru ditambahkan ke respons

response.content_part.added

Bagian konten baru ditambahkan ke pesan asisten

response.audio.delta

Data audio inkremental dari model

response.content_part.done

Streaming konten teks atau audio untuk pesan asisten selesai

response.output_item.done

Streaming item output lengkap untuk pesan asisten selesai

response.audio.done

Generasi audio selesai

response.done

Respons selesai

commit mode

Atur session.mode ke "commit" dalam event session.update. Dalam mode ini, klien secara eksplisit melakukan commit buffer teks ke server untuk memicu sintesis.

  1. Klien mengirim event session.update. Server merespons dengan event session.created dan session.updated.

  2. Klien mengirim event input_text_buffer.append untuk menambahkan teks ke buffer sisi server.

  3. Klien mengirim event input_text_buffer.commit untuk melakukan commit buffer ke server, lalu mengirim event session.finish guna menandakan bahwa tidak ada input teks tambahan.

  4. Server merespons dengan response.created dan mulai menghasilkan respons.

  5. Server mengirim event response.output_item.added, response.content_part.added, dan response.audio.delta.

  6. Setelah respons selesai, server mengembalikan response.audio.done, response.content_part.done, response.output_item.done, dan response.done.

  7. Server mengirim event session.finished untuk mengakhiri sesi.

Siklus hidup

Event klien

Event server

Inisialisasi sesi

session.update

Konfigurasi sesi

session.created

Sesi dibuat

session.updated

Konfigurasi sesi diperbarui

Input teks pengguna

input_text_buffer.append

Tambahkan teks ke buffer

input_text_buffer.commit

Commit buffer ke server

input_text_buffer.clear

Kosongkan buffer

input_text_buffer.committed

Server mengonfirmasi teks yang dikomit

Output audio server

Tidak ada

response.created

Server mulai menghasilkan respons

response.output_item.added

Konten output baru ditambahkan ke respons

response.content_part.added

Bagian konten baru ditambahkan ke pesan asisten

response.audio.delta

Data audio inkremental dari model

response.content_part.done

Streaming konten teks atau audio untuk pesan asisten selesai

response.output_item.done

Streaming item output lengkap untuk pesan asisten selesai

response.audio.done

Generasi audio selesai

response.done

Respons selesai

Kontrol instruksi

Kontrol instruksi memungkinkan Anda membentuk ekspresivitas suara melalui bahasa alami—cukup jelaskan nada, laju ucapan, emosi, atau karakteristik suara secara langsung tanpa perlu menyesuaikan parameter audio.

Model yang didukung: Hanya seri Qwen3-TTS-Instruct-Flash-Realtime.

Cara menggunakan: Masukkan instruksi dalam parameter instructions, misalnya: "Berbicara cepat dengan intonasi naik, cocok untuk memperkenalkan produk fesyen."

Bahasa yang didukung: Teks instruksi hanya mendukung Mandarin dan Inggris.

Batas panjang: Instruksi tidak boleh melebihi 1.600 token.

Kasus penggunaan:

  • Pengisi suara audiobook dan drama radio

  • Pengisi suara iklan dan promosi

  • Pengisi suara karakter game dan animasi

  • Asisten suara cerdas yang peka emosi

  • Narasi siaran berita dan dokumenter

Kiat menulis deskripsi suara yang efektif:

  • Prinsip inti:

    1. Bersifat spesifik: Gunakan deskriptor konkret seperti "dalam", "jernih", atau "sedikit cepat". Hindari kata-kata samar seperti "menyenangkan" atau "normal".

    2. Cakup beberapa dimensi: Gabungkan pitch, laju ucapan, dan emosi. Deskripsi satu dimensi seperti "pitch tinggi" menghasilkan output generik.

    3. Bersifat objektif: Jelaskan karakteristik suara secara fisik dan persepsi, bukan preferensi pribadi. Misalnya, gunakan "pitch sedikit tinggi dengan nada energik" daripada "suara favorit saya".

    4. Jelaskan kualitas, jangan meniru: Fokus pada karakteristik suara, bukan permintaan peniruan individu tertentu (seperti selebriti atau aktor). Model tidak mendukung peniruan langsung, dan permintaan semacam itu berisiko melanggar hak cipta.

    5. Bersifat ringkas: Pastikan setiap kata memberikan makna. Hindari pengulangan sinonim atau penguat yang tidak perlu (misalnya, "suara yang benar-benar hebat").

  • Referensi dimensi: Menggabungkan beberapa dimensi guna menghasilkan output yang lebih kaya dan ekspresif.

    Dimensi

    Contoh

    Pitch

    Tinggi, sedang, rendah, sedikit tinggi, sedikit rendah

    Laju ucapan

    Cepat, sedang, lambat, sedikit cepat, sedikit lambat

    Emosi

    Ceria, tenang, lembut, serius, hidup, tenang, menenangkan

    Kualitas suara

    Magnetis, jernih, serak, lembut, manis, resonan, kuat

    Kasus penggunaan

    Siaran berita, pengisi suara iklan, audiobook, karakter animasi, asisten suara, narasi dokumenter

  • Contoh:

    • Gaya siaran standar: Artikulasi jelas dan tepat dengan pengucapan bulat sempurna

    • Eskalasi emosional: Volume meningkat cepat dari percakapan biasa hingga berteriak, dengan kepribadian langsung dan emosi yang diekspresikan secara terbuka

    • Keadaan emosional khusus: Ucapan sedikit cadel karena napas tersengal-sengal, disertai sedikit serak dan ketegangan yang jelas akibat menangis

    • Gaya pengisi suara iklan: Pitch sedikit tinggi, laju ucapan sedang, penuh energi dan karisma—ideal untuk pengisi suara iklan

    • Gaya lembut dan menenangkan: Laju ucapan sedikit lambat, pitch lembut dan manis, nada hangat dan menghibur, seperti teman yang peduli

Referensi API

Sintesis Suara Real-Time – Referensi API Qwen

Kloning Suara – Referensi API

Desain Suara – Referensi API

Perbandingan fitur model

Fitur

Qwen3-TTS-Instruct-Flash-Realtime

Qwen3-TTS-VD-Realtime

Qwen3-TTS-VC-Realtime

Qwen3-TTS-Flash-Realtime

Qwen-TTS-Realtime

Bahasa yang didukung

Mandarin, Inggris, Spanyol, Rusia, Italia, Prancis, Korea, Jepang, Jerman, Portugis

Mandarin, Inggris, Spanyol, Rusia, Italia, Prancis, Korea, Jepang, Jerman, Portugis

Mandarin (dialek Beijing, Shanghai, Sichuan, Nanjing, Shaanxi, Hokkien, Tianjin, dan Kanton, tergantung pada suara), Inggris, Spanyol, Rusia, Italia, Prancis, Korea, Jepang, Jerman, Portugis

Mandarin, Inggris

Format audio

PCM, WAV, MP3, Opus

PCM

Laju sampel

8 kHz, 16 kHz, 24 kHz, 48 kHz

24 kHz

Kloning suara

Tidak didukung

Didukung

Tidak didukung

Desain suara

Tidak didukung

Didukung

Tidak didukung

SSML

Tidak didukung

LaTeX

Tidak didukung

Kontrol volume

Didukung

Tidak didukung

Kontrol laju ucapan

Didukung

Tidak didukung

Kontrol pitch

Didukung

Tidak didukung

Kontrol bitrate

Didukung

Tidak didukung

Timestamp

Tidak didukung

Kontrol instruksi

Didukung

Tidak didukung

Input streaming

Didukung

Streaming Output

Didukung

Pembatasan laju

Permintaan per menit (RPM): 180

qwen3-tts-flash-realtime dan qwen3-tts-flash-realtime-2025-11-27: Permintaan per menit (RPM): 180

qwen3-tts-flash-realtime-2025-09-18: Permintaan per menit (RPM): 10

Permintaan per menit (RPM): 10

Token per menit (TPM): 100.000

Metode akses

SDK Java/Python, API WebSocket

Harga

Internasional: $0,143/10.000 karakter

Tiongkok daratan: $0,143/10.000 karakter

Internasional: $0,143353/10.000 karakter

Tiongkok daratan: $0,143353/10.000 karakter

Internasional: $0,13/10.000 karakter

Tiongkok daratan: $0,143353/10.000 karakter

Tiongkok daratan:

  • Input: $0,345/1.000 token

  • Keluaran: $1,721/1.000 token

Suara yang didukung

Model yang berbeda mendukung suara yang berbeda. Untuk menggunakan suara tertentu, atur parameter voice ke nilai yang tercantum dalam kolom parameter suara pada tabel di bawah ini.

voice parameter

Detail

Bahasa yang Didukung

Model yang Didukung

Cherry

Nama suara: Cherry

Deskripsi: Wanita muda ceria, positif, ramah, dan natural (perempuan)

Mandarin, Inggris, Prancis, Jerman, Rusia, Italia, Spanyol, Portugis, Jepang, Korea

  • Qwen3-TTS-Instruct-Flash-Realtime: qwen3-tts-instruct-flash-realtime, qwen3-tts-instruct-flash-realtime-2026-01-22

  • Qwen3-TTS-Flash-Realtime: qwen3-tts-flash-realtime, qwen3-tts-flash-realtime-2025-11-27, qwen3-tts-flash-realtime-2025-09-18

  • Qwen-TTS-Realtime: qwen-tts-realtime, qwen-tts-realtime-latest, qwen-tts-realtime-2025-07-15

Serena

Nama suara: Serena

Deskripsi: Wanita muda lembut (perempuan)

Mandarin, Inggris, Prancis, Jerman, Rusia, Italia, Spanyol, Portugis, Jepang, Korea

  • Qwen3-TTS-Instruct-Flash-Realtime: qwen3-tts-instruct-flash-realtime, qwen3-tts-instruct-flash-realtime-2026-01-22

  • Qwen3-TTS-Flash-Realtime: qwen3-tts-flash-realtime, qwen3-tts-flash-realtime-2025-11-27

  • Qwen-TTS-Realtime: qwen-tts-realtime, qwen-tts-realtime-latest, qwen-tts-realtime-2025-07-15

Ethan

Nama suara: Ethan

Deskripsi: Mandarin standar dengan sedikit aksen utara; ceria, hangat, energik, dan bersemangat (laki-laki)

Mandarin, Inggris, Prancis, Jerman, Rusia, Italia, Spanyol, Portugis, Jepang, Korea

  • Qwen3-TTS-Instruct-Flash-Realtime: qwen3-tts-instruct-flash-realtime, qwen3-tts-instruct-flash-realtime-2026-01-22

  • Qwen3-TTS-Flash-Realtime: qwen3-tts-flash-realtime, qwen3-tts-flash-realtime-2025-11-27, qwen3-tts-flash-realtime-2025-09-18

  • Qwen-TTS-Realtime: qwen-tts-realtime, qwen-tts-realtime-latest, qwen-tts-realtime-2025-07-15

Chelsie

Nama suara: Chelsie

Deskripsi: Pacar virtual dua dimensi (perempuan)

Mandarin, Inggris, Prancis, Jerman, Rusia, Italia, Spanyol, Portugis, Jepang, Korea

  • Qwen3-TTS-Instruct-Flash-Realtime: qwen3-tts-instruct-flash-realtime, qwen3-tts-instruct-flash-realtime-2026-01-22

  • Qwen3-TTS-Flash-Realtime: qwen3-tts-flash-realtime, qwen3-tts-flash-realtime-2025-11-27

  • Qwen-TTS-Realtime: qwen-tts-realtime, qwen-tts-realtime-latest, qwen-tts-realtime-2025-07-15

Momo

Nama suara: Momo

Deskripsi: Main-main dan nakal, menghibur Anda (perempuan)

Mandarin, Inggris, Prancis, Jerman, Rusia, Italia, Spanyol, Portugis, Jepang, Korea

  • Qwen3-TTS-Instruct-Flash-Realtime: qwen3-tts-instruct-flash-realtime, qwen3-tts-instruct-flash-realtime-2026-01-22

  • Qwen3-TTS-Flash-Realtime: qwen3-tts-flash-realtime, qwen3-tts-flash-realtime-2025-11-27

Vivian

Nama suara: Vivian

Deskripsi: Percaya diri, manis, dan sedikit galak (perempuan)

Mandarin, Inggris, Prancis, Jerman, Rusia, Italia, Spanyol, Portugis, Jepang, Korea

  • Qwen3-TTS-Instruct-Flash-Realtime: qwen3-tts-instruct-flash-realtime, qwen3-tts-instruct-flash-realtime-2026-01-22

  • Qwen3-TTS-Flash-Realtime: qwen3-tts-flash-realtime, qwen3-tts-flash-realtime-2025-11-27

Moon

Nama suara: Moon

Deskripsi: Pria tampan dan berani bernama Yuebai (laki-laki)

Mandarin, Inggris, Prancis, Jerman, Rusia, Italia, Spanyol, Portugis, Jepang, Korea

  • Qwen3-TTS-Instruct-Flash-Realtime: qwen3-tts-instruct-flash-realtime, qwen3-tts-instruct-flash-realtime-2026-01-22

  • Qwen3-TTS-Flash-Realtime: qwen3-tts-flash-realtime, qwen3-tts-flash-realtime-2025-11-27

Maia

Nama suara: Maia

Deskripsi: Perpaduan kecerdasan dan kelembutan (perempuan)

Mandarin, Inggris, Prancis, Jerman, Rusia, Italia, Spanyol, Portugis, Jepang, Korea

  • Qwen3-TTS-Instruct-Flash-Realtime: qwen3-tts-instruct-flash-realtime, qwen3-tts-instruct-flash-realtime-2026-01-22

  • Qwen3-TTS-Flash-Realtime: qwen3-tts-flash-realtime, qwen3-tts-flash-realtime-2025-11-27

Kai

Nama suara: Kai

Deskripsi: Spa audio yang menenangkan untuk telinga Anda (laki-laki)

Mandarin, Inggris, Prancis, Jerman, Rusia, Italia, Spanyol, Portugis, Jepang, Korea

  • Qwen3-TTS-Instruct-Flash-Realtime: qwen3-tts-instruct-flash-realtime, qwen3-tts-instruct-flash-realtime-2026-01-22

  • Qwen3-TTS-Flash-Realtime: qwen3-tts-flash-realtime, qwen3-tts-flash-realtime-2025-11-27

Nofish

Nama suara: Nofish

Deskripsi: Desainer yang tidak bisa mengucapkan bunyi retrofleks (laki-laki)

Mandarin, Inggris, Prancis, Jerman, Rusia, Italia, Spanyol, Portugis, Jepang, Korea

  • Qwen3-TTS-Instruct-Flash-Realtime: qwen3-tts-instruct-flash-realtime, qwen3-tts-instruct-flash-realtime-2026-01-22

  • Qwen3-TTS-Flash-Realtime: qwen3-tts-flash-realtime, qwen3-tts-flash-realtime-2025-11-27, qwen3-tts-flash-realtime-2025-09-18

Bella

Nama suara: Bella

Deskripsi: Gadis kecil yang minum tapi tidak pernah memukul saat mabuk (perempuan)

Mandarin, Inggris, Prancis, Jerman, Rusia, Italia, Spanyol, Portugis, Jepang, Korea

  • Qwen3-TTS-Instruct-Flash-Realtime: qwen3-tts-instruct-flash-realtime, qwen3-tts-instruct-flash-realtime-2026-01-22

  • Qwen3-TTS-Flash-Realtime: qwen3-tts-flash-realtime, qwen3-tts-flash-realtime-2025-11-27

Jennifer

Nama suara: Jennifer

Deskripsi: Suara perempuan berkualitas premium sinematik berbahasa Inggris Amerika (perempuan)

Mandarin, Inggris, Prancis, Jerman, Rusia, Italia, Spanyol, Portugis, Jepang, Korea

  • Qwen3-TTS-Flash-Realtime: qwen3-tts-flash-realtime, qwen3-tts-flash-realtime-2025-11-27, qwen3-tts-flash-realtime-2025-09-18

Ryan

Nama suara: Ryan

Deskripsi: Penuh ritme, penuh gaya dramatis, menyeimbangkan keaslian dan ketegangan (laki-laki)

Mandarin, Inggris, Prancis, Jerman, Rusia, Italia, Spanyol, Portugis, Jepang, Korea

  • Qwen3-TTS-Flash-Realtime: qwen3-tts-flash-realtime, qwen3-tts-flash-realtime-2025-11-27, qwen3-tts-flash-realtime-2025-09-18

Katerina

Nama suara: Katerina

Deskripsi: Suara wanita dewasa dengan ritme kaya dan mudah diingat (perempuan)

Mandarin, Inggris, Prancis, Jerman, Rusia, Italia, Spanyol, Portugis, Jepang, Korea

  • Qwen3-TTS-Flash-Realtime: qwen3-tts-flash-realtime, qwen3-tts-flash-realtime-2025-11-27, qwen3-tts-flash-realtime-2025-09-18

Aiden

Nama suara: Aiden

Deskripsi: Pria muda berbahasa Inggris Amerika yang mahir memasak (laki-laki)

Mandarin, Inggris, Prancis, Jerman, Rusia, Italia, Spanyol, Portugis, Jepang, Korea

  • Qwen3-TTS-Flash-Realtime: qwen3-tts-flash-realtime, qwen3-tts-flash-realtime-2025-11-27

Eldric Sage

Nama suara: Eldric Sage

Deskripsi: Orang tua yang tenang dan bijaksana—teruji seperti pohon pinus, namun pikiran jernih seperti cermin (laki-laki)

Mandarin, Inggris, Prancis, Jerman, Rusia, Italia, Spanyol, Portugis, Jepang, Korea

  • Qwen3-TTS-Instruct-Flash-Realtime: qwen3-tts-instruct-flash-realtime, qwen3-tts-instruct-flash-realtime-2026-01-22

  • Qwen3-TTS-Flash-Realtime: qwen3-tts-flash-realtime, qwen3-tts-flash-realtime-2025-11-27

Mia

Nama suara: Mia

Deskripsi: Lembut seperti air musim semi, patuh seperti salju segar (perempuan)

Mandarin, Inggris, Prancis, Jerman, Rusia, Italia, Spanyol, Portugis, Jepang, Korea

  • Qwen3-TTS-Instruct-Flash-Realtime: qwen3-tts-instruct-flash-realtime, qwen3-tts-instruct-flash-realtime-2026-01-22

  • Qwen3-TTS-Flash-Realtime: qwen3-tts-flash-realtime, qwen3-tts-flash-realtime-2025-11-27

Mochi

Nama suara: Mochi

Deskripsi: Pemuda cerdas dan cepat tanggap—kepolosan kekanak-kanakan tetap ada, namun kebijaksanaan bersinar (laki-laki)

Mandarin, Inggris, Prancis, Jerman, Rusia, Italia, Spanyol, Portugis, Jepang, Korea

  • Qwen3-TTS-Instruct-Flash-Realtime: qwen3-tts-instruct-flash-realtime, qwen3-tts-instruct-flash-realtime-2026-01-22

  • Qwen3-TTS-Flash-Realtime: qwen3-tts-flash-realtime, qwen3-tts-flash-realtime-2025-11-27

Bellona

Nama suara: Bellona

Deskripsi: Suara kuat dan jelas yang menghidupkan karakter—begitu mengharukan hingga membuat darah mendidih. Dengan keagungan heroik dan diksi sempurna, suara ini menangkap seluruh spektrum ekspresi manusia.

Mandarin, Inggris, Prancis, Jerman, Rusia, Italia, Spanyol, Portugis, Jepang, Korea

  • Qwen3-TTS-Instruct-Flash-Realtime: qwen3-tts-instruct-flash-realtime, qwen3-tts-instruct-flash-realtime-2026-01-22

  • Qwen3-TTS-Flash-Realtime: qwen3-tts-flash-realtime, qwen3-tts-flash-realtime-2025-11-27

Vincent

Nama suara: Vincent

Deskripsi: Suara unik serak dan berasap—hanya satu kalimat membangkitkan pasukan dan kisah kepahlawanan (laki-laki)

Mandarin, Inggris, Prancis, Jerman, Rusia, Italia, Spanyol, Portugis, Jepang, Korea

  • Qwen3-TTS-Instruct-Flash-Realtime: qwen3-tts-instruct-flash-realtime, qwen3-tts-instruct-flash-realtime-2026-01-22

  • Qwen3-TTS-Flash-Realtime: qwen3-tts-flash-realtime, qwen3-tts-flash-realtime-2025-11-27

Bunny

Nama suara: Bunny

Deskripsi: Gadis kecil yang penuh "kelucuan" (perempuan)

Mandarin, Inggris, Prancis, Jerman, Rusia, Italia, Spanyol, Portugis, Jepang, Korea

  • Qwen3-TTS-Instruct-Flash-Realtime: qwen3-tts-instruct-flash-realtime, qwen3-tts-instruct-flash-realtime-2026-01-22

  • Qwen3-TTS-Flash-Realtime: qwen3-tts-flash-realtime, qwen3-tts-flash-realtime-2025-11-27

Neil

Nama suara: Neil

Deskripsi: Intonasi dasar datar dengan pengucapan tepat dan jelas—pembaca berita profesional (laki-laki)

Mandarin, Inggris, Prancis, Jerman, Rusia, Italia, Spanyol, Portugis, Jepang, Korea

  • Qwen3-TTS-Instruct-Flash-Realtime: qwen3-tts-instruct-flash-realtime, qwen3-tts-instruct-flash-realtime-2026-01-22

  • Qwen3-TTS-Flash-Realtime: qwen3-tts-flash-realtime, qwen3-tts-flash-realtime-2025-11-27

Elias

Nama suara: Elias

Deskripsi: Menjaga ketelitian akademis sambil menggunakan teknik bercerita untuk mengubah pengetahuan kompleks menjadi modul pembelajaran yang mudah dicerna (perempuan)

Mandarin, Inggris, Prancis, Jerman, Rusia, Italia, Spanyol, Portugis, Jepang, Korea

  • Qwen3-TTS-Instruct-Flash-Realtime: qwen3-tts-instruct-flash-realtime, qwen3-tts-instruct-flash-realtime-2026-01-22

  • Qwen3-TTS-Flash-Realtime: qwen3-tts-flash-realtime, qwen3-tts-flash-realtime-2025-11-27, qwen3-tts-flash-realtime-2025-09-18

Arthur

Nama suara: Arthur

Deskripsi: Suara sederhana dan bersahaja yang dipenuhi waktu dan asap tembakau—perlahan menceritakan kisah desa dan keanehan (laki-laki)

Mandarin, Inggris, Prancis, Jerman, Rusia, Italia, Spanyol, Portugis, Jepang, Korea

  • Qwen3-TTS-Instruct-Flash-Realtime: qwen3-tts-instruct-flash-realtime, qwen3-tts-instruct-flash-realtime-2026-01-22

  • Qwen3-TTS-Flash-Realtime: qwen3-tts-flash-realtime, qwen3-tts-flash-realtime-2025-11-27

Nini

Nama suara: Nini

Deskripsi: Suara lembut dan manja seperti kue beras manis—panggilan panjang "Kakak" begitu manis hingga melelehkan tulang (perempuan)

Mandarin, Inggris, Prancis, Jerman, Rusia, Italia, Spanyol, Portugis, Jepang, Korea

  • Qwen3-TTS-Instruct-Flash-Realtime: qwen3-tts-instruct-flash-realtime, qwen3-tts-instruct-flash-realtime-2026-01-22

  • Qwen3-TTS-Flash-Realtime: qwen3-tts-flash-realtime, qwen3-tts-flash-realtime-2025-11-27

Seren

Nama suara: Seren

Deskripsi: Suara lembut dan menenangkan untuk membantu Anda tertidur lebih cepat. Selamat malam, mimpi indah (perempuan)

Mandarin, Inggris, Prancis, Jerman, Rusia, Italia, Spanyol, Portugis, Jepang, Korea

  • Qwen3-TTS-Instruct-Flash-Realtime: qwen3-tts-instruct-flash-realtime, qwen3-tts-instruct-flash-realtime-2026-01-22

  • Qwen3-TTS-Flash-Realtime: qwen3-tts-flash-realtime, qwen3-tts-flash-realtime-2025-11-27

Pip

Nama suara: Pip

Deskripsi: Anak laki-laki main-main dan nakal penuh keajaiban kekanak-kanakan—apakah ini kenangan Anda tentang Shin-chan? (laki-laki)

Mandarin, Inggris, Prancis, Jerman, Rusia, Italia, Spanyol, Portugis, Jepang, Korea

  • Qwen3-TTS-Instruct-Flash-Realtime: qwen3-tts-instruct-flash-realtime, qwen3-tts-instruct-flash-realtime-2026-01-22

  • Qwen3-TTS-Flash-Realtime: qwen3-tts-flash-realtime, qwen3-tts-flash-realtime-2025-11-27

Stella

Nama suara: Stella

Deskripsi: Biasanya suara remaja perempuan manis dan linglung—tetapi saat berteriak "Aku mewakili bulan untuk mengalahkanmu!", ia langsung memancarkan cinta dan keadilan yang tak tergoyahkan (perempuan)

Mandarin, Inggris, Prancis, Jerman, Rusia, Italia, Spanyol, Portugis, Jepang, Korea

  • Qwen3-TTS-Instruct-Flash-Realtime: qwen3-tts-instruct-flash-realtime, qwen3-tts-instruct-flash-realtime-2026-01-22

  • Qwen3-TTS-Flash-Realtime: qwen3-tts-flash-realtime, qwen3-tts-flash-realtime-2025-11-27

Bodega

Nama suara: Bodega

Deskripsi: Pria Spanyol yang penuh gairah (laki-laki)

Mandarin, Inggris, Prancis, Jerman, Rusia, Italia, Spanyol, Portugis, Jepang, Korea

  • Qwen3-TTS-Flash-Realtime: qwen3-tts-flash-realtime, qwen3-tts-flash-realtime-2025-11-27

Sonrisa

Nama suara: Sonrisa

Deskripsi: Wanita Latin Amerika ceria dan terbuka (perempuan)

Mandarin, Inggris, Prancis, Jerman, Rusia, Italia, Spanyol, Portugis, Jepang, Korea

  • Qwen3-TTS-Flash-Realtime: qwen3-tts-flash-realtime, qwen3-tts-flash-realtime-2025-11-27

Alek

Nama suara: Alek

Deskripsi: Dingin seperti semangat Rusia, namun hangat seperti lapisan dalam mantel wol (laki-laki)

Mandarin, Inggris, Prancis, Jerman, Rusia, Italia, Spanyol, Portugis, Jepang, Korea

  • Qwen3-TTS-Flash-Realtime: qwen3-tts-flash-realtime, qwen3-tts-flash-realtime-2025-11-27

Dolce

Nama suara: Dolce

Deskripsi: Pria Italia santai (laki-laki)

Mandarin, Inggris, Prancis, Jerman, Rusia, Italia, Spanyol, Portugis, Jepang, Korea

  • Qwen3-TTS-Flash-Realtime: qwen3-tts-flash-realtime, qwen3-tts-flash-realtime-2025-11-27

Sohee

Nama suara: Sohee

Deskripsi: Unnie Korea yang hangat, ceria, dan ekspresif secara emosional (perempuan)

Mandarin, Inggris, Prancis, Jerman, Rusia, Italia, Spanyol, Portugis, Jepang, Korea

  • Qwen3-TTS-Flash-Realtime: qwen3-tts-flash-realtime, qwen3-tts-flash-realtime-2025-11-27

Ono Anna

Nama suara: Ono Anna

Deskripsi: Teman masa kecil yang cerdas dan bersemangat (perempuan)

Mandarin, Inggris, Prancis, Jerman, Rusia, Italia, Spanyol, Portugis, Jepang, Korea

  • Qwen3-TTS-Flash-Realtime: qwen3-tts-flash-realtime, qwen3-tts-flash-realtime-2025-11-27

Lenn

Nama suara: Lenn

Deskripsi: Rasional dalam hati, memberontak dalam detail—pemuda Jerman yang mengenakan setelan jas dan mendengarkan post-punk

Mandarin, Inggris, Prancis, Jerman, Rusia, Italia, Spanyol, Portugis, Jepang, Korea

  • Qwen3-TTS-Flash-Realtime: qwen3-tts-flash-realtime, qwen3-tts-flash-realtime-2025-11-27

Emilien

Nama suara: Emilien

Deskripsi: Kakak laki-laki Prancis yang romantis (laki-laki)

Mandarin, Inggris, Prancis, Jerman, Rusia, Italia, Spanyol, Portugis, Jepang, Korea

  • Qwen3-TTS-Flash-Realtime: qwen3-tts-flash-realtime, qwen3-tts-flash-realtime-2025-11-27

Andre

Nama suara: Andre

Deskripsi: Suara laki-laki magnetis, alami, dan stabil

Mandarin, Inggris, Prancis, Jerman, Rusia, Italia, Spanyol, Portugis, Jepang, Korea

  • Qwen3-TTS-Flash-Realtime: qwen3-tts-flash-realtime, qwen3-tts-flash-realtime-2025-11-27

Radio Gol

Nama suara: Radio Gol

Deskripsi: Penyiar sepak bola Radio Gol! Hari ini saya akan berkomentar tentang sepak bola menggunakan nama saya (laki-laki)

Mandarin, Inggris, Prancis, Jerman, Rusia, Italia, Spanyol, Portugis, Jepang, Korea

  • Qwen3-TTS-Flash-Realtime: qwen3-tts-flash-realtime, qwen3-tts-flash-realtime-2025-11-27

Jada

Nama suara: Shanghai - Jada

Deskripsi: Bibi Shanghai yang berbicara cepat dan energetik (perempuan)

Dialek Shanghainese, Inggris, Prancis, Jerman, Rusia, Italia, Spanyol, Portugis, Jepang, Korea

  • Qwen3-TTS-Flash-Realtime: qwen3-tts-flash-realtime, qwen3-tts-flash-realtime-2025-11-27, qwen3-tts-flash-realtime-2025-09-18

Dylan

Nama suara: Beijing - Dylan

Deskripsi: Pemuda yang dibesarkan di hutong Beijing (laki-laki)

Dialek Beijing, Inggris, Prancis, Jerman, Rusia, Italia, Spanyol, Portugis, Jepang, Korea

  • Qwen3-TTS-Flash-Realtime: qwen3-tts-flash-realtime, qwen3-tts-flash-realtime-2025-11-27, qwen3-tts-flash-realtime-2025-09-18

Li

Nama suara: Nanjing - Li

Deskripsi: Guru yoga yang sabar (laki-laki)

Dialek Nanjing, Inggris, Prancis, Jerman, Rusia, Italia, Spanyol, Portugis, Jepang, Korea

  • Qwen3-TTS-Flash-Realtime: qwen3-tts-flash-realtime, qwen3-tts-flash-realtime-2025-11-27, qwen3-tts-flash-realtime-2025-09-18

Marcus

Nama suara: Shaanxi - Marcus

Deskripsi: Wajah lebar, sedikit bicara, hati tulus, suara dalam—rasa autentik Shaanxi (laki-laki)

Dialek Shaanxi, Inggris, Prancis, Jerman, Rusia, Italia, Spanyol, Portugis, Jepang, Korea

  • Qwen3-TTS-Flash-Realtime: qwen3-tts-flash-realtime, qwen3-tts-flash-realtime-2025-11-27, qwen3-tts-flash-realtime-2025-09-18

Roy

Nama suara: Southern Min - Roy

Deskripsi: Pria Taiwan yang humoris, langsung, dan hidup (laki-laki)

Dialek Southern Min, Inggris, Prancis, Jerman, Rusia, Italia, Spanyol, Portugis, Jepang, Korea

  • Qwen3-TTS-Flash-Realtime: qwen3-tts-flash-realtime, qwen3-tts-flash-realtime-2025-11-27, qwen3-tts-flash-realtime-2025-09-18

Peter

Nama suara: Tianjin - Peter

Deskripsi: Crosstalk ala Tianjin, pendukung profesional (laki-laki)

Dialek Tianjin, Inggris, Prancis, Jerman, Rusia, Italia, Spanyol, Portugis, Jepang, Korea

  • Qwen3-TTS-Flash-Realtime: qwen3-tts-flash-realtime, qwen3-tts-flash-realtime-2025-11-27, qwen3-tts-flash-realtime-2025-09-18

Sunny

Nama suara: Sichuan - Sunny

Deskripsi: Gadis Sichuan yang manisnya bisa melelehkan hati (perempuan)

Dialek Sichuan, Inggris, Prancis, Jerman, Rusia, Italia, Spanyol, Portugis, Jepang, Korea

  • Qwen3-TTS-Flash-Realtime: qwen3-tts-flash-realtime, qwen3-tts-flash-realtime-2025-11-27, qwen3-tts-flash-realtime-2025-09-18

Eric

Nama suara: Sichuan - Eric

Deskripsi: Pria Sichuan dari Chengdu yang menonjol dalam kehidupan sehari-hari (laki-laki)

Dialek Sichuan, Inggris, Prancis, Jerman, Rusia, Italia, Spanyol, Portugis, Jepang, Korea

  • Qwen3-TTS-Flash-Realtime: qwen3-tts-flash-realtime, qwen3-tts-flash-realtime-2025-11-27, qwen3-tts-flash-realtime-2025-09-18

Rocky

Nama suara: Kanton - Rocky

Deskripsi: A Qiang yang humoris dan cerdas memberikan obrolan langsung (laki-laki)

Dialek Kanton, Inggris, Prancis, Jerman, Rusia, Italia, Spanyol, Portugis, Jepang, Korea

  • Qwen3-TTS-Flash-Realtime: qwen3-tts-flash-realtime, qwen3-tts-flash-realtime-2025-11-27, qwen3-tts-flash-realtime-2025-09-18

Kiki

Nama suara: Kanton - Kiki

Deskripsi: Sahabat perempuan Hong Kong yang manis (perempuan)

Dialek Kanton, Inggris, Prancis, Jerman, Rusia, Italia, Spanyol, Portugis, Jepang, Korea

  • Qwen3-TTS-Flash-Realtime: qwen3-tts-flash-realtime, qwen3-tts-flash-realtime-2025-11-27, qwen3-tts-flash-realtime-2025-09-18