Sintesis suara real-time mengonversi teks menjadi suara alami melalui koneksi WebSocket. Fitur ini mendukung input dan output streaming, kloning suara, desain suara, serta kontrol audio detail untuk kasus penggunaan seperti asisten suara, buku audio, dan layanan pelanggan cerdas.
Ikhtisar
Sintesis suara real-time berlatensi rendah melalui WebSocket, dirancang untuk asisten suara, layanan pelanggan cerdas, takarir langsung, dan skenario lain yang memerlukan respons instan.
-
Input dan output streaming (WebSocket full-duplex) dengan waktu ke audio pertama yang rendah, ideal untuk percakapan real-time seperti asisten suara dan layanan pelanggan cerdas
-
Laju ucapan, pitch, volume, dan bitrate yang dapat disesuaikan untuk kontrol suara detail
-
Kompatibel dengan format audio utama (PCM, WAV, MP3, Opus) dan mendukung output laju sampel hingga 48 kHz
-
Mendukung Kontrol berbasis instruksi, memungkinkan instruksi bahasa alami untuk mengatur ekspresivitas suara
-
Mendukung Kloning suara dan Desain Suara untuk kustomisasi suara
Jika Anda tidak memerlukan output real-time, gunakan Sintesis suara non-real-time (API HTTP), yang cocok untuk skenario batch seperti buku audio dan sulih suara materi pembelajaran. Untuk panduan pemilihan model, lihat Sintesis suara.
Prasyarat
-
Konfigurasikan Kunci API dan tetapkan sebagai Variabel lingkungan.
-
Jika Anda memanggil layanan melalui SDK DashScope, instal SDK terbaru.
Memulai dengan cepat
Contoh berikut menunjukkan sintesis suara untuk setiap model. Untuk contoh dan deskripsi parameter lebih lanjut, lihat Referensi API masing-masing model.
CosyVoice
cosyvoice-v3.5-plus dan cosyvoice-v3.5-flash hanya tersedia di wilayah Beijing dan hanya mendukung skenario desain suara dan kloning suara (tidak ada suara sistem). Sebelum menggunakan model ini, buat suara melalui Kloning suara atau Desain Suara, lalu atur voice ke ID suara dan model ke nama model yang sesuai dalam kode Anda.
Contoh berikut menunjukkan cara melakukan sintesis suara dengan suara sistem (lihat Daftar Suara CosyVoice).
Untuk menggunakan kontrol instruksi, konfigurasikan instruksi melalui parameter instructions.
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: https://www.alibabacloud.com/help/en/model-studio/get-api-key
# Jika variabel lingkungan belum dikonfigurasi, ganti baris berikut dengan Kunci API Studio Model Anda: dashscope.api_key = "sk-xxx"
dashscope.api_key = os.environ.get('DASHSCOPE_API_KEY')
# URL wilayah Singapura. Ganti WorkspaceId dengan ID ruang kerja aktual Anda. URL berbeda berdasarkan wilayah.
dashscope.base_websocket_api_url='wss://{WorkspaceId}.ap-southeast-1.maas.aliyuncs.com/api-ws/v1/inference'
# Model
# Versi model berbeda memerlukan jenis suara yang sesuai:
# cosyvoice-v3-flash/cosyvoice-v3-plus: Gunakan suara seperti longanyang.
# cosyvoice-v2: Gunakan suara seperti longxiaochun_v2.
# Setiap suara mendukung bahasa berbeda. Saat mensintesis bahasa non-Tiongkok seperti Jepang atau Korea, pilih suara yang mendukung bahasa target. Untuk detailnya, lihat daftar suara CosyVoice.
model = "cosyvoice-v3-flash"
# Suara
voice = "longanyang"
# Buat instance SpeechSynthesizer dan teruskan parameter permintaan seperti model dan suara di 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: {}, first package delay: {} 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)
Java
import com.alibaba.dashscope.audio.ttsv2.SpeechSynthesisParam;
import com.alibaba.dashscope.audio.ttsv2.SpeechSynthesizer;
import com.alibaba.dashscope.utils.Constants;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
public class Main {
// Model
// Versi model berbeda memerlukan jenis suara yang sesuai:
// cosyvoice-v3-flash/cosyvoice-v3-plus: Gunakan suara seperti longanyang.
// cosyvoice-v2: Gunakan suara seperti longxiaochun_v2.
// Setiap suara mendukung bahasa berbeda. Saat mensintesis bahasa non-Tiongkok seperti Jepang atau Korea, pilih suara yang mendukung bahasa target. Untuk detailnya, lihat daftar suara CosyVoice.
private static String model = "cosyvoice-v3-flash";
// Suara
private static String voice = "longanyang";
public static void streamAudioDataToSpeaker() {
// Parameter permintaan
SpeechSynthesisParam param =
SpeechSynthesisParam.builder()
// Kunci API berbeda antara wilayah Singapura dan Beijing. Dapatkan Kunci API: https://www.alibabacloud.com/help/en/model-studio/get-api-key
// Jika variabel lingkungan belum dikonfigurasi, ganti baris berikut dengan Kunci API Studio Model Anda: .apiKey("sk-xxx")
.apiKey(System.getenv("DASHSCOPE_API_KEY"))
.model(model) // Model
.voice(voice) // Suara
.build();
// Mode sinkron: nonaktifkan callback (parameter kedua null)
SpeechSynthesizer synthesizer = new SpeechSynthesizer(param, null);
ByteBuffer audio = null;
try {
// Blokir hingga audio dikembalikan
audio = synthesizer.call("How is the weather today?");
} catch (Exception e) {
throw new RuntimeException(e);
} finally {
// Tutup koneksi WebSocket setelah tugas selesai
synthesizer.getDuplexApi().close(1000, "bye");
}
if (audio != null) {
// Simpan data audio ke file lokal "output.mp3"
File file = new File("output.mp3");
// Pengiriman teks pertama memerlukan pembentukan koneksi WebSocket, sehingga latensi paket pertama mencakup waktu pembentukan koneksi
// Catatan: getFirstPackageDelay() memerlukan dashscope-sdk-java 2.18.0 atau lebih baru
System.out.println(
"[Metric] requestId: "
+ synthesizer.getLastRequestId()
+ ", first package delay (ms): "
+ synthesizer.getFirstPackageDelay());
try (FileOutputStream fos = new FileOutputStream(file)) {
fos.write(audio.array());
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
public static void main(String[] args) {
// URL wilayah Singapura. Ganti WorkspaceId dengan ID ruang kerja aktual Anda. URL berbeda berdasarkan wilayah.
Constants.baseWebsocketApiUrl = "wss://{WorkspaceId}.ap-southeast-1.maas.aliyuncs.com/api-ws/v1/inference";
streamAudioDataToSpeaker();
System.exit(0);
}
}Qwen-TTS
Contoh berikut menunjukkan cara melakukan sintesis suara menggunakan suara sistem (lihat Suara yang Didukung).
Untuk menggunakan Kontrol Berbasis Instruksi, atur parameter model ke qwen3-tts-instruct-flash-realtime dan konfigurasikan instruksi melalui parameter instructions.
Python
Mode server commit
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():
"""
Tetapkan Kunci API DashScope Anda. Informasi selengkapnya:
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/en/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
class MyCallback(QwenTtsRealtimeCallback):
def __init__(self):
self.complete_event = threading.Event()
self.file = open('result_24k.pcm', 'wb')
def on_open(self) -> None:
print('connection opened, init player')
def on_close(self, close_status_code, close_msg) -> None:
self.file.close()
print('connection closed with code: {}, msg: {}, destroy player'.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('start session: {}'.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'response {qwen_tts_realtime.get_last_response_id()} done')
if 'session.finished' == type:
print('session finished')
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('Initializing ...')
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. Ganti WorkspaceId dengan ID ruang kerja aktual Anda. Jika Anda menggunakan wilayah Beijing, ganti dengan: wss://{WorkspaceId}.ap-southeast-1.maas.aliyuncs.com/api-ws/v1/realtime
url='wss://{WorkspaceId}.ap-southeast-1.maas.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='Speak quickly with a rising intonation, suitable for introducing fashion products.',
# optimize_instructions=True,
mode = 'server_commit'
)
for text_chunk in text_to_synthesize:
print(f'send text: {text_chunk}')
qwen_tts_realtime.append_text(text_chunk)
time.sleep(0.1)
qwen_tts_realtime.finish()
callback.wait_for_finished()
print('[Metric] session: {}, first audio delay: {}'.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():
"""
Tetapkan Kunci API DashScope Anda. Informasi selengkapnya:
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/en/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
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('connection opened, init player')
def on_close(self, close_status_code, close_msg) -> None:
print('connection closed with code: {}, msg: {}, destroy player'.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('start session: {}'.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'response {qwen_tts_realtime.get_last_response_id()} done')
self.complete_event.set()
self.file.close()
if 'session.finished' == type:
print('session finished')
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('Initializing ...')
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. Ganti WorkspaceId dengan ID ruang kerja aktual Anda. Jika Anda menggunakan wilayah Beijing, ganti dengan: wss://{WorkspaceId}.ap-southeast-1.maas.aliyuncs.com/api-ws/v1/realtime
url='wss://{WorkspaceId}.ap-southeast-1.maas.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='Speak quickly with a rising intonation, suitable for introducing fashion products.',
# optimize_instructions=True,
mode = 'commit'
)
print(f'send text: {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'send text: {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'send text: {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] session: {}, first audio delay: {}'.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 yang dibuffer 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. Ganti WorkspaceId dengan ID ruang kerja aktual Anda. Untuk Tiongkok (Beijing), gunakan wss://{WorkspaceId}.cn-beijing.maas.aliyuncs.com/api-ws/v1/realtime.
.url("wss://{WorkspaceId}.ap-southeast-1.maas.aliyuncs.com/api-ws/v1/realtime")
// Kunci API berbeda antara Singapura dan Tiongkok (Beijing). Lihat https://www.alibabacloud.com/help/en/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 pengakhiran 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 yang dibuffer 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 yang dibuffer 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. Ganti WorkspaceId dengan ID ruang kerja aktual Anda. Untuk Tiongkok (Beijing), gunakan wss://{WorkspaceId}.cn-beijing.maas.aliyuncs.com/api-ws/v1/realtime.
.url("wss://{WorkspaceId}.ap-southeast-1.maas.aliyuncs.com/api-ws/v1/realtime")
// Kunci API berbeda antara Singapura dan Tiongkok (Beijing). Lihat https://www.alibabacloud.com/help/en/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("connection opened");
System.out.println("Enter text and press Enter to send. Enter 'quit' to exit the program.");
}
@Override
public void onEvent(JsonObject message) {
String type = message.get("type").getAsString();
switch(type) {
case "session.created":
System.out.println("start session: " + 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("response done");
// 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("session finished");
if (qwenTtsRef.get() != null) {
System.out.println("[Metric] response: " + qwenTtsRef.get().getResponseId() +
", first audio delay: " + qwenTtsRef.get().getFirstAudioDelay() + " ms");
}
completeLatch.get().countDown();
default:
break;
}
}
@Override
public void onClose(int code, String reason) {
System.out.println("connection closed code: " + code + ", reason: " + 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("Enter the text to synthesize: ");
String text = scanner.nextLine();
// Keluar saat pengguna memasukkan 'quit'.
if ("quit".equalsIgnoreCase(text.trim())) {
System.out.println("Closing the connection...");
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);
}
}
Konfigurasi sesi
Mode interaksi Qwen-TTS
API Realtime Qwen-TTS menyediakan dua mode interaksi:
-
Mode server_commit: Server secara cerdas menangani segmentasi teks dan waktu sintesis. Mode ini cocok untuk sintesis berkelanjutan dari blok teks besar. Klien hanya perlu menambahkan teks tanpa mengelola segmentasi atau pengiriman.
-
Mode commit: Klien secara manual mengirimkan buffer teks untuk memicu sintesis. Mode ini cocok untuk skenario yang memerlukan kontrol tepat atas waktu sintesis, seperti sintesis bergiliran dalam AI percakapan.
Alihkan mode interaksi:
-
WebSocket: Atur bidang
modedalam eventsession.update.{ "type": "session.update", "session": { "mode": "server_commit" } } -
SDK Python: Atur parameter
modedalam metodeupdate_session.qwen_tts_realtime.update_session( voice='Cherry', response_format=AudioFormat.PCM_24000HZ_MONO_16BIT, mode='server_commit' ) -
SDK Java: Atur parameter
modemelaluiQwenTtsRealtimeConfig.builder().QwenTtsRealtimeConfig config = QwenTtsRealtimeConfig.builder() .voice("Cherry") .responseFormat(ttsFormat) .mode("server_commit") .build(); qwenTtsRealtime.updateSession(config);
Untuk contoh kode SDK lengkap, lihat SDK Python dan SDK Java. Untuk siklus hidup event WebSocket dan penggunaan kembali koneksi, lihat Referensi API WebSocket.
Fitur lanjutan
Kontrol berbasis instruksi
Kontrol berbasis instruksi memungkinkan Anda membentuk nada, kecepatan, emosi, dan timbre melalui deskripsi bahasa alami, tanpa menyesuaikan parameter audio yang kompleks.
Spesifikasi instruksi berdasarkan model:
CosyVoice
Model yang didukung: cosyvoice-v3.5-plus, cosyvoice-v3.5-flash, cosyvoice-v3-plus, cosyvoice-v3-flash
Model berbeda memiliki persyaratan format instruksi berbeda:
-
cosyvoice-v3.5-plus dan cosyvoice-v3.5-flash:
-
Timbre Kloning/Desain Suara: Menerima instruksi apa pun.
-
Suara sistem: v3.5 tidak mendukung suara sistem.
-
-
cosyvoice-v3-plus:
-
Timbre Kloning/Desain Suara: Kontrol instruksi tidak didukung.
-
Suara sistem: Instruksi harus mengikuti format tetap. Untuk detailnya, lihat Daftar Suara CosyVoice.
-
-
cosyvoice-v3-flash:
-
Timbre Kloning/Desain Suara: Menerima instruksi apa pun.
-
Suara sistem: Instruksi harus mengikuti format tetap. Untuk detailnya, lihat Daftar Suara CosyVoice.
-
Cara menggunakan: Tentukan konten instruksi melalui parameter instructions.
Bahasa yang didukung untuk teks instruksi:
-
cosyvoice-v3.5-plus dan cosyvoice-v3.5-flash:
-
Timbre Kloning/Desain Suara: Tiongkok, Inggris, Prancis, Jerman, Jepang, Korea, Rusia, Portugis, Thailand, Indonesia, dan Vietnam.
-
Suara sistem: v3.5 tidak mendukung suara sistem.
-
-
cosyvoice-v3-plus:
-
Timbre Kloning/Desain Suara: Tiongkok, Inggris, Prancis, Jerman, Jepang, Korea, dan Rusia.
-
Suara sistem: Instruksi harus mengikuti format tetap. Untuk detailnya, lihat Daftar Suara CosyVoice.
-
-
cosyvoice-v3-flash:
-
Kloning Suara/Desain Timbre: Bahasa Tionghoa, Inggris, Prancis, Jerman, Jepang, Korea, dan Rusia.
-
Suara sistem: Tiongkok.
-
Batas panjang teks instruksi: Hingga 100 karakter. Karakter Tiongkok (termasuk Tiongkok Sederhana/Tradisional, Kanji Jepang, dan Hanja Korea) dihitung sebagai 2 karakter. Karakter lain (tanda baca, huruf, angka, Kana Jepang, Hangul Korea, dll.) dihitung sebagai 1 karakter.
Qwen-TTS
Model yang didukung: Hanya model seri Qwen3-TTS-Instruct-Flash-Realtime yang didukung.
Cara menggunakan: Tentukan konten instruksi melalui parameter instruction.
Bahasa yang didukung untuk teks instruksi: Hanya Tiongkok dan Inggris.
Batas panjang teks instruksi: Hingga 1.600 token.
Kasus Penggunaan:
-
Sulih suara buku audio dan drama radio
-
Sulih suara iklan dan promosi
-
Sulih suara karakter game dan animasi
-
Asisten suara dengan ekspresi emosional
-
Narasi dokumenter dan siaran berita
Kiat menulis deskripsi suara berkualitas tinggi:
-
Prinsip inti:
-
Spesifik, bukan samar: Gunakan kata yang menggambarkan kualitas suara konkret, seperti "dalam," "jernih," atau "sedikit cepat." Hindari istilah subjektif atau samar seperti "bagus" atau "normal."
-
Multi-dimensi, bukan satu sisi: Deskripsi yang baik mencakup beberapa dimensi (jenis kelamin, usia, emosi, dll.). Menulis hanya "suara perempuan" terlalu luas untuk menghasilkan timbre yang khas.
-
Objektif, bukan subjektif: Fokus pada kualitas fisik dan persepsi suara. Misalnya, gunakan "pitch sedikit tinggi dengan energi" daripada "suara favorit saya."
-
Orisinal, bukan meniru: Jelaskan kualitas suara yang Anda inginkan, daripada meminta peniruan tokoh publik tertentu (seperti selebriti atau aktor). Model tidak mendukung peniruan, dan hal ini dapat melibatkan risiko hak cipta.
-
Ringkas, bukan berlebihan: Setiap kata harus berarti. Hindari mengulang sinonim atau menumpuk pengubah yang tidak berarti.
-
-
Dimensi deskripsi:
Menggabungkan dimensi berikut menghasilkan hasil yang lebih akurat. Semakin banyak dimensi yang dijelaskan, semakin tepat outputnya.
Dimensi
Contoh deskripsi
Jenis kelamin
Laki-laki, perempuan, netral
Usia
Anak (5-12), remaja (13-18), dewasa muda (19-35), paruh baya (36-55), lansia (55+)
Pitch
Tinggi, sedang, rendah, sedikit tinggi, sedikit rendah
Kecepatan
Cepat, sedang, lambat, sedikit cepat, sedikit lambat
Emosi
Ceria, tenang, lembut, serius, hidup, tenang, menenangkan
Timbre
Magnetis, jernih, serak, lembut, manis, kaya, kuat
Kasus penggunaan
Siaran berita, iklan, buku audio, karakter animasi, asisten suara, narasi dokumenter
-
Contoh:
-
Gaya siaran standar: Artikulasi jelas dan tepat dengan pengucapan standar
-
Suara perempuan muda dan hidup dengan kecepatan sedikit cepat dan intonasi naik yang nyata, cocok untuk memperkenalkan produk fesyen
-
Suara laki-laki paruh baya yang tenang dengan kecepatan lambat, timbre dalam dan magnetis, cocok untuk membaca berita atau narasi dokumenter
-
Suara perempuan intelektual yang lembut, sekitar 30 tahun, dengan nada tenang, cocok untuk membaca buku audio
-
Suara anak lucu, sekitar perempuan 8 tahun, ucapan sedikit kekanak-kanakan, cocok untuk sulih suara karakter animasi
-
Dialek
Gunakan model untuk menghasilkan ucapan dalam dialek Tiongkok seperti Henan, Sichuan, atau Kanton. Konfigurasi bervariasi berdasarkan model dan jenis suara.
Pengaturan dialek berdasarkan model:
CosyVoice
-
Suara sistem: Pilih salah satu suara berikut dari Daftar Suara CosyVoice:
-
Suara dengan dukungan dialek bawaan (misalnya,
longshange_v3) menghasilkan dialek tersebut tanpa konfigurasi tambahan. -
Suara yang mendukung Kontrol berbasis instruksi dan memungkinkan pemilihan dialek (misalnya,
longanhuan_v3): tentukan dialek target dalam teks instruksi.
-
-
Timbre kloning suara: Gunakan Kontrol berbasis instruksi untuk mengatur dialek — misalnya, atur teks instruksi ke
请用河南话表达. -
Timbre desain suara: Dialek belum didukung.
Dialek yang didukung per model: Lihat entri "Bahasa yang didukung" untuk setiap model di CosyVoice.
Contoh: Untuk menghasilkan ucapan dialek Henan, gunakan model cosyvoice-v3-flash dengan suara longanhuan_v3 dan atur teks instruksi ke "请用河南话表达。".
# coding=utf-8
import os
import dashscope
from dashscope.audio.tts_v2 import *
# Kunci API berbeda antara wilayah Singapura dan Beijing. Dapatkan Kunci API: https://www.alibabacloud.com/help/en/model-studio/get-api-key
# Jika variabel lingkungan belum dikonfigurasi, ganti baris berikut dengan Kunci API Studio Model Anda: dashscope.api_key = "sk-xxx"
dashscope.api_key = os.environ.get('DASHSCOPE_API_KEY')
# URL wilayah Singapura. Ganti WorkspaceId dengan ID ruang kerja aktual Anda. URL berbeda berdasarkan wilayah.
dashscope.base_websocket_api_url='wss://{WorkspaceId}.ap-southeast-1.maas.aliyuncs.com/api-ws/v1/inference'
# Model
# Versi model berbeda memerlukan jenis suara yang sesuai:
# cosyvoice-v3-flash/cosyvoice-v3-plus: Gunakan suara seperti longanyang.
# cosyvoice-v2: Gunakan suara seperti longxiaochun_v2.
# Pilih suara yang mendukung dialek
model = "cosyvoice-v3-flash"
# Suara
voice = "longanhuan_v3"
# Buat instance SpeechSynthesizer dan teruskan parameter permintaan seperti model, suara, dan instruksi di konstruktor
synthesizer = SpeechSynthesizer(model=model, voice=voice, instruction="请用河南话表达。")
# Kirim teks untuk sintesis dan dapatkan audio biner
audio = synthesizer.call("叫你去买盐,你买回来一袋面,这不是弄啥嘞吗!")
# Pengiriman teks pertama memerlukan pembentukan koneksi WebSocket, sehingga latensi paket pertama mencakup waktu pembentukan koneksi
print('[Metric] requestId: {}, first package delay: {} 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)
Qwen-TTS
-
Suara sistem: Gunakan suara sistem yang mendukung dialek. Untuk daftar suara Qwen-TTS, lihat Suara yang didukung.
-
Timbre Kloning/Desain Suara: Dialek belum didukung.
Dialek yang didukung per model: Lihat entri "Bahasa yang didukung" untuk setiap model di Qwen3-TTS.
Protokol WebSocket mentah
Contoh berikut menunjukkan cara menghubungkan langsung ke server melalui protokol WebSocket mentah, untuk skenario di mana SDK DashScope tidak digunakan. Ini adalah implementasi kerja minimal. Untuk spesifikasi protokol WebSocket setiap model, lihat referensi API yang sesuai.
Penerapan produksi
Penggunaan kembali koneksi (WebSocket)
Koneksi WebSocket mendukung penggunaan kembali: setelah tugas sintesis selesai, tugas berikutnya dapat dimulai pada koneksi yang sama tanpa perlu menghubungkan ulang.
Alur penggunaan kembali:
-
CosyVoice: Klien mengirim
finish-task. Setelah server mengembalikan eventtask-finished, klien dapat mengirim eventrun-taskuntuk memulai tugas baru. -
Qwen-TTS: Klien mengirim
session.finish. Setelah server mengembalikansession.finished, klien dapat membuat sesi baru untuk tugas berikutnya.
-
Tunggu hingga server mengembalikan event penyelesaian (
task-finishedatausession.finished) sebelum memulai tugas baru. -
CosyVoice memerlukan
task_idyang berbeda untuk setiap tugas pada koneksi yang digunakan kembali. -
Jika tugas gagal, server mengembalikan event error dan menutup koneksi. Koneksi tidak dapat digunakan kembali.
-
Jika tidak ada tugas baru yang dimulai dalam waktu 60 detik, koneksi akan terputus secara otomatis.
Untuk detail event, lihat Referensi API CosyVoice, Referensi API Qwen-TTS.
Praktik terbaik konkurensi tinggi
SDK DashScope mencakup mekanisme pooling bawaan untuk menggunakan kembali koneksi WebSocket dan objek sintesis, mengurangi overhead pembuatan dan penghancuran yang sering terjadi.
Model dan wilayah yang didukung
Singapura
Untuk memanggil model berikut, pilih Kunci API dari wilayah Singapura:
-
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)
-
Tiongkok (Beijing)
Untuk memanggil model berikut, pilih Kunci API dari wilayah Beijing:
-
CosyVoice: cosyvoice-v3.5-plus, cosyvoice-v3.5-flash, cosyvoice-v3-plus, cosyvoice-v3-flash, cosyvoice-v2
-
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)
-
Qwen-TTS-Realtime: qwen-tts-realtime (stabil, saat ini setara dengan qwen-tts-realtime-2025-07-15), qwen-tts-realtime-latest (terbaru, saat ini setara dengan qwen-tts-realtime-2025-07-15), qwen-tts-realtime-2025-07-15 (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.
Referensi API
FAQ
P: Bagaimana cara memperbaiki pengucapan salah dalam sintesis suara? Bagaimana cara mengontrol pengucapan karakter homofon?
-
Ganti karakter polifonik dengan homofon untuk memperbaiki masalah pelafalan secara cepat.
-
Gunakan bahasa markup SSML untuk mengontrol pengucapan .
P: Bagaimana cara mengatasi audio diam saat menggunakan suara yang dikloning?
-
Verifikasi status suara
Panggil API Kloning/Desain Suara dan pastikan
statussuara adalahOK. -
Periksa konsistensi versi model
Pastikan parameter
target_modelyang digunakan saat kloning suara cocok dengan parametermodelyang digunakan saat sintesis suara. Misalnya:-
Jika Anda menggunakan
cosyvoice-v3-plusuntuk kloning, -
Anda juga harus menggunakan
cosyvoice-v3-plusuntuk sintesis.
-
-
Verifikasi kualitas audio sumber
Periksa apakah audio sumber yang digunakan untuk kloning suara memenuhi persyaratan audio dan praktik terbaik:
-
Durasi audio: 10–20 detik
-
Kualitas audio jernih
-
Tidak ada kebisingan latar belakang
-
-
Periksa parameter permintaan
Pastikan parameter
voicedalam permintaan sintesis suara Anda diatur ke ID suara yang dikloning.
P: Apa yang harus saya lakukan jika suara yang dikloning menghasilkan ucapan yang tidak stabil atau tidak lengkap?
Jika ucapan yang disintesis dari suara yang dikloning menunjukkan masalah berikut:
-
Pemutaran tidak lengkap yang hanya membaca bagian dari teks
-
Kualitas sintesis tidak konsisten
-
Jeda abnormal atau segmen diam dalam ucapan
Kemungkinan penyebab: Kualitas audio sumber tidak memenuhi persyaratan.
Solusi: Periksa apakah audio sumber memenuhi persyaratan Panduan perekaman untuk kloning suara. Kami merekomendasikan merekam ulang sesuai panduan tersebut.
P: Mengapa durasi aktual berbeda dari durasi yang ditampilkan di header file WAV?
Sintesis suara menggunakan mekanisme streaming yang mengembalikan data secara progresif saat dihasilkan. Durasi pada header file WAV yang disimpan bersifat perkiraan dan mungkin tidak akurat. Untuk memperoleh durasi yang tepat, atur format ke pcm, tunggu hingga hasil sintesis lengkap, lalu tambahkan header file WAV secara manual.
P: Mengapa audio tidak diputar?
Atasi berdasarkan skenario berikut:
-
Audio disimpan sebagai file lengkap (misalnya, xx.mp3)
-
Konsistensi format audio: Format audio dalam parameter permintaan harus sesuai dengan ekstensi file (misalnya, jika format diatur ke
wav, file harus disimpan sebagai.wav). -
Kompatibilitas pemutar: Pastikan pemutar Anda mendukung format audio dan laju sampel tersebut.
-
-
Pemutaran audio streaming
-
Simpan aliran audio sebagai file lengkap dan coba putar dengan pemutar media. Jika file tidak dapat diputar, rujuk langkah pemecahan masalah pada Skenario 1.
-
Jika file dapat diputar dengan benar, masalahnya terletak pada implementasi pemutaran streaming. Pastikan pemutar Anda mendukung pemutaran streaming (seperti ffmpeg, pyaudio, AudioFormat, atau MediaSource).
-
P: Mengapa pemutaran audio tersendat?
Atasi dengan langkah-langkah berikut:
-
Periksa laju pengiriman teks: Pastikan interval antar segmen teks cukup singkat sehingga segmen berikutnya tiba sebelum audio sebelumnya selesai diputar.
-
Periksa kinerja fungsi callback:
-
Pastikan fungsi callback tidak menjalankan logika bisnis yang memblokir.
-
Callback berjalan di thread WebSocket. Memblokirnya akan menunda penerimaan data. Tulis data audio ke buffer terpisah dan proses di thread terpisah.
-
-
Periksa stabilitas jaringan: Fluktuasi jaringan dapat menyebabkan gangguan atau penundaan transmisi audio.
P: Mengapa sintesis suara memakan waktu lama?
Atasi dengan langkah-langkah berikut:
-
Periksa interval input
Untuk sintesis streaming, pastikan interval antar segmen teks tidak terlalu lama. Interval yang panjang akan meningkatkan total waktu sintesis.
-
Analisis metrik kinerja
-
Latensi paket pertama: biasanya sekitar 500 ms.
-
RTF (Real-Time Factor = total waktu sintesis / durasi audio): harus kurang dari 1,0 dalam kondisi normal.
-
P: Bagaimana cara membatasi Kunci API hanya untuk sintesis suara saja (isolasi izin)?
Buat ruang kerja baru dan otorisasi hanya model tertentu untuk membatasi cakupan Kunci API. Untuk detailnya, lihat Kelola ruang kerja.