Skip to main content
Sintesis suara

Sintesis suara real-time

Lakukan konversi teks-ke-ucapan secara streaming dengan latensi paket pertama yang rendah. Sintesis suara real-time mendukung input dan output streaming, kloning suara, desain suara, serta kontrol audio detail halus untuk asisten suara, buku audio, dan layanan pelanggan cerdas.

Ikhtisar

Konversi teks ke ucapan secara real-time dengan latensi rendah.
  • Input dan output streaming dengan latensi paket pertama yang rendah
  • Laju ucapan, pitch, volume, dan bitrate yang dapat disesuaikan untuk kontrol audio detail halus
  • Kompatibel dengan format audio utama (PCM, WAV, MP3, Opus) dengan output laju sampel hingga 48 kHz
  • Mendukung Kontrol instruksi, yang memungkinkan Anda mengontrol ekspresivitas ucapan melalui instruksi bahasa alami
  • Mendukung Kloning suara dan Desain Suara untuk pembuatan suara kustom
  • Mendukung Tag emosi dan tag bahasa kaya, yang memungkinkan Anda menyematkan tag emosi atau efek suara dalam teks
Untuk skenario batch seperti buku audio dan sulih suara materi kursus, gunakan Sintesis suara non-real-time. Untuk panduan pemilihan model, lihat Sintesis suara.

Prasyarat

Memulai cepat

Contoh berikut menunjukkan sintesis suara untuk setiap model. Untuk contoh dan detail parameter lainnya, lihat Referensi API.
  • Qwen-Audio-TTS
  • CosyVoice
Contoh berikut mensintesis suara menggunakan suara sistem.Untuk menggunakan fitur Kontrol instruksi, atur instruksi melalui parameter instruction.
Python
# coding=utf-8

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

# Kunci API berbeda antara wilayah Singapura dan Beijing. Dapatkan Kunci API Anda: 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 China Anda: dashscope.api_key = "sk-xxx"
dashscope.api_key = os.environ.get('DASHSCOPE_API_KEY')

# Berikut adalah konfigurasi untuk wilayah Singapura. Ganti "{WorkspaceId}" dengan ID ruang kerja aktual Anda. Konfigurasi bervariasi berdasarkan wilayah.
dashscope.base_websocket_api_url='wss://{WorkspaceId}.ap-southeast-1.maas.aliyuncs.com/api-ws/v1/inference'

# Model
# qwen-audio-3.0-tts-flash/qwen-audio-3.0-tts-plus: Gunakan suara seperti longanhuan_v3.6.
# Setiap suara mendukung bahasa yang berbeda. Untuk mensintesis bahasa non-Cina seperti Jepang atau Korea, pilih suara yang mendukung bahasa target. Lihat daftar suara untuk detailnya.
model = "qwen-audio-3.0-tts-flash"
# Suara
voice = "longanhuan_v3.6"

# Buat instance SpeechSynthesizer dan teruskan parameter permintaan seperti model dan suara di konstruktor
synthesizer = SpeechSynthesizer(model=model, voice=voice)
# Kirim teks yang akan disintesis 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 penyiapan koneksi
print('[Metric] requestId: {}, first-packet latency: {} 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)

Fitur lanjutan

Kontrol instruksi

Kontrol instruksi memungkinkan penyesuaian nada, kecepatan, emosi, dan karakteristik timbre ucapan melalui deskripsi bahasa alami, tanpa perlu mengonfigurasi parameter audio yang kompleks. Spesifikasi instruksi berdasarkan model:
  • Qwen-Audio-TTS
  • CosyVoice
  • Qwen-TTS
Model yang didukung: qwen-audio-3.0-tts-plus, qwen-audio-3.0-tts-flashSuara sistem dan suara kloning: menerima instruksi apa pun.
Kasus penggunaan:
  • Narasi buku audio dan drama radio
  • Narasi iklan dan video promosi
  • Suara karakter game dan animasi
  • Asisten suara yang ekspresif secara emosional
  • Narasi dokumenter dan siaran berita
Menulis deskripsi suara yang efektif:
  • Prinsip dasar:
    1. Bersifat spesifik, bukan samar: Gunakan kata-kata yang menggambarkan kualitas vokal, seperti "dalam", "jernih", atau "kecepatan sedikit cepat". Hindari istilah subjektif atau samar seperti "bagus" atau "normal".
    2. Bersifat multidimensi, bukan satu dimensi: Deskripsi yang baik biasanya mencakup beberapa dimensi (seperti jenis kelamin, usia, dan emosi). Menulis hanya "suara perempuan" terlalu umum untuk menghasilkan timbre yang khas.
    3. Bersifat objektif, bukan subjektif: Fokus pada karakteristik fisik dan perseptual suara. Misalnya, gunakan "nada lebih tinggi dengan intonasi energetik" daripada "suara favorit saya".
    4. Bersifat orisinal, bukan imitatif: Jelaskan kualitas vokal alih-alih meminta peniruan individu tertentu (seperti selebriti atau aktor). Model tidak mendukung peniruan, dan hal tersebut dapat menimbulkan risiko pelanggaran hak cipta.
    5. Bersifat ringkas, bukan redundan: Pastikan setiap kata memiliki tujuan. Hindari sinonim berulang atau pengubah yang tidak bermakna.
  • Referensi dimensi deskripsi: Gabungkan dimensi-dimensi berikut untuk mendeskripsikan suara. Semakin banyak dimensi yang Anda sertakan, semakin akurat output-nya.

Dimension

Contoh deskripsi

Gender

Laki-laki, perempuan, androgini

Age

Anak (5–12), remaja (13–18), dewasa muda (19–35), paruh baya (36–55), lansia (55+)

Pitch

Tinggi, mid-range, rendah, agak tinggi, agak rendah

Speed

Cepat, sedang, lambat, agak cepat, agak lambat

Emotion

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

Characteristics

Magnetis, jernih, serak, hangat, manis, kaya, kuat

Use case

Siaran berita, narasi iklan, buku audio, karakter animasi, asisten suara, narasi dokumenter

  • Contoh:
    • Gaya siaran standar: artikulasi jelas dan tepat dengan pelafalan sempurna
    • Suara perempuan muda yang ceria dengan kecepatan lebih cepat dan intonasi naik yang terasa jelas, cocok untuk memperkenalkan produk fesyen
    • Laki-laki paruh baya yang tenang, kecepatan lambat, suara dalam dan magnetis, cocok untuk membacakan berita atau narasi dokumenter
    • Perempuan intelektual yang lembut, sekitar usia 30 tahun, dengan nada merata, cocok untuk narasi buku audio
    • Suara anak yang lucu, kira-kira anak perempuan berusia 8 tahun, berbicara dengan kualitas yang sedikit kekanak-kanakan, cocok untuk narasi karakter animasi

Dialek

Bagian ini menjelaskan cara menghasilkan ucapan dalam dialek Tiongkok (seperti dialek Henan, dialek Sichuan, dan Kanton). Metode konfigurasi bervariasi tergantung pada model dan jenis voice. Konfigurasi dialek berdasarkan model:
  • Qwen-Audio-TTS
  • CosyVoice
  • Qwen-TTS
  • System voices: Pilih salah satu jenis voice berikut:
    • System voice dengan dukungan dialek bawaan, yang menghasilkan dialek yang sesuai tanpa perlu konfigurasi tambahan.
    • Voice yang mendukung Instruction control dan dapat dikonfigurasi untuk menghasilkan dialek tertentu melalui teks instruksi.
  • Voice cloning voices: Konfigurasikan melalui fitur Instruction control. Misalnya, atur teks instruksi menjadi 请用河南话表达.
Dialek yang didukung: Lihat kolom "Supported languages" untuk setiap model di Qwen-Audio-TTS.

Tag emosi dan bahasa kaya

Model seri Qwen-Audio-TTS mendukung penyematan tag emosi dan bahasa kaya secara langsung dalam teks untuk sintesis suara (parameter text). Tag-tag ini mengontrol ekspresi emosional atau menyisipkan efek vokal (seperti tawa dan desahan) pada posisi tertentu, sehingga menghasilkan ucapan yang lebih ekspresif tanpa perlu mengonfigurasi parameter audio yang kompleks.
Model yang didukung: hanya qwen-audio-3.0-tts-plus dan qwen-audio-3.0-tts-flash.Batasan: Hanya mode streaming unidirectional yang didukung.
Tag kontrol Tag kontrol menetapkan emosi atau gaya ucapan. Tempatkan tag dalam teks untuk memengaruhi seluruh teks berikutnya hingga tag kontrol berikutnya muncul atau kalimat secara otomatis tersegmentasi karena panjangnya.

Tag

Deskripsi

[sad]

Sedih

[amazed]

Takjub

[deep and loud shouting]

Teriakan keras dan dalam

[trembling]

Gemetar

[angry]

Marah

[excited]

Bersemangat

[sarcastic]

Sarkastik

[curious]

Ingin tahu

[like dracula]

Gaya Dracula (dalam dan menyeramkan)

[bored]

Bosan

[tired]

Lelah

[scornful]

Menghina

[shouting]

Berteriak

[asmr]

Bisikan lembut ASMR

[panicked]

Panik

[mischievously]

Nakal

[empathetic]

Empatik

[whispers]

Berbisik

[reluctantly]

Enggan

[crying]

Menangis

[serious]

Serius

[very slowly]

Ucapan sangat lambat

[very fast]

Ucapan sangat cepat

Tag bahasa kaya Tag bahasa kaya menyisipkan efek vokal pada posisi saat ini dalam teks tanpa memengaruhi gaya emosional teks di sekitarnya.

Tag

Deskripsi

[gasp]

Astaga!

[sighing]

Mendesah

[clears throat]

Membersihkan tenggorokan

[giggles]

Tertawa kecil

[laughing]

Tertawa

[cough]

Batuk

[snorts]

Snort

Contoh penggunaan Contoh berikut menunjukkan cara menggabungkan tag kontrol dan tag bahasa kaya dalam parameter text: [excited]What a beautiful day today![laughing]Let's go out and have fun together! Dalam teks ini, [excited] adalah tag kontrol yang menerapkan emosi bersemangat pada seluruh teks berikutnya. [laughing] adalah tag bahasa kaya yang menyisipkan tawa pada posisi tersebut sebelum melanjutkan sintesis teks sisanya. Anda juga dapat beralih antar emosi berbeda dalam teks yang sama: [serious]Please pay attention to the safety precautions.[excited]Alright, let's get started now! Di sini, [serious] menetapkan nada serius pada kalimat pertama, dan [excited] mengubah nada menjadi bersemangat mulai dari kalimat kedua.

Cancel task

Jika Anda perlu menginterupsi proses sintesis saat ini selama sintesis suara real-time, kirimkan perintah cancel. Setelah pembatalan, server segera mengakhiri task saat ini dan mengembalikan event completion. Anda dapat memulai task sintesis baru pada koneksi WebSocket yang sama tanpa perlu terhubung ulang. Penggunaan:
  • Python SDK: versi 1.26.4 atau lebih baru, panggil SpeechSynthesizer.streaming_cancel().
  • Java SDK: versi 2.22.26 atau lebih baru, panggil SpeechSynthesizer.streamingCancel().
  • Protokol raw WebSocket: Kirim event finish-task dan atur directive=cancel dalam input.
Batasan model:
  • China (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.

Panggilan protokol mentah WebSocket

Contoh berikut menunjukkan cara menghubungkan langsung ke server melalui protokol mentah WebSocket, cocok untuk skenario tanpa SDK DashScope. Ini merupakan implementasi minimal yang dapat dijalankan. Untuk detail protokol WebSocket, lihat referensi API untuk masing-masing model.
  • Qwen-Audio-TTS/CosyVoice
  • Qwen-TTS
Qwen-Audio-TTS dan CosyVoice menggunakan protokol WebSocket yang sama. Contoh berikut menggunakan qwen-audio-3.0-tts-flash. Untuk menggunakan CosyVoice, ganti parameter model dengan model CosyVoice (seperti cosyvoice-v3-flash) dan parameter voice dengan suara yang diinginkan.
  • Go
  • C#
  • PHP
  • Node.js
  • Java
  • Python
package main

import (
	"encoding/json"
	"fmt"
	"net/http"
	"os"
	"strings"
	"time"

	"github.com/google/uuid"
	"github.com/gorilla/websocket"
)

const (
	// Berikut ini adalah konfigurasi untuk wilayah Singapura. Ganti "{WorkspaceId}" dengan ID ruang kerja Anda yang sebenarnya. Konfigurasi bervariasi berdasarkan wilayah.
	wsURL      = "wss://{WorkspaceId}.ap-southeast-1.maas.aliyuncs.com/api-ws/v1/inference"
	outputFile = "output.mp3"
)

func main() {
	// API Key berbeda antara wilayah Singapura dan Beijing. Dapatkan API Key Anda: https://www.alibabacloud.com/help/zh/model-studio/get-api-key
	// Jika Anda belum mengonfigurasi variabel lingkungan, ganti baris berikut dengan API Key Model Studio China Anda: apiKey := "sk-xxx"
	apiKey := os.Getenv("DASHSCOPE_API_KEY")

	// Hapus file output
	os.Remove(outputFile)
	os.Create(outputFile)

	// Hubungkan ke WebSocket
	header := make(http.Header)
	header.Add("X-DashScope-DataInspection", "enable")
	header.Add("Authorization", fmt.Sprintf("bearer %s", apiKey))

	conn, resp, err := websocket.DefaultDialer.Dial(wsURL, header)
	if err != nil {
		if resp != nil {
			fmt.Printf("Koneksi gagal, kode status HTTP: %d\n", resp.StatusCode)
		}
		fmt.Println("Koneksi gagal:", err)
		return
	}
	defer conn.Close()

	// Hasilkan ID tugas
	taskID := uuid.New().String()
	fmt.Printf("ID tugas yang dihasilkan: %s\n", taskID)

	// Mengirim event run-task
	runTaskCmd := map[string]interface{}{
		"header": map[string]interface{}{
			"action":    "run-task",
			"task_id":   taskID,
			"streaming": "duplex",
		},
		"payload": map[string]interface{}{
			"task_group": "audio",
			"task":       "tts",
			"function":   "SpeechSynthesizer",
			"model":      "qwen-audio-3.0-tts-flash",
			"parameters": map[string]interface{}{
				"text_type":   "PlainText",
				"voice":       "longanhuan_v3.6",
				"format":      "mp3",
				"sample_rate": 22050,
				"volume":      50,
				"rate":        1,
				"pitch":       1,
				// Jika enable_ssml diatur ke true, hanya satu event continue-task yang dapat dikirim; jika tidak, error "Text request limit violated, expected 1." akan dikembalikan
				"enable_ssml": false,
			},
			"input": map[string]interface{}{},
		},
	}

	runTaskJSON, _ := json.Marshal(runTaskCmd)
	fmt.Printf("Mengirim event run-task: %s\n", string(runTaskJSON))

	err = conn.WriteMessage(websocket.TextMessage, runTaskJSON)
	if err != nil {
		fmt.Println("Gagal mengirim run-task:", err)
		return
	}

	textSent := false

	// Proses pesan
	for {
		messageType, message, err := conn.ReadMessage()
		if err != nil {
			fmt.Println("Gagal membaca pesan:", err)
			break
		}

		// Proses pesan biner
		if messageType == websocket.BinaryMessage {
			fmt.Printf("Menerima pesan biner, panjang: %d\n", len(message))
			file, _ := os.OpenFile(outputFile, os.O_APPEND|os.O_WRONLY|os.O_CREATE, 0644)
			file.Write(message)
			file.Close()
			continue
		}

		// Proses pesan teks
		messageStr := string(message)
		fmt.Printf("Menerima pesan teks: %s\n", strings.ReplaceAll(messageStr, "\n", ""))

		// Uraikan JSON untuk mendapatkan jenis event
		var msgMap map[string]interface{}
		if json.Unmarshal(message, &msgMap) == nil {
			if header, ok := msgMap["header"].(map[string]interface{}); ok {
				if event, ok := header["event"].(string); ok {
					fmt.Printf("Jenis event: %s\n", event)

					switch event {
					case "task-started":
						fmt.Println("=== Menerima event task-started ===")

						if !textSent {
							// Mengirim event continue-task

							texts := []string{"Before my bed, moonlight shines bright, I suspect it's frost upon the ground.", "I raise my eyes to gaze at the bright moon, then bow my head, thinking of home."}

							for _, text := range texts {
								continueTaskCmd := map[string]interface{}{
									"header": map[string]interface{}{
										"action":    "continue-task",
										"task_id":   taskID,
										"streaming": "duplex",
									},
									"payload": map[string]interface{}{
										"input": map[string]interface{}{
											"text": text,
										},
									},
								}

								continueTaskJSON, _ := json.Marshal(continueTaskCmd)
								fmt.Printf("Mengirim event continue-task: %s\n", string(continueTaskJSON))

								err = conn.WriteMessage(websocket.TextMessage, continueTaskJSON)
								if err != nil {
									fmt.Println("Gagal mengirim continue-task:", err)
									return
								}
							}

							textSent = true

							// Tunda sebelum mengirim finish-task
							time.Sleep(500 * time.Millisecond)

							// Mengirim event finish-task
							finishTaskCmd := map[string]interface{}{
								"header": map[string]interface{}{
									"action":    "finish-task",
									"task_id":   taskID,
									"streaming": "duplex",
								},
								"payload": map[string]interface{}{
									"input": map[string]interface{}{},
								},
							}

							finishTaskJSON, _ := json.Marshal(finishTaskCmd)
							fmt.Printf("Mengirim event finish-task: %s\n", string(finishTaskJSON))

							err = conn.WriteMessage(websocket.TextMessage, finishTaskJSON)
							if err != nil {
								fmt.Println("Gagal mengirim finish-task:", err)
								return
							}
						}

					case "task-finished":
						fmt.Println("=== Tugas selesai ===")
						return

					case "task-failed":
						fmt.Println("=== Tugas gagal ===")
						if header["error_message"] != nil {
							fmt.Printf("Pesan error: %s\n", header["error_message"])
						}
						return

					case "result-generated":
						fmt.Println("Menerima event result-generated")
					}
				}
			}
		}
	}
}

Terapkan di produksi

Penggunaan kembali koneksi (WebSocket)

Koneksi WebSocket dapat digunakan kembali: setelah tugas sintesis selesai, Anda dapat memulai tugas berikutnya pada koneksi yang sama tanpa perlu membentuk koneksi baru. Proses penggunaan kembali:
  • Qwen-Audio-TTS / Qwen-Audio-TTS/CosyVoice: Klien mengirim finish-task, dan setelah server mengembalikan task-finished, klien dapat mengirim run-task untuk memulai tugas baru.
  • Qwen-TTS: Klien mengirim session.finish, dan setelah server mengembalikan session.finished, klien dapat membuat sesi baru untuk memulai tugas berikutnya.
Penggunaan kembali setelah pembatalan: Untuk Qwen-Audio-TTS / Qwen-Audio-TTS/CosyVoice, jika Anda membatalkan tugas saat ini menggunakan direktif cancel, Anda juga dapat mengirim run-task baru pada koneksi yang sama setelah server mengembalikan task-finished. Untuk detailnya, lihat Batalkan Tugas.
  1. Tunggu server mengembalikan event penyelesaian (task-finished atau session.finished) sebelum memulai tugas baru.
  2. Qwen-Audio-TTS, Qwen-Audio-TTS/CosyVoice memerlukan task_id yang berbeda untuk setiap tugas pada koneksi yang digunakan kembali.
  3. Jika tugas gagal, server mengembalikan event error dan menutup koneksi. Koneksi tersebut tidak dapat digunakan kembali.
  4. Jika tidak ada tugas baru yang dimulai dalam waktu 60 detik setelah tugas sebelumnya berakhir, koneksi akan ditutup secara otomatis.
Untuk detail event setiap model, lihat Referensi API yang sesuai.

Batas laju

Pemanggilan model tunduk pada batas laju. Ketika batas terlampaui, server mengembalikan error Requests rate limit exceeded, please try again later. Kurangi laju permintaan atau konkurensi Anda, lalu coba lagi. Untuk batas laju setiap model, lihat Pembatasan Laju.

Praktik terbaik konkurensi tinggi

SDK DashScope memiliki pooling bawaan yang menggunakan kembali koneksi WebSocket dan objek synthesizer, sehingga menghilangkan overhead pembuatan dan penghancuran berulang.
  • Qwen-Audio-TTS/CosyVoice
Qwen-Audio-TTS dan Qwen-Audio-TTS/CosyVoice menggunakan antarmuka SDK yang sama. Contoh berikut juga berlaku untuk model Qwen-Audio-TTS — cukup ganti parameter model dan voice.

Prasyarat

  • SDK Python
  • SDK Java
SDK Python menggunakan SpeechSynthesizerObjectPool untuk mengelola dan menggunakan kembali objek SpeechSynthesizer.Pool membuat sejumlah instance SpeechSynthesizer dan membentuk koneksi WebSocket saat inisialisasi. Saat Anda meminjam objek, objek tersebut siap mengirim permintaan segera, sehingga mengurangi latensi paket pertama. Setelah objek dikembalikan, koneksi tetap aktif untuk tugas berikutnya.

Langkah implementasi

  1. Instal dependensi: Instal dependensi DashScope (pip install -U dashscope).
  2. Buat dan konfigurasikan pool objek Atur ukuran pool menjadi 1,5–2 kali konkurensi puncak, dan jangan melebihi batas QPS akun Anda. Buat pool singleton global (pembentukan koneksi saat inisialisasi memakan waktu):
from dashscope.audio.tts_v2 import SpeechSynthesizerObjectPool

synthesizer_object_pool = SpeechSynthesizerObjectPool(max_size=20)
import dashscope
// Berikut adalah konfigurasi untuk wilayah China (Beijing). Ganti "{WorkspaceId}" dengan ID ruang kerja aktual Anda. Konfigurasi bervariasi berdasarkan wilayah.
dashscope.base_http_api_url = "https://{WorkspaceId}.cn-beijing.maas.aliyuncs.com/api/v1"
  • Dalam skenario pool objek, SpeechSynthesizerObjectPool membentuk koneksi WebSocket dengan server menggunakan dashscope.api_key global saat ini saat inisialisasi. Kunci API ditulis ke header Authorization hanya selama handshake WebSocket untuk autentikasi. Pesan tugas berikutnya (seperti run-task) tidak membawa Kunci API. Memodifikasi dashscope.api_key setelah pembuatan pool tidak memengaruhi koneksi yang ada — objek yang dipinjam melalui borrow_synthesizer (termasuk yang dikembalikan dan dipinjam ulang) masih menggunakan Kunci API dari handshake awal. Nilai baru diabaikan diam-diam, yang dapat menyebabkan atribusi identitas, kuota, atau penagihan berbeda dari yang diharapkan. Catatan: borrow_synthesizer tidak mendukung menentukan Kunci API sebagai parameter.
  • Untuk menggunakan beberapa Kunci API, pertahankan instance SpeechSynthesizerObjectPool terpisah untuk setiap kunci.
  1. Pinjam objek SpeechSynthesizer dari pool Jika jumlah objek yang belum dikembalikan melebihi kapasitas pool, sistem membuat objek tambahan. Objek tambahan ini harus membentuk koneksi baru dan tidak mendapat manfaat dari pooling.
speech_synthesizer = connectionPool.borrow_synthesizer(
    model='cosyvoice-v3-flash',
    voice='longanyang',
    seed=12382,
    callback=synthesizer_callback
)
  1. Lakukan sintesis ucapan Panggil metode call atau streaming_call objek SpeechSynthesizer untuk mensintesis ucapan.
  2. Kembalikan objek SpeechSynthesizer Kembalikan objek setelah tugas selesai agar tersedia untuk digunakan kembali. Jangan mengembalikan objek dengan tugas yang belum lengkap atau gagal.
connectionPool.return_synthesizer(speech_synthesizer)
Kode lengkap
Sebelum menggunakan kode ini: SpeechSynthesizerObjectPool membentuk koneksi WebSocket dan melakukan autentikasi menggunakan dashscope.api_key global saat ini saat inisialisasi. Memodifikasi dashscope.api_key setelah pembuatan pool tidak memengaruhi koneksi yang ada — nilai baru diabaikan diam-diam. Untuk beberapa Kunci API, pertahankan instance pool terpisah untuk setiap kunci. Untuk detailnya, lihat catatan penting di atas.
// !/usr/bin/env python3
// Hak Cipta (C) Alibaba Group. Seluruh Hak Dilindungi.
// Lisensi MIT (https://opensource.org/licenses/MIT)

import os
import time
import threading

import dashscope
from dashscope.audio.tts_v2 import *

USE_CONNECTION_POOL = True
text_to_synthesize = [
    'Kalimat 1: Selamat datang di layanan sintesis ucapan Alibaba.',
    'Kalimat 2: Selamat datang di layanan sintesis ucapan Alibaba.',
    'Kalimat 3: Selamat datang di layanan sintesis ucapan Alibaba.',
]
connectionPool = None

def init_dashscope_api_key():
    '''
    Tetapkan 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 Anda: 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>'  // tetapkan Kunci API secara manual

def synthesis_text_to_speech_and_play_by_streaming_mode(text, task_id):
    global USE_CONNECTION_POOL, connectionPool
    '''
    Sintesis ucapan dengan teks yang diberikan dengan mode streaming, panggilan asinkron dan putar audio yang disintesis secara real-time.
    untuk informasi lebih lanjut, silakan merujuk ke https://www.alibabacloud.com/help/document_detail/2712523.html
    '''

    complete_event = threading.Event()

    // Definisikan callback untuk menangani hasil

    class Callback(ResultCallback):
        def on_open(self):
            // saat menggunakan pool objek, on_open akan dipanggil setelah tugas dimulai
            self.file = open(f'result_{task_id}.mp3', 'wb')
            print(f'[task_{task_id}] mulai')

        def on_complete(self):
            print(f'[task_{task_id}] tugas sintesis ucapan selesai berhasil.')
            complete_event.set()

        def on_error(self, message: str):
            print(f'[task_{task_id}] tugas sintesis ucapan gagal, {message}')

        def on_close(self):
            // saat menggunakan pool objek, on_open akan dipanggil setelah tugas selesai
            print(f'[task_{task_id}] selesai')

        def on_event(self, message):
            // print(f'terima pesan sintesis ucapan {message}')
            pass

        def on_data(self, data: bytes) -> None:
            // kirim ke pemutar
            // simpan audio ke file
            self.file.write(data)

    // Panggil callback synthesizer ucapan
    synthesizer_callback = Callback()

    // Inisialisasi synthesizer ucapan
    // Anda dapat menyesuaikan parameter sintesis, seperti suara, format, laju sampel atau parameter lainnya
    if USE_CONNECTION_POOL:
        speech_synthesizer = connectionPool.borrow_synthesizer(
            model='cosyvoice-v3-flash',
            voice='longanyang',
            seed=12382,
            callback=synthesizer_callback
        )
    else:
        speech_synthesizer = SpeechSynthesizer(model='cosyvoice-v3-flash',
                                               voice='longanyang',
                                               seed=12382,
                                               callback=synthesizer_callback)
    try:
        speech_synthesizer.call(text)
    except Exception as e:
        print(f'[task_{task_id}] tugas sintesis ucapan gagal, {e}')
        if USE_CONNECTION_POOL:
            // tutup koneksi synthesizer secara manual jika tugas gagal saat menggunakan pool koneksi.
            speech_synthesizer.close()
        return

    print('[task_{}] Teks yang disintesis: {}'.format(task_id, text))
    complete_event.wait()
    print('[task_{}][Metric] requestId: {}, latensi paket pertama ms: {}'.format(
        task_id,
        speech_synthesizer.get_last_request_id(),
        speech_synthesizer.get_first_package_delay()))
    if USE_CONNECTION_POOL:
        connectionPool.return_synthesizer(speech_synthesizer)

// fungsi utama
if __name__ == '__main__':
    // Anda harus mengatur dashscope.api_key dan base_websocket_api_url sebelum membuat SpeechSynthesizerObjectPool.
    // Pool membentuk koneksi WebSocket menggunakan dashscope.api_key global saat ini saat waktu inisialisasi.
    // Memodifikasi dashscope.api_key setelah pembuatan pool tidak akan memengaruhi koneksi yang ada di pool.
    // Berikut adalah konfigurasi untuk wilayah Singapura. Ganti "{WorkspaceId}" dengan ID ruang kerja aktual Anda. Konfigurasi bervariasi berdasarkan wilayah.
    dashscope.base_websocket_api_url='wss://{WorkspaceId}.ap-southeast-1.maas.aliyuncs.com/api-ws/v1/inference'
    init_dashscope_api_key()

    if USE_CONNECTION_POOL:
        print('membuat pool koneksi')
        start_time = time.time() * 1000
        connectionPool = SpeechSynthesizerObjectPool(max_size=3)
        end_time = time.time() * 1000
        print('pool koneksi dibuat, biaya: {} ms'.format(end_time - start_time))

    task_thread_list = []
    for task_id in range(3):
        thread = threading.Thread(
            target=synthesis_text_to_speech_and_play_by_streaming_mode,
            args=(text_to_synthesize[task_id], task_id))
        task_thread_list.append(thread)

    for task_thread in task_thread_list:
        task_thread.start()

    for task_thread in task_thread_list:
        task_thread.join()

    if USE_CONNECTION_POOL:
        connectionPool.shutdown()

Manajemen sumber daya dan penanganan error

  • Tugas berhasil: Setelah tugas sintesis selesai secara normal, panggil connectionPool.return_synthesizer(speech_synthesizer) untuk mengembalikan objek SpeechSynthesizer ke pool untuk digunakan kembali.
    Jangan mengembalikan objek SpeechSynthesizer dengan tugas yang belum lengkap atau gagal.
  • Tugas gagal: Jika error internal SDK atau exception logika bisnis menyebabkan tugas dibatalkan, tutup koneksi WebSocket dasar: speech_synthesizer.close()
  • Setelah semua tugas sintesis selesai, matikan pool: connectionPool.shutdown()
  • Saat terjadi error TaskFailed di sisi server, tidak diperlukan penanganan tambahan.

Model dan wilayah yang didukung

  • Singapura
  • China (Beijing)
Untuk memanggil model berikut, pilih Kunci API dari wilayah Singapura:
  • Qwen-Audio-TTS: qwen-audio-3.0-tts-plus, qwen-audio-3.0-tts-flash
  • Qwen-Audio-TTS/CosyVoice: cosyvoice-v3-plus, cosyvoice-v3-flash
  • Qwen-TTS:
    • Qwen3-TTS-Instruct-Flash-Realtime: qwen3-tts-instruct-flash-realtime (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 (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)

Suara yang didukung

Model yang berbeda mendukung suara yang berbeda. Atur parameter permintaan voice ke nilai yang tercantum dalam kolom parameter suara pada daftar suara yang sesuai.

Referensi API

FAQ

T: Bagaimana cara memperbaiki pengucapan yang salah dalam sintesis ucapan? Bagaimana cara mengontrol pengucapan karakter homofon?

  • Ganti karakter polifonik dengan homofon untuk memperbaiki masalah pengucapan secara cepat.
  • Gunakan markup SSML untuk mengontrol pengucapan .

T: Bagaimana cara memecahkan masalah audio diam saat menggunakan suara kloning?

  1. Verifikasi status suara Panggil antarmuka API kloning/desain suara dan pastikan status suara adalah OK.
  2. Periksa konsistensi versi model Pastikan parameter target_model yang digunakan selama kloning suara cocok dengan parameter model yang digunakan untuk sintesis ucapan. Misalnya:
    • Kloning menggunakan cosyvoice-v3-plus
    • Sintesis juga harus menggunakan cosyvoice-v3-plus
  3. Verifikasi kualitas audio sumber Periksa apakah audio sumber yang digunakan untuk kloning suara memenuhi persyaratan di API kloning/desain suara:
    • Durasi audio: 10–20 detik
    • Kualitas audio jernih
    • Tidak ada kebisingan latar belakang
  4. Periksa parameter permintaan Konfirmasi bahwa parameter voice dalam permintaan sintesis ucapan diatur ke ID suara kloning.

T: Apa yang harus saya lakukan jika audio yang disintesis dari suara kloning tidak stabil atau tidak lengkap?

Jika audio yang disintesis dari suara kloning memiliki salah satu masalah berikut:
  • Pemutaran audio tidak lengkap, hanya sebagian teks yang diucapkan
  • Kualitas sintesis tidak konsisten
  • Audio berisi jeda abnormal atau segmen diam
Kemungkinan penyebab: Audio sumber tidak memenuhi persyaratan kualitas. Solusi: Periksa apakah audio sumber memenuhi persyaratan di Panduan perekaman untuk kloning suara. Rekam ulang audio mengikuti panduan perekaman.

T: Mengapa durasi aktual audio yang disintesis berbeda dari durasi yang ditunjukkan di file WAV?

Sintesis ucapan menggunakan mekanisme streaming yang mengembalikan data saat dihasilkan. Durasi di header file WAV yang disimpan bersifat perkiraan dan mungkin tidak akurat. Untuk durasi yang tepat, atur format ke pcm, tunggu hingga hasil sintesis lengkap, lalu tambahkan header file WAV secara manual.

T: Mengapa file audio tidak dapat diputar?

Pecahkan masalah berdasarkan skenario Anda:
  1. Audio disimpan sebagai file lengkap (misalnya, xx.mp3)
    1. Konsistensi format audio: Format audio dalam parameter permintaan harus sesuai dengan ekstensi file (misalnya, jika parameter adalah wav, file harus berekstensi .wav).
    2. Kompatibilitas pemutar: Pastikan pemutar mendukung format audio dan laju sampel tersebut.
  2. Pemutaran audio streaming
    1. Simpan aliran audio sebagai file lengkap dan coba putar dengan pemutar media. Jika file tidak dapat diputar, rujuk ke skenario 1 di atas.
    2. Jika file dapat diputar dengan benar, masalahnya terletak pada implementasi pemutaran streaming. Pastikan pemutar mendukung pemutaran streaming (seperti ffmpeg, pyaudio, AudioFormat, atau MediaSource).

T: Mengapa pemutaran audio tersendat?

Pecahkan masalah dengan langkah-langkah berikut:
  1. Periksa laju pengiriman teks: Pastikan interval pengiriman masuk akal agar segmen audio sebelumnya tidak selesai sebelum teks berikutnya tiba.
  2. Periksa kinerja fungsi callback:
    • Pastikan tidak ada logika pemblokiran di fungsi callback.
    • Callback berjalan di thread WebSocket. Operasi pemblokiran akan memengaruhi penerimaan data. Tulis data audio ke buffer terpisah dan proses di thread lain.
  3. Periksa stabilitas jaringan: Fluktuasi jaringan dapat menyebabkan gangguan atau penundaan transmisi audio.

T: Mengapa sintesis ucapan memakan waktu lama?

Pecahkan masalah dengan langkah-langkah berikut:
  1. Periksa interval input Untuk sintesis streaming, periksa apakah interval pengiriman teks terlalu lama. Interval yang panjang meningkatkan total waktu sintesis.
  2. Analisis metrik kinerja
    • Latensi paket pertama: biasanya sekitar 500 ms.
    • RTF (Real-Time Factor = total waktu sintesis / durasi audio): harus kurang dari 1,0.

T: Bagaimana cara membatasi Kunci API hanya untuk layanan sintesis ucapan (isolasi izin)?

Buat ruang kerja baru dan berikan akses hanya ke model tertentu. Hal ini membatasi cakupan Kunci API. Untuk detailnya, lihat Kelola ruang kerja.
Rencana Token (Team Edition)
Model playground
Statistik dan Pemantauan
Asset Center
Dukungan layanan