All Products
Search
Document Center

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

Last Updated:Sep 02, 2026

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

Panduan pengguna: Untuk deskripsi model dan rekomendasi pemilihan, lihat Sintesis suara.

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

Ganti {WorkspaceId} dengan Workspace ID Anda yang sebenarnya. Domain lama tetap berfungsi penuh.

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:

Parameter

Tipe

Wajib

Deskripsi

text

str

Ya

Teks 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:

Parameter

Tipe

Wajib

Deskripsi

text

str

Ya

Segmen 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:

Parameter

Tipe

Wajib

Deskripsi

complete_timeout_millis

int

Tidak

Timeout 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.

PentingBatasan model:

  • Tiongkok (Beijing): Semua model Qwen-Audio-TTS mendukung fitur ini. Model CosyVoice memerlukan versi v2 atau lebih baru.
  • Singapura: Semua model Qwen-Audio-TTS mendukung fitur ini. Model CosyVoice tidak mendukung fitur ini.

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

model

str

Ya

Nama model.

voice

str

Ya

voicestring(wajib)

Voice yang digunakan untuk sintesis suara.

format

enum

Tidak

Format 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.

volume

int

Tidak

Tingkat volume.

Nilai default: 50.

Nilai valid: [0, 100].

speech_rate

float

Tidak

Laju bicara.

Nilai default: 1.0.

Nilai valid: [0.5, 2.0].

pitch_rate

float

Tidak

Pitch.

Nilai default: 1.0.

Nilai valid: [0.5, 2.0].

bit_rate

int

Tidak

Bit 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].

CatatanSetel 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_enabled

bool

Tidak

Menentukan apakah timestamp tingkat kata diaktifkan.

Nilai default: false.

Hanya tersedia dalam mode keluaran streaming. Voice yang didukung: voice hasil kloning dari cosyvoice-v3.5-plus, cosyvoice-v3.5-flash, cosyvoice-v3-flash, cosyvoice-v3-plus, dan cosyvoice-v2, serta voice sistem yang ditandai sebagai didukung di Daftar voice Qwen-Audio-TTS, Daftar Voice CosyVoice. Voice hasil kloning dari model lain tidak mendukung fitur ini.

CatatanSetel word_timestamp_enabled melalui parameter additional_params:

synthesizer = SpeechSynthesizer(
          model="cosyvoice-v3-flash",
          voice="your_voice",  # Voice sistem atau hasil kloning yang mendukung timestamp tingkat kata
          additional_params={"word_timestamp_enabled": True}
      )

seed

int

Tidak

Seed 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_hints

list[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 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

instruction

str

Tidak

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

Untuk detail penggunaan, lihat Kontrol instruksi.

enable_aigc_tag

bool

Tidak

Menentukan 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.

Hanya qwen-audio-3.0-tts-plus, qwen-audio-3.0-tts-flash, cosyvoice-v3-flash, cosyvoice-v3-plus, dan cosyvoice-v2 yang mendukung fitur ini.

CatatanSetel 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_propagator

str

Tidak

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

Nilai default: UID Alibaba Cloud.

Hanya qwen-audio-3.0-tts-plus, qwen-audio-3.0-tts-flash, cosyvoice-v3-flash, cosyvoice-v3-plus, dan cosyvoice-v2 yang mendukung fitur ini.

Diatur melalui parameter additional_params. Lihat contoh enable_aigc_tag.

aigc_propagate_id

str

Tidak

Mengatur 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.

Hanya qwen-audio-3.0-tts-plus, qwen-audio-3.0-tts-flash, cosyvoice-v3-flash, cosyvoice-v3-plus, dan cosyvoice-v2 yang mendukung fitur ini.

Diatur melalui parameter additional_params. Lihat contoh enable_aigc_tag.

hot_fix

dict

Tidak

Mengonfigurasi koreksi pelafalan dan penggantian teks yang diterapkan sebelum sintesis.

Fitur ini tidak didukung oleh qwen-audio-3.0-tts-plus, qwen-audio-3.0-tts-flash, atau cosyvoice-v2.

Parameter:

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

Contoh:

synthesizer = SpeechSynthesizer(
    model="cosyvoice-v3.5-plus",
    voice="cosyvoice-v3.5-plus-vd-announcer-xxxxxx", # Voice
    hot_fix={
        "pronunciation": [{"weather": "tian1 qi4"}],
        "replace": [{"today": "gold day"}]
    }
)

enable_markdown_filter

bool

Tidak

PentingHanya voice hasil kloning dari cosyvoice-v3-flash yang mendukung fitur ini.

Menentukan apakah penyaringan Markdown diaktifkan. Saat diaktifkan, sistem secara otomatis menghapus simbol markup Markdown dari teks input sebelum sintesis, sehingga simbol tersebut tidak dibacakan.

Nilai default: false.

Nilai valid:

  • true: Aktifkan penyaringan Markdown
  • false: Nonaktifkan penyaringan Markdown

CatatanSetel enable_markdown_filter melalui parameter additional_params:

synthesizer = SpeechSynthesizer(
          model="cosyvoice-v3-flash",
          voice="your_voice",  # Voice hasil kloning dari cosyvoice-v3-flash
          additional_params={"enable_markdown_filter": True}
      )

callback

ResultCallback

Tidak

Instans 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:

Parameter

Tipe

Wajib

Deskripsi

message

str

Ya

Event 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:

Parameter

Tipe

Wajib

Deskripsi

data

bytes

Ya

Chunk 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:

Parameter

Tipe

Wajib

Deskripsi

message

str

Ya

Deskripsi 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:

Bidang

Tipe

Deskripsi

type

str

Tipe event. Nilai: sentence-begin (sintesis kalimat dimulai), sentence-synthesis (sintesis kalimat sedang berlangsung), atau sentence-end (sintesis kalimat selesai).

original_text

str

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

sentence

dict

Informasi 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()))