All Products
Search
Document Center

Alibaba Cloud Model Studio:Qwen-Audio-TTS Python SDK

Last Updated:Oct 11, 2026

Gunakan DashScope Python SDK untuk mengintegrasikan sintesis suara real-time Qwen-Audio-TTS ke dalam aplikasi Anda melalui mode non-streaming, streaming satu arah, atau streaming dua arah.

Titik akhir layanan

SDK menggunakan titik akhir Wilayah Beijing secara default. Untuk beralih ke wilayah lain, ubah dashscope.base_websocket_api_url sebelum inisialisasi.

Singapura

wss://{WorkspaceId}.ap-southeast-1.maas.aliyuncs.com/api-ws/v1/inference

Ganti {WorkspaceId} dengan workspace ID Anda yang sebenarnya.

Tiongkok (Beijing)

wss://{WorkspaceId}.cn-beijing.maas.aliyuncs.com/api-ws/v1/inference

Ganti {WorkspaceId} dengan workspace ID Anda yang sebenarnya.

Beralih ke wilayah Singapura:

import dashscope

# Setel di awal kode Anda
dashscope.base_websocket_api_url = 'wss://{WorkspaceId}.ap-southeast-1.maas.aliyuncs.com/api-ws/v1/inference'

PentingAlibaba Cloud Model Studio telah merilis domain khusus ruang kerja untuk wilayah Tiongkok (Beijing) dan Singapura. Domain khusus baru ini memberikan performa lebih unggul dan stabilitas lebih tinggi untuk permintaan inferensi. Kami merekomendasikan migrasi ke domain baru:

  • Tiongkok (Beijing): dari dashscope.aliyuncs.com ke {WorkspaceId}.cn-beijing.maas.aliyuncs.com
  • Singapura: dari dashscope-intl.aliyuncs.com ke {WorkspaceId}.ap-southeast-1.maas.aliyuncs.com

SpeechSynthesizer

Jalur paket: dashscope.audio.tts_v2.SpeechSynthesizer

Konstruktor

SpeechSynthesizer(
    model: str,
    voice: str,
    format: AudioFormat = AudioFormat.MP3_22050HZ_MONO_256KBPS,
    volume: int = 50,
    speech_rate: float = 1.0,
    pitch_rate: float = 1.0,
    callback: ResultCallback = None)

call() - non-streaming

Signature metode:

def call(self, text: str) -> bytes

Parameter:

ParameterTipeWajibDeskripsi
textstrYaTeks lengkap yang akan disintesis. Panjang maksimum: 20.000 karakter.

Nilai kembalian: bytes yang berisi data audio lengkap.

Deskripsi: Pemanggilan blocking ini mengembalikan seluruh data audio sekaligus. Metode ini paling cocok untuk teks pendek yang tidak memerlukan streaming real-time. Inisialisasi ulang instans SpeechSynthesizer sebelum setiap pemanggilan.

streaming_call() - streaming

Signature metode:

def streaming_call(self, text: str) -> None

Parameter:

ParameterTipeWajibDeskripsi
textstrYaSegmen teks yang akan disintesis. Panggil metode ini beberapa kali untuk menambahkan teks. Maksimum per pemanggilan: 20.000 karakter. Maksimum kumulatif: 200.000 karakter.

Deskripsi: Pemanggilan streaming dua arah ini menerima teks dalam segmen dan mengirimkan audio hasil sintesis melalui callback secara real-time. Metode ini ideal untuk integrasi dengan model bahasa besar di mana teks dihasilkan secara progresif. Panggil streaming_complete() setelah mengirimkan seluruh teks.

streaming_complete() - akhiri streaming

Signature metode:

def streaming_complete(self) -> None

Deskripsi: Memberi tahu server bahwa seluruh teks telah dikirim. Memblokir thread saat ini hingga sisa teks disintesis dan seluruh data audio dikembalikan. Tidak memanggil metode ini dapat menyebabkan teks di akhir tidak dikonversi menjadi suara.

streaming_cancel() - batalkan sintesis streaming

Signature metode:

def streaming_cancel(self, complete_timeout_millis: int = 10000) -> None

Parameter:

ParameterTipeWajibDeskripsi
complete_timeout_millisintTidakTimeout dalam milidetik untuk menunggu server mengembalikan event task-finished. Nilai default: 10000.

Deskripsi: Membatalkan giliran sintesis suara streaming saat ini. Setelah memanggil metode ini, SDK segera mengakhiri tugas saat ini. Anda dapat memulai tugas sintesis baru pada koneksi yang sama tanpa perlu menginisialisasi ulang instans SpeechSynthesizer.

PentingPersyaratan versi: Fitur ini memerlukan Python SDK versi 1.26.4 atau lebih baru.

get_last_request_id() - dapatkan ID permintaan

Signature metode:

def get_last_request_id(self) -> str

Nilai kembalian: str yang berisi ID permintaan dari permintaan terbaru. Gunakan ini untuk troubleshooting dan pelacakan.

get_first_package_delay() - dapatkan latensi paket pertama

Signature metode:

def get_first_package_delay(self) -> int

Nilai kembalian: int yang merepresentasikan delay dalam milidetik dari pengiriman teks hingga menerima chunk audio pertama. Panggil ini setelah sintesis selesai.

get_response() - dapatkan pesan respons

Signature metode:

def get_response(self) -> str

Nilai kembalian: str yang berisi pesan respons dalam format JSON dari tugas sintesis terbaru, termasuk status permintaan dan informasi output.

Parameter konstruktor

Parameter berikut ditetapkan melalui konstruktor SpeechSynthesizer untuk mengontrol model, voice, format, dan karakteristik audio.

ParameterTipeWajibDeskripsi
modelstrYaNama model.
voicestrYaVoice yang digunakan untuk sintesis suara.
  • Voice sistem: Lihat Daftar voice Qwen-Audio-TTS
  • Suara kloning: Suara kustom yang dibuat melalui kloning suara.
  • Suara kustom: Suara yang dibuat melalui desain suara.
formatenumTidakFormat encoding audio dan laju sampel.

Default: AudioFormat.MP3_22050HZ_MONO_256KBPS.

Enum AudioFormat terletak di dashscope.audio.tts_v2 dan mendukung format MP3, WAV, PCM, dan lainnya.

volumeintTidakTingkat volume.

Nilai default: 50.

Nilai valid: [0, 100].

speech_ratefloatTidakLaju bicara.

Nilai default: 1.0.

Nilai valid: [0.5, 2.0].

pitch_ratefloatTidakPitch.

Nilai default: 1.0.

Nilai valid: [0.5, 2.0].

bit_rateintTidakBit rate audio dalam kbps. Saat format audio adalah mp3 atau opus, gunakan bit_rate untuk menyesuaikan bit rate.

Nilai default: 32.

Nilai valid: [6, 510].

Setel bit_rate melalui parameter additional_params:

synthesizer = SpeechSynthesizer(
          model="qwen-audio-3.0-tts-flash",
          voice="longanhuan_v3.6",
          additional_params={"bit_rate": 128}
      )
word_timestamp_enabledboolTidakMenentukan apakah timestamp tingkat kata diaktifkan.

Nilai default: false.

Hanya tersedia dalam mode keluaran streaming. Suara hasil kloning didukung. Untuk suara sistem yang didukung, lihat Daftar voice Qwen-Audio-TTS.

Setel word_timestamp_enabled melalui parameter additional_params:

synthesizer = SpeechSynthesizer(
          model="qwen-audio-3.0-tts-flash",
          voice="your_voice",  # Voice sistem atau hasil kloning yang mendukung timestamp tingkat kata
          additional_params={"word_timestamp_enabled": True}
      )
seedintTidakSeed acak untuk mengontrol variasi dalam output sintesis. Ketika versi model, teks, voice, dan parameter lainnya tidak berubah, menggunakan seed yang sama menghasilkan output identik.

Nilai default: 0.

Nilai valid: [0, 65535].

language_hintslist[str]Tidak

Penting

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

Menentukan bahasa target untuk sintesis suara guna meningkatkan kualitas output.

Saat pelafalan angka, ekspansi singkatan, pembacaan simbol, atau sintesis bahasa minoritas tidak sesuai ekspektasi, gunakan parameter ini. Contohnya:

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

Nilai yang valid

  • zh: Tionghoa
  • en: Inggris
  • fr: Prancis
  • de: Jerman
  • ja: Jepang
  • ko: Korea
  • ru: Rusia
  • pt: Portugis
  • th: Thai
  • id: Bahasa Indonesia
  • vi: Vietnam
  • es: Spanyol
  • it: Italia
  • ms: Malaysia
  • fil: Filipino
  • ar: Arab
instructionstrTidakMengontrol karakteristik sintesis seperti dialek, emosi, atau gaya berbicara.

Untuk detail penggunaan, lihat Kontrol instruksi.

enable_aigc_tagboolTidakMenentukan apakah watermark AIGC disematkan dalam audio yang dihasilkan. Saat diatur ke true, watermark disematkan dalam file audio dengan format yang didukung (wav/mp3/opus).

Nilai default: false.

Setel enable_aigc_tag, aigc_propagator, dan aigc_propagate_id melalui parameter additional_params:

synthesizer = SpeechSynthesizer(
          model="qwen-audio-3.0-tts-flash",
          voice="longanhuan_v3.6",
          additional_params={
              "enable_aigc_tag": True,
              "aigc_propagator": "your_propagator",
              "aigc_propagate_id": "your_propagate_id"
          }
      )
aigc_propagatorstrTidakMengatur bidang ContentPropagator dalam watermark AIGC, yang mengidentifikasi propagator konten. Hanya berlaku saat enable_aigc_tag bernilai true.

Nilai default: UID Alibaba Cloud.

Diatur melalui parameter additional_params. Lihat contoh enable_aigc_tag.

aigc_propagate_idstrTidakMengatur bidang PropagateID dalam watermark AIGC, yang secara unik mengidentifikasi aksi propagasi tertentu. Hanya berlaku saat enable_aigc_tag bernilai true.

Nilai default: ID permintaan dari permintaan sintesis suara saat ini.

Diatur melalui parameter additional_params. Lihat contoh enable_aigc_tag.

hot_fixdictTidakMengonfigurasi koreksi pelafalan dan penggantian teks yang diterapkan sebelum sintesis.

Parameter:

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

Contoh:

synthesizer = SpeechSynthesizer(
    model="qwen-audio-3.0-tts-flash",
    voice="your_voice_id", # Voice
    hot_fix={
        "pronunciation": [{"weather": "tian1 qi4"}],
        "replace": [{"today": "gold day"}]
    }
)
callbackResultCallbackTidakInstans callback untuk menerima audio hasil sintesis dan notifikasi event secara asinkron. Saat diatur, call() berjalan dalam mode streaming dan mengirimkan audio melalui callback on_data. Saat tidak diatur, call() berjalan dalam mode non-streaming dan mengembalikan seluruh audio sebagai bytes.

ResultCallback

Jalur paket: dashscope.audio.tts_v2.ResultCallback

on_open() - koneksi terbentuk

Signature metode:

def on_open(self) -> None

Dipicu saat: Koneksi WebSocket berhasil dibentuk. Gunakan callback ini untuk menginisialisasi aliran output audio atau membuka sumber daya file.

on_event() - menerima respons server

Signature metode:

def on_event(self, message: str) -> None

Parameter:

ParameterTipeWajibDeskripsi
messagestrYaEvent respons server dalam format JSON yang berisi header (informasi permintaan) dan payload (informasi output). Bidang payload.output berisi tipe event, teks asli, dan detail lainnya. Lihat bidang output dalam pesan on_event.

Dipicu saat: Respons server diterima. Pesan berupa string JSON yang berisi output event sintesis (tipe event, teks asli, informasi kalimat). Urai dengan json.loads(message) dan akses payload.output untuk detailnya.

on_complete() - sintesis selesai

Signature metode:

def on_complete(self) -> None

Dipicu saat: Seluruh teks telah disintesis dan seluruh data audio telah dikirimkan melalui on_data. Gunakan callback ini untuk memanggil get_first_package_delay() guna mendapatkan metrik performa.

on_data() - menerima data audio

Signature metode:

def on_data(self, data: bytes) -> None

Parameter:

ParameterTipeWajibDeskripsi
databytesYaChunk data biner audio dalam format yang ditentukan oleh parameter format pada konstruktor.

Dipicu saat: Chunk data audio diterima. Callback ini dipanggil beberapa kali selama sintesis. Gunakan untuk menulis data ke file atau mengirimkannya ke perangkat pemutaran.

on_error() - terjadi error

Signature metode:

def on_error(self, message: str) -> None

Parameter:

ParameterTipeWajibDeskripsi
messagestrYaDeskripsi error yang berisi kode kesalahan dan alasan detail.

Dipicu saat: Terjadi error selama sintesis. Koneksi ditutup secara otomatis setelah callback ini dijalankan. Catat error untuk troubleshooting.

on_close() - koneksi ditutup

Signature metode:

def on_close(self) -> None

Dipicu saat: Koneksi WebSocket ditutup (baik normal maupun karena error). Gunakan callback ini untuk melepas sumber daya seperti perangkat pemutaran audio.

Bidang output dalam pesan on_event

Pesan JSON yang diterima oleh callback on_event berisi bidang payload.output dengan informasi event sintesis. Gunakan bidang ini untuk melacak progres sintesis dan mengambil detail per kalimat. Struktur bidang output adalah sebagai berikut:

BidangTipeDeskripsi
typestrTipe event. Nilai: sentence-begin (sintesis kalimat dimulai), sentence-synthesis (sintesis kalimat sedang berlangsung), atau sentence-end (sintesis kalimat selesai).
original_textstrTeks asli dari kalimat saat ini. Dikembalikan dalam event sentence-begin dan sentence-end.
sentencedictInformasi kalimat. Berisi index (nomor urut kalimat) dan words (daftar kata dengan informasi timestamp saat word_timestamp_enabled aktif).

Contoh pesan:

{
  "header": {
    "task_id": "xxx",
    "event": "result-generated",
    "attributes": {}
  },
  "payload": {
    "output": {
      "type": "sentence-begin",
      "original_text": "How is the weather today?",
      "sentence": {
        "index": 0,
        "words": []
      }
    }
  }
}

Contoh penguraian:

import json

def on_event(self, message):
    data = json.loads(message)
    output = data.get('payload', {}).get('output', {})
    event_type = output.get('type', '')
    original_text = output.get('original_text', '')
    if event_type:
        print(f'Tipe event: {event_type}, Teks asli: {original_text}')

Contoh kode

SDK mendukung mode sintesis berikut:

  • Non-streaming: Pemanggilan blocking yang mengirimkan teks lengkap sekaligus dan mengembalikan seluruh audio langsung. Paling cocok untuk sintesis suara teks pendek.
  • Streaming satu arah: Pemanggilan non-blocking yang mengirimkan teks lengkap sekaligus dan mengirimkan data audio (mungkin dalam chunk) melalui fungsi callback. Paling cocok untuk skenario teks pendek yang memerlukan latensi rendah.
  • Streaming dua arah: Pemanggilan non-blocking yang mengirimkan teks dalam beberapa segmen dan mengirimkan audio hasil sintesis secara inkremental melalui fungsi callback secara real-time. Paling cocok untuk skenario teks panjang yang memerlukan latensi rendah.

Non-streaming

Teks yang dikirim dalam satu pemanggilan tidak boleh melebihi 20.000 karakter. Melebihi batas ini menyebabkan error.

PentingInisialisasi ulang instans SpeechSynthesizer sebelum setiap pemanggilan call.

# coding=utf-8

import dashscope
from dashscope.audio.tts_v2 import *
import os

# Kunci API untuk wilayah Singapura dan Beijing berbeda. Dapatkan Kunci API: https://www.alibabacloud.com/help/zh/model-studio/get-api-key
# Jika variabel lingkungan tidak dikonfigurasi, ganti baris berikut dengan Kunci API Model Studio Anda: dashscope.api_key = "sk-xxx"
dashscope.api_key = os.environ.get('DASHSCOPE_API_KEY')

# Konfigurasi berikut untuk wilayah Singapura. Ganti "{WorkspaceId}" dengan workspace ID Anda yang sebenarnya. Konfigurasi bervariasi berdasarkan wilayah.
dashscope.base_websocket_api_url='wss://{WorkspaceId}.ap-southeast-1.maas.aliyuncs.com/api-ws/v1/inference'

# Model
model = "qwen-audio-3.0-tts-flash"
# Voice
voice = "longanhuan_v3.6"

# Buat instans SpeechSynthesizer, meneruskan parameter permintaan seperti model dan voice dalam konstruktor
synthesizer = SpeechSynthesizer(model=model, voice=voice)
# Kirim teks untuk sintesis dan dapatkan audio biner
audio = synthesizer.call("How is the weather today?")
# Pengiriman teks pertama memerlukan pembentukan koneksi WebSocket, sehingga latensi paket pertama mencakup waktu pembentukan koneksi
print('[Metric] requestId: {}, latensi paket pertama: {} ms'.format(
    synthesizer.get_last_request_id(),
    synthesizer.get_first_package_delay()))

# Simpan audio ke file lokal
with open('output.mp3', 'wb') as f:
    f.write(audio)

Streaming satu arah

Teks yang dikirim dalam satu pemanggilan tidak boleh melebihi 20.000 karakter. Melebihi batas ini menyebabkan error.

PentingInisialisasi ulang instans SpeechSynthesizer sebelum setiap pemanggilan call.

# coding=utf-8

import os
import json
import dashscope
from dashscope.audio.tts_v2 import *

from datetime import datetime

def get_timestamp():
    now = datetime.now()
    formatted_timestamp = now.strftime("[%Y-%m-%d %H:%M:%S.%f]")
    return formatted_timestamp

# Kunci API untuk wilayah Singapura dan Beijing berbeda. Dapatkan Kunci API: https://www.alibabacloud.com/help/zh/model-studio/get-api-key
# Jika variabel lingkungan tidak dikonfigurasi, ganti baris berikut dengan Kunci API Model Studio Anda: dashscope.api_key = "sk-xxx"
dashscope.api_key = os.environ.get('DASHSCOPE_API_KEY')

# Konfigurasi berikut untuk wilayah Singapura. Ganti "{WorkspaceId}" dengan workspace ID Anda yang sebenarnya. Konfigurasi bervariasi berdasarkan wilayah.
dashscope.base_websocket_api_url='wss://{WorkspaceId}.ap-southeast-1.maas.aliyuncs.com/api-ws/v1/inference'

# Model
model = "qwen-audio-3.0-tts-flash"
# Voice
voice = "longanhuan_v3.6"

# Definisikan antarmuka callback
class Callback(ResultCallback):
    _player = None
    _stream = None

    def on_open(self):
        self.file = open("output.mp3", "wb")
        print("Koneksi terbentuk: " + get_timestamp())

    def on_complete(self):
        print("Sintesis suara selesai, seluruh hasil diterima: " + get_timestamp())
        # Setelah tugas selesai (callback on_complete dipicu), Anda dapat memanggil get_first_package_delay untuk mendapatkan latensi
        # Pengiriman teks pertama memerlukan pembentukan koneksi WebSocket, sehingga latensi paket pertama mencakup waktu pembentukan koneksi
        print('[Metric] requestId: {}, latensi paket pertama: {} ms'.format(
            synthesizer.get_last_request_id(),
            synthesizer.get_first_package_delay()))

    def on_error(self, message: str):
        print(f"Error sintesis suara: {message}")

    def on_close(self):
        print("Koneksi ditutup: " + get_timestamp())
        self.file.close()

    def on_event(self, message):
        # Urai event sisi server dan dapatkan informasi output
        data = json.loads(message)
        output = data.get('payload', {}).get('output', {})
        event_type = output.get('type', '')
        original_text = output.get('original_text', '')
        if event_type:
            print(f"Tipe event: {event_type}, teks asli: {original_text}")

    def on_data(self, data: bytes) -> None:
        print(get_timestamp() + " Panjang audio biner: " + str(len(data)))
        self.file.write(data)

callback = Callback()

# Buat instans SpeechSynthesizer, meneruskan parameter permintaan seperti model dan voice dalam konstruktor
synthesizer = SpeechSynthesizer(
    model=model,
    voice=voice,
    callback=callback,
)

# Kirim teks untuk sintesis dan dapatkan audio biner secara real-time melalui metode callback on_data
synthesizer.call("How is the weather today?")

Streaming dua arah

Teks yang dikirim per pemanggilan tidak boleh melebihi 20.000 karakter. Teks kumulatif tidak boleh melebihi 200.000 karakter.

  • Selama input streaming, panggil streaming_call beberapa kali untuk mengirimkan segmen teks secara berurutan. Server secara otomatis melakukan segmentasi kalimat pada teks yang diterima:

    • Kalimat lengkap langsung disintesis
    • Kalimat tidak lengkap dibuffer hingga lengkap

    Saat streaming_complete dipanggil, server memaksa mensintesis seluruh teks yang diterima namun belum diproses (termasuk kalimat tidak lengkap).

  • Interval antar segmen teks tidak boleh melebihi 23 detik. Jika tidak, exception "request timeout after 23 seconds" akan muncul.

    Jika tidak ada teks yang akan dikirim, segera panggil streaming_complete untuk mengakhiri tugas.

    PentingSelalu panggil streaming_complete. Tidak melakukannya dapat menyebabkan teks di akhir tidak dikonversi menjadi suara.

    Server menerapkan timeout 23 detik. Nilai ini tidak dapat diubah di sisi klien.

# coding=utf-8
#
# Petunjuk instalasi pyaudio:
# Untuk macOS, jalankan perintah berikut:
#   brew install portaudio
#   pip install pyaudio
# Untuk Debian/Ubuntu, jalankan perintah berikut:
#   sudo apt-get install python-pyaudio python3-pyaudio
#   atau
#   pip install pyaudio
# Untuk CentOS, jalankan perintah berikut:
#   sudo yum install -y portaudio portaudio-devel && pip install pyaudio
# Untuk Microsoft Windows, jalankan perintah berikut:
#   python -m pip install pyaudio

import os
import time
import pyaudio
import json
import dashscope
from dashscope.api_entities.dashscope_response import SpeechSynthesisResponse
from dashscope.audio.tts_v2 import *

from datetime import datetime

def get_timestamp():
    now = datetime.now()
    formatted_timestamp = now.strftime("[%Y-%m-%d %H:%M:%S.%f]")
    return formatted_timestamp

# Kunci API untuk wilayah Singapura dan Beijing berbeda. Dapatkan Kunci API: https://www.alibabacloud.com/help/zh/model-studio/get-api-key
# Jika variabel lingkungan tidak dikonfigurasi, ganti baris berikut dengan Kunci API Model Studio Anda: dashscope.api_key = "sk-xxx"
dashscope.api_key = os.environ.get('DASHSCOPE_API_KEY')

# Konfigurasi berikut untuk wilayah Singapura. Ganti "{WorkspaceId}" dengan workspace ID Anda yang sebenarnya. Konfigurasi bervariasi berdasarkan wilayah.
dashscope.base_websocket_api_url='wss://{WorkspaceId}.ap-southeast-1.maas.aliyuncs.com/api-ws/v1/inference'

# Model
model = "qwen-audio-3.0-tts-flash"
# Voice
voice = "longanhuan_v3.6"

# Definisikan antarmuka callback
class Callback(ResultCallback):
    _player = None
    _stream = None

    def on_open(self):
        print("Koneksi terbentuk: " + get_timestamp())
        self._player = pyaudio.PyAudio()
        self._stream = self._player.open(
            format=pyaudio.paInt16, channels=1, rate=22050, output=True
        )

    def on_complete(self):
        print("Sintesis suara selesai, seluruh hasil diterima: " + get_timestamp())

    def on_error(self, message: str):
        print(f"Error sintesis suara: {message}")

    def on_close(self):
        print("Koneksi ditutup: " + get_timestamp())
        # Hentikan pemutar
        self._stream.stop_stream()
        self._stream.close()
        self._player.terminate()

    def on_event(self, message):
        # Urai event sisi server dan dapatkan informasi output
        data = json.loads(message)
        output = data.get('payload', {}).get('output', {})
        event_type = output.get('type', '')
        original_text = output.get('original_text', '')
        if event_type:
            print(f"Tipe event: {event_type}, teks asli: {original_text}")

    def on_data(self, data: bytes) -> None:
        print(get_timestamp() + " Panjang audio biner: " + str(len(data)))
        self._stream.write(data)

callback = Callback()

test_text = [
    "Streaming text-to-speech SDK,",
    "converts input text",
    "into binary audio data.",
    "Compared with non-streaming speech synthesis,",
    "streaming synthesis offers better real-time performance.",
    "Users hear near-synchronous audio output while typing,",
    "greatly improving interaction experience",
    "and reducing wait time.",
    "Ideal for large language model (LLM) integration,",
    "where text is streamed for speech synthesis.",
]

# Buat instans SpeechSynthesizer, meneruskan parameter permintaan seperti model dan voice dalam konstruktor
synthesizer = SpeechSynthesizer(
    model=model,
    voice=voice,
    format=AudioFormat.PCM_22050HZ_MONO_16BIT,
    callback=callback,
)

# Kirim teks untuk sintesis streaming. Dapatkan audio biner secara real-time melalui metode callback on_data
for text in test_text:
    synthesizer.streaming_call(text)
    time.sleep(0.1)
# Akhiri sintesis suara streaming
synthesizer.streaming_complete()

# Pengiriman teks pertama memerlukan pembentukan koneksi WebSocket, sehingga latensi paket pertama mencakup waktu pembentukan koneksi
print('[Metric] requestId: {}, latensi paket pertama: {} ms'.format(
    synthesizer.get_last_request_id(),
    synthesizer.get_first_package_delay()))