Converta texto em fala via WebSocket com baixa latência no primeiro pacote. A síntese de fala em tempo real oferece entrada e saída em streaming, clonagem de voz, design de voz e controles de áudio refinados para assistentes de voz, audiolivros e atendimento ao cliente inteligente.
Visão geral
Transforme texto em fala em tempo real usando um protocolo de streaming bidirecional via WebSocket com baixa latência.
Entrada e saída em streaming com baixa latência no primeiro pacote
Controle refinado de áudio com ajustes de velocidade da fala, tom, volume e taxa de bits
Compatibilidade com os principais formatos de áudio (PCM, WAV, MP3, Opus) e saída com taxa de amostragem de até 48 kHz
Suporte a Controle por instruções, que permite controlar a expressividade da fala por meio de instruções em linguagem natural
Suporte a Clonagem de voz e Design de voz para criação de vozes personalizadas
Suporte a Tags de emoção e linguagem rica, que permitem inserir tags de emoção ou efeitos sonoros diretamente no texto
Para cenários em lote, como narração de audiolivros e materiais didáticos, utilize a Síntese de fala não em tempo real. Para orientações sobre escolha de modelos, consulte Síntese de fala.
Pré-requisitos
Configure uma chave de API e defina-a como variável de ambiente.
Se você chamar a API pelo DashScope SDK, instale a versão mais recente do SDK.
Início rápido
Os exemplos a seguir demonstram a síntese de fala para cada modelo. Para mais exemplos e detalhes dos parâmetros, consulte a Referência da API.
Qwen-Audio-TTS
O exemplo abaixo sintetiza fala utilizando uma voz do sistema.
Para usar o recurso de Controle por instruções, defina as instruções por meio do parâmetro instruction.
Python
# coding=utf-8
import os
import dashscope
from dashscope.audio.tts_v2 import *
# The API Key differs between the Singapore and Beijing regions. Get your API Key: https://www.alibabacloud.com/help/zh/model-studio/get-api-key
# If you have not configured the environment variable, replace the next line with your Chinese Model Studio API Key: dashscope.api_key = "sk-xxx"
dashscope.api_key = os.environ.get('DASHSCOPE_API_KEY')
# The following is the configuration for the Singapore region. Replace "{WorkspaceId}" with your actual workspace ID. The configuration varies by region.
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: Use voices such as longanlingxi.
# Each voice supports different languages. To synthesize non-Chinese languages such as Japanese or Korean, select a voice that supports the target language. See the voice list for details.
model = "qwen-audio-3.0-tts-flash"
# Voice
voice = "longanlingxi"
# Instantiate SpeechSynthesizer and pass request parameters such as model and voice in the constructor
synthesizer = SpeechSynthesizer(model=model, voice=voice)
# Send the text to be synthesized and get the binary audio
audio = synthesizer.call("How is the weather today?")
# The first text submission requires establishing a WebSocket connection, so the first-packet latency includes connection setup time
print('[Metric] requestId: {}, first-packet latency: {} ms'.format(
synthesizer.get_last_request_id(),
synthesizer.get_first_package_delay()))
# Save the audio to a local file
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
// qwen-audio-3.0-tts-flash/qwen-audio-3.0-tts-plus: Use voices such as longanlingxi.
// Each voice supports different languages. To synthesize non-Chinese languages such as Japanese or Korean, select a voice that supports the target language. See the voice list for details.
private static String model = "qwen-audio-3.0-tts-flash";
// Voice
private static String voice = "longanlingxi";
public static void streamAudioDataToSpeaker() {
// Request parameters
SpeechSynthesisParam param =
SpeechSynthesisParam.builder()
// The API Key differs between the Singapore and Beijing regions. Get your API Key: https://www.alibabacloud.com/help/zh/model-studio/get-api-key
// If you have not configured the environment variable, replace the next line with your Chinese Model Studio API Key: .apiKey("sk-xxx")
.apiKey(System.getenv("DASHSCOPE_API_KEY"))
.model(model) // Model
.voice(voice) // Voice
.build();
// Synchronous mode: disable callback (second parameter is null)
SpeechSynthesizer synthesizer = new SpeechSynthesizer(param, null);
ByteBuffer audio = null;
try {
// Block until audio is returned
audio = synthesizer.call("How is the weather today?");
} catch (Exception e) {
throw new RuntimeException(e);
} finally {
// Close the WebSocket connection when the task is done
synthesizer.getDuplexApi().close(1000, "bye");
}
if (audio != null) {
// Save the audio data to the local file "output.mp3"
File file = new File("output.mp3");
// The first text submission requires establishing a WebSocket connection, so the first-packet latency includes connection setup time
// Note: getFirstPackageDelay() requires dashscope-sdk-java 2.18.0 or later
System.out.println(
"[Metric] requestId: "
+ synthesizer.getLastRequestId()
+ ", first-packet latency (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) {
// The following is the configuration for the Singapore region. Replace "{WorkspaceId}" with your actual workspace ID. The configuration varies by region.
Constants.baseWebsocketApiUrl = "wss://{WorkspaceId}.ap-southeast-1.maas.aliyuncs.com/api-ws/v1/inference";
streamAudioDataToSpeaker();
System.exit(0);
}
}
CosyVoice
Os modelos cosyvoice-v3.5-plus e cosyvoice-v3.5-flash estão disponíveis apenas na região de Beijing e suportam exclusivamente cenários de design de voz e clonagem de voz (sem vozes do sistema). Antes de utilizá-los, crie uma voz personalizada por meio de Clonagem de voz ou Design de voz. Em seguida, defina voice com o ID da voz e model com o nome do modelo correspondente no seu código.
O exemplo a seguir sintetiza fala usando uma voz do sistema (consulte a Lista de vozes do CosyVoice).
Para utilizar o recurso de Controle por instruções, configure as instruções através do parâmetro instruction.
Python
# coding=utf-8
import os
import dashscope
from dashscope.audio.tts_v2 import *
# The API Key differs between the Singapore and Beijing regions. Get your API Key: https://www.alibabacloud.com/help/zh/model-studio/get-api-key
# If you have not configured the environment variable, replace the next line with your Chinese Model Studio API Key: dashscope.api_key = "sk-xxx"
dashscope.api_key = os.environ.get('DASHSCOPE_API_KEY')
# The following is the configuration for the Singapore region. Replace "{WorkspaceId}" with your actual workspace ID. The configuration varies by region.
dashscope.base_websocket_api_url='wss://{WorkspaceId}.ap-southeast-1.maas.aliyuncs.com/api-ws/v1/inference'
# Model
# Different model versions require their corresponding voices:
# cosyvoice-v3-flash/cosyvoice-v3-plus: Use voices such as longanyang.
# cosyvoice-v2: Use voices such as longxiaochun_v2.
# Each voice supports different languages. To synthesize non-Chinese languages such as Japanese or Korean, select a voice that supports the target language. See the Qwen-Audio-TTS/CosyVoice voice list for details.
model = "cosyvoice-v3-flash"
# Voice
voice = "longanyang"
# Instantiate SpeechSynthesizer and pass request parameters such as model and voice in the constructor
synthesizer = SpeechSynthesizer(model=model, voice=voice)
# Send the text to be synthesized and get the binary audio
audio = synthesizer.call("How is the weather today?")
# The first text submission requires establishing a WebSocket connection, so the first-packet latency includes connection setup time
print('[Metric] requestId: {}, first-packet latency: {} ms'.format(
synthesizer.get_last_request_id(),
synthesizer.get_first_package_delay()))
# Save the audio to a local file
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
// Different model versions require their corresponding voices:
// cosyvoice-v3-flash/cosyvoice-v3-plus: Use voices such as longanyang.
// cosyvoice-v2: Use voices such as longxiaochun_v2.
// Each voice supports different languages. To synthesize non-Chinese languages such as Japanese or Korean, select a voice that supports the target language. See the Qwen-Audio-TTS/CosyVoice voice list for details.
private static String model = "cosyvoice-v3-flash";
// Voice
private static String voice = "longanyang";
public static void streamAudioDataToSpeaker() {
// Request parameters
SpeechSynthesisParam param =
SpeechSynthesisParam.builder()
// The API Key differs between the Singapore and Beijing regions. Get your API Key: https://www.alibabacloud.com/help/zh/model-studio/get-api-key
// If you have not configured the environment variable, replace the next line with your Chinese Model Studio API Key: .apiKey("sk-xxx")
.apiKey(System.getenv("DASHSCOPE_API_KEY"))
.model(model) // Model
.voice(voice) // Voice
.build();
// Synchronous mode: disable callback (second parameter is null)
SpeechSynthesizer synthesizer = new SpeechSynthesizer(param, null);
ByteBuffer audio = null;
try {
// Block until audio is returned
audio = synthesizer.call("How is the weather today?");
} catch (Exception e) {
throw new RuntimeException(e);
} finally {
// Close the WebSocket connection when the task is done
synthesizer.getDuplexApi().close(1000, "bye");
}
if (audio != null) {
// Save the audio data to the local file "output.mp3"
File file = new File("output.mp3");
// The first text submission requires establishing a WebSocket connection, so the first-packet latency includes connection setup time
// Note: getFirstPackageDelay() requires dashscope-sdk-java 2.18.0 or later
System.out.println(
"[Metric] requestId: "
+ synthesizer.getLastRequestId()
+ ", first-packet latency (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) {
// The following is the configuration for the Singapore region. Replace "{WorkspaceId}" with your actual workspace ID. The configuration varies by region.
Constants.baseWebsocketApiUrl = "wss://{WorkspaceId}.ap-southeast-1.maas.aliyuncs.com/api-ws/v1/inference";
streamAudioDataToSpeaker();
System.exit(0);
}
}
Qwen-TTS
Este exemplo sintetiza fala utilizando uma voz do sistema (consulte as Vozes suportadas).
Para habilitar o recurso de Controle por instruções, substitua model por qwen3-tts-instruct-flash-realtime e configure as instruções usando o parâmetro instructions.
Python
Server commit mode
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():
"""
Set your DashScope API key. More information:
https://github.com/aliyun/alibabacloud-bailian-speech-demo/blob/master/PREREQUISITES.md
"""
# API keys differ between the Singapore and Beijing regions. Get an API key: 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'] # Load API key from environment variable DASHSCOPE_API_KEY
else:
dashscope.api_key = 'your-dashscope-api-key' # Set API key manually
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(
# To use instruction control, replace the model with qwen3-tts-instruct-flash-realtime
model='qwen3-tts-flash-realtime',
callback=callback,
# Singapore region
url='wss://dashscope-intl.aliyuncs.com/api-ws/v1/realtime'
)
qwen_tts_realtime.connect()
qwen_tts_realtime.update_session(
voice = 'Cherry',
response_format = AudioFormat.PCM_24000HZ_MONO_16BIT,
# To use instruction control, uncomment the following lines and replace the model with 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(),
))
Commit mode
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():
"""
Set your DashScope API key. More information:
https://github.com/aliyun/alibabacloud-bailian-speech-demo/blob/master/PREREQUISITES.md
"""
# API keys differ between the Singapore and Beijing regions. Get an API key: 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'] # Load API key from environment variable DASHSCOPE_API_KEY
else:
dashscope.api_key = 'your-dashscope-api-key' # Set API key manually
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(
# To use instruction control, replace the model with qwen3-tts-instruct-flash-realtime
model='qwen3-tts-flash-realtime',
callback=callback,
# Singapore region
url='wss://dashscope-intl.aliyuncs.com/api-ws/v1/realtime'
)
qwen_tts_realtime.connect()
qwen_tts_realtime.update_session(
voice = 'Cherry',
response_format = AudioFormat.PCM_24000HZ_MONO_16BIT,
# To use instruction control, uncomment the following lines and replace the model with 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
Modo 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;
// Real-time PCM audio player
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();
// Initialize the audio format and audio line.
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);
// Write audio data to 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();
}
// Play an audio chunk and block until playback completes.
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);
// Wait for the buffered audio to finish playing.
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();
// Save the complete audio file.
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()
// To use instruction control, replace the model with qwen3-tts-instruct-flash-realtime.
.model("qwen3-tts-flash-realtime")
// China (Beijing) region
.url("wss://dashscope-intl.aliyuncs.com/api-ws/v1/realtime")
// API keys differ between Singapore and China (Beijing). See 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);
// Create a real-time audio player instance.
RealtimePcmPlayer audioPlayer = new RealtimePcmPlayer(24000);
QwenTtsRealtime qwenTtsRealtime = new QwenTtsRealtime(param, new QwenTtsRealtimeCallback() {
@Override
public void onOpen() {
// Handle connection establishment.
}
@Override
public void onEvent(JsonObject message) {
String type = message.get("type").getAsString();
switch(type) {
case "session.created":
// Handle session creation.
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();
// Play audio in real time.
audioPlayer.write(recvAudioB64);
break;
case "response.done":
// Handle response completion.
break;
case "session.finished":
// Handle session termination.
completeLatch.get().countDown();
default:
break;
}
}
@Override
public void onClose(int code, String reason) {
// Handle connection closure.
}
});
qwenTtsRef.set(qwenTtsRealtime);
try {
qwenTtsRealtime.connect();
} catch (NoApiKeyException e) {
throw new RuntimeException(e);
}
QwenTtsRealtimeConfig config = QwenTtsRealtimeConfig.builder()
.voice("Cherry")
.responseFormat(ttsFormat)
.mode("server_commit")
// To use instruction control, uncomment the following lines and replace the model with 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();
// Wait for audio playback to complete, then shut down the player.
audioPlayer.waitForComplete();
audioPlayer.shutdown();
System.exit(0);
}
}
Modo 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;
// Real-time PCM audio player
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();
// Initialize the audio format and audio line.
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);
// Write audio data to 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();
}
// Play an audio chunk and block until playback completes.
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);
// Wait for the buffered audio to finish playing.
Thread.sleep(audioLength - 10);
}
public void write(String b64Audio) {
b64AudioBuffer.add(b64Audio);
}
public void cancel() {
b64AudioBuffer.clear();
RawAudioBuffer.clear();
}
public void waitForComplete() throws InterruptedException {
// Wait for all buffered audio data to finish playing.
while (!b64AudioBuffer.isEmpty() || !RawAudioBuffer.isEmpty()) {
Thread.sleep(100);
}
// Wait for the audio line to drain.
line.drain();
}
public void shutdown() throws InterruptedException {
stopped.set(true);
decoderThread.join();
playerThread.join();
// Save the complete audio file.
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()
// To use instruction control, replace the model with qwen3-tts-instruct-flash-realtime.
.model("qwen3-tts-flash-realtime")
// China (Beijing) region
.url("wss://dashscope-intl.aliyuncs.com/api-ws/v1/realtime")
// API keys differ between Singapore and China (Beijing). See 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));
// Create a real-time player instance.
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);
// Play audio in real time.
audioPlayer.write(recvAudioB64);
break;
case "response.done":
System.out.println("response done");
// Wait for audio playback to complete.
try {
audioPlayer.waitForComplete();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
// Prepare for the next input.
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 {
// Wait for playback to complete, then shut down the player.
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")
// To use instruction control, uncomment the following lines and replace the model with qwen3-tts-instruct-flash-realtime.
// .instructions("")
// .optimizeInstructions(true)
.build();
qwenTtsRealtime.updateSession(config);
// Read user input in a loop.
while (true) {
System.out.print("Enter the text to synthesize: ");
String text = scanner.nextLine();
// Exit when the user enters 'quit'.
if ("quit".equalsIgnoreCase(text.trim())) {
System.out.println("Closing the connection...");
qwenTtsRealtime.finish();
completeLatch.get().await();
break;
}
// Skip empty input.
if (text.trim().isEmpty()) {
continue;
}
// Re-initialize the countdown latch.
completeLatch.set(new CountDownLatch(1));
// Send the text.
qwenTtsRealtime.appendText(text);
qwenTtsRealtime.commit();
// Wait for the current synthesis to complete.
completeLatch.get().await();
}
// Clean up resources.
audioPlayer.waitForComplete();
audioPlayer.shutdown();
scanner.close();
System.exit(0);
}
}
Configuração de sessão
Modos de interação do Qwen-TTS
A API em tempo real do Qwen-TTS oferece dois modos de interação:
Modo server_commit: O servidor gerencia automaticamente a segmentação de texto e o tempo de síntese. Ideal para síntese contínua de grandes blocos de texto, pois o cliente apenas adiciona o conteúdo sem precisar controlar a segmentação ou o envio.
Modo commit: O cliente envia explicitamente o buffer de texto para acionar a síntese. Recomendado para cenários que exigem controle preciso sobre o momento da síntese, como interações por turno em IA conversacional.
Alternar entre os modos de interação:
-
WebSocket: Defina o campo
modeno eventosession.update.{ "type": "session.update", "session": { "mode": "server_commit" } } -
SDK Python: Configure o parâmetro
modeno métodoupdate_session.qwen_tts_realtime.update_session( voice='Cherry', response_format=AudioFormat.PCM_24000HZ_MONO_16BIT, mode='server_commit' ) -
SDK Java: Especifique o parâmetro
modepor meio deQwenTtsRealtimeConfig.builder().QwenTtsRealtimeConfig config = QwenTtsRealtimeConfig.builder() .voice("Cherry") .responseFormat(ttsFormat) .mode("server_commit") .build(); qwenTtsRealtime.updateSession(config);
Para exemplos completos de código dos SDKs, consulte SDK Python e SDK Java. Para detalhes sobre o ciclo de vida de eventos WebSocket e reutilização de conexões, acesse a Referência da API WebSocket.
Recursos avançados
Controle por instruções
O controle por instruções utiliza descrições em linguagem natural para ajustar o tom, a velocidade, a emoção e as características do timbre da fala, sem a necessidade de configurar parâmetros de áudio complexos.
Especificações de instruções por modelo:
Qwen-Audio-TTS
Modelos suportados: qwen-audio-3.0-tts-plus, qwen-audio-3.0-tts-flash
Vozes do sistema e vozes clonadas: aceitam qualquer instrução.
CosyVoice
Modelos suportados: cosyvoice-v3.5-plus, cosyvoice-v3.5-flash, cosyvoice-v3-plus, cosyvoice-v3-flash
Os requisitos de formato de instrução variam conforme o modelo:
-
cosyvoice-v3.5-plus, cosyvoice-v3.5-flash:
Vozes clonadas ou projetadas: aceitam qualquer instrução.
Vozes do sistema: a versão v3.5 não oferece suporte a vozes do sistema.
-
cosyvoice-v3-plus:
Vozes clonadas ou projetadas: não oferecem suporte ao controle por instruções.
Vozes do sistema: as instruções devem seguir um formato e conteúdo fixos. Consulte Lista de vozes do CosyVoice.
-
cosyvoice-v3-flash:
Vozes clonadas ou projetadas: aceitam qualquer instrução.
Vozes do sistema: as instruções devem seguir um formato e conteúdo fixos. Consulte Lista de vozes do CosyVoice.
Uso: Especifique o conteúdo da instrução por meio do parâmetro instruction.
Idiomas suportados para o texto da instrução:
-
cosyvoice-v3.5-plus, cosyvoice-v3.5-flash:
Vozes clonadas ou projetadas: chinês, inglês, francês, alemão, japonês, coreano, russo, português, tailandês, indonésio e vietnamita.
Vozes do sistema: a versão v3.5 não oferece suporte a vozes do sistema.
-
cosyvoice-v3-plus:
Vozes clonadas ou projetadas: chinês, inglês, francês, alemão, japonês, coreano e russo.
Vozes do sistema: as instruções devem seguir um formato e conteúdo fixos. Consulte Lista de vozes do CosyVoice.
-
cosyvoice-v3-flash:
Vozes clonadas ou projetadas: chinês, inglês, francês, alemão, japonês, coreano e russo.
Vozes do sistema: apenas chinês.
Limite de comprimento do texto da instrução: máximo de 100 caracteres. Caracteres chineses (incluindo chinês simplificado/tradicional, kanji japonês e hanja coreano) contam como 2 caracteres cada. Todos os outros caracteres (como pontuação, letras, dígitos, kana japonês e hangul coreano) contam como 1 caractere cada.
Qwen-TTS
Modelos suportados: Apenas modelos da série Qwen3-TTS-Instruct-Flash-Realtime.
Uso: Especifique o conteúdo da instrução por meio do parâmetro instructions.
Idiomas suportados para o texto da instrução: Apenas chinês e inglês.
Limite de comprimento do texto da instrução: Máximo de 1.600 tokens.
Casos de uso:
Narração de audiolivros e radionovelas
Locução para publicidade e vídeos promocionais
Dublagem de personagens de jogos e animações
Assistentes de voz com expressividade emocional
Narração de documentários e telejornais
Como escrever descrições de voz eficazes:
-
Princípios fundamentais:
Seja específico, não vago: Utilize termos que descrevam qualidades vocais, como "profundo", "nítido" ou "ritmo levemente rápido". Evite palavras subjetivas ou imprecisas como "bonito" ou "normal".
Aborde múltiplas dimensões: Uma boa descrição geralmente abrange vários aspectos (como gênero, idade e emoção). Escrever apenas "voz feminina" é muito genérico para gerar um timbre distinto.
Mantenha a objetividade: Concentre-se nas características físicas e perceptivas da voz. Por exemplo, use "tom mais agudo e enérgico" em vez de "minha voz favorita".
Priorize a originalidade: Descreva as qualidades vocais em vez de solicitar a imitação de pessoas específicas (como celebridades ou atores). O modelo não suporta imitações e isso pode acarretar riscos de direitos autorais.
Seja conciso: Garanta que cada palavra tenha um propósito. Evite sinônimos repetitivos ou modificadores sem significado.
-
Referência de dimensões descritivas:
Combine as dimensões abaixo para descrever uma voz. Quanto mais dimensões você incluir, mais precisa será a saída.
Dimensão
Exemplos de descrições
Gênero
Masculino, feminino, andrógino
Idade
Criança (5-12), adolescente (13-18), jovem adulto (19-35), meia-idade (36-55), idoso (55+)
Tom
Agudo, médio, grave, levemente agudo, levemente grave
Velocidade
Rápida, moderada, lenta, levemente rápida, levemente lenta
Emoção
Alegre, calmo, gentil, sério, animado, sereno, suave
Características
Magnético, nítido, rouco, aveludado, doce, encorpado, poderoso
Caso de uso
Telejornal, locução publicitária, audiolivro, personagem de animação, assistente de voz, narração de documentário
-
Exemplos:
Estilo de locução padrão: articulação clara e precisa com pronúncia perfeita
Uma voz feminina jovem e animada, com ritmo acelerado e entonação ascendente marcante, ideal para apresentações de produtos de moda
Um homem de meia-idade calmo, ritmo lento, voz profunda e magnética, adequado para leitura de notícias ou narração de documentários
Uma mulher intelectual e gentil, de cerca de 30 anos, com tom uniforme, apropriada para narração de audiolivros
Uma voz infantil fofa, aproximadamente de uma menina de 8 anos, falando com um leve tom pueril, adequada para dublagem de personagens de animação
Dialetos
Esta seção descreve como produzir fala em dialetos chineses (como o dialeto de Henan, dialeto de Sichuan e cantonês). Os métodos de configuração variam conforme o modelo e o tipo de voz.
Configuração de dialetos por modelo:
Qwen-Audio-TTS
-
Vozes do sistema: Selecione um dos seguintes tipos de voz:
Uma voz do sistema com suporte nativo a dialetos, que gera o dialeto correspondente sem configurações adicionais.
Uma voz compatível com Controle por instruções, configurável para gerar um dialeto específico por meio de texto de instrução.
Vozes clonadas: Configure através do recurso de Controle por instruções. Por exemplo, defina o texto da instrução como
请用河南话表达.
Dialetos suportados: Consulte a coluna "Idiomas suportados" de cada modelo em Qwen-Audio-TTS.
CosyVoice
-
Vozes do sistema: Escolha um dos seguintes tipos de voz na Lista de vozes do CosyVoice:
Uma voz do sistema com suporte nativo a dialetos (como
longshange_v3), que gera o dialeto correspondente sem configurações adicionais.Uma voz compatível com Controle por instruções e configurável para gerar um dialeto específico (como
longanhuan_v3), definido via texto de instrução.
Vozes clonadas: Configure através do recurso de Controle por instruções. Por exemplo, defina o texto da instrução como
请用河南话表达.Vozes projetadas: não oferecem suporte a dialetos.
Dialetos suportados: Consulte a coluna "Idiomas suportados" de cada modelo em CosyVoice.
Exemplo: Utilize o modelo cosyvoice-v3-flash com a voz longanhuan_v3 e defina o texto da instrução como "请用河南话表达。" para gerar fala no dialeto de Henan.
# coding=utf-8
import os
import dashscope
from dashscope.audio.tts_v2 import *
# The API Key differs between the Singapore and Beijing regions. Get your API Key: https://www.alibabacloud.com/help/zh/model-studio/get-api-key
# If you have not configured the environment variable, replace the next line with your Chinese Model Studio API Key: dashscope.api_key = "sk-xxx"
dashscope.api_key = os.environ.get('DASHSCOPE_API_KEY')
# The following is the configuration for the Singapore region. Replace "{WorkspaceId}" with your actual workspace ID. The configuration varies by region.
dashscope.base_websocket_api_url='wss://{WorkspaceId}.ap-southeast-1.maas.aliyuncs.com/api-ws/v1/inference'
# Model
# Different model versions require their corresponding voices:
# cosyvoice-v3-flash/cosyvoice-v3-plus: Use voices such as longanyang.
# cosyvoice-v2: Use voices such as longxiaochun_v2.
# Select the appropriate voice for your target language
model = "cosyvoice-v3-flash"
# Voice
voice = "longanhuan_v3"
# Instantiate SpeechSynthesizer and pass request parameters such as model and voice in the constructor
synthesizer = SpeechSynthesizer(model=model, voice=voice, instruction="请用河南话表达。")
# Send the text to be synthesized and get the binary audio
audio = synthesizer.call("叫你去买盐,你买回来一袋面,这不是弄啥嘞吗!")
# The first text submission requires establishing a WebSocket connection, so the first-packet latency includes connection setup time
print('[Metric] requestId: {}, first-packet latency: {} ms'.format(
synthesizer.get_last_request_id(),
synthesizer.get_first_package_delay()))
# Save the audio to a local file
with open('output.mp3', 'wb') as f:
f.write(audio)
Qwen-TTS
Vozes do sistema: Utilize uma voz do sistema com suporte nativo a dialetos. Consulte a lista de vozes do Qwen-TTS em Vozes suportadas.
Vozes clonadas: não oferecem suporte a dialetos.
Vozes projetadas: não oferecem suporte a dialetos.
Dialetos suportados: Consulte a coluna "Idiomas suportados" de cada modelo em Qwen3-TTS.
Tags de emoção e linguagem rica
Os modelos da série Qwen-Audio-TTS permitem incorporar tags de emoção e linguagem rica diretamente no texto a ser sintetizado (parâmetro text). Essas tags controlam a expressão emocional ou inserem efeitos vocais (como risadas e suspiros) em posições específicas, produzindo uma fala mais expressiva sem a necessidade de configurar parâmetros de áudio complexos.
Modelos suportados: Apenas qwen-audio-3.0-tts-plus e qwen-audio-3.0-tts-flash.
Limitação: Somente o modo de streaming unidirecional é suportado.
Tags de controle
As tags de controle definem a emoção ou o estilo da fala. Insira uma tag no texto para afetar todo o conteúdo subsequente até que outra tag de controle apareça ou a frase seja segmentada automaticamente devido ao seu tamanho.
|
Tag |
Descrição |
|
|
Triste |
|
|
Espantado |
|
|
Grito profundo e alto |
|
|
Trêmulo |
|
|
Com raiva |
|
|
Empolgado |
|
|
Sarcástico |
|
|
Curioso |
|
|
Estilo Drácula (profundo e sombrio) |
|
|
Entediado |
|
|
Cansado |
|
|
Cantando |
|
|
Desdenhoso |
|
|
Gritando |
|
|
Sussurro suave ASMR |
|
|
Em pânico |
|
|
Malicioso |
|
|
Empático |
|
|
Sussurro |
|
|
Relutante |
|
|
Chorando |
|
|
Sério |
|
|
Fala muito lenta |
|
|
Fala muito rápida |
Tags de linguagem rica
As tags de linguagem rica inserem um efeito vocal na posição atual do texto, sem alterar o estilo emocional do conteúdo ao redor.
|
Tag |
Descrição |
|
|
Arquejo |
|
|
Suspiro |
|
|
Limpeza de garganta |
|
|
Risadinha |
|
|
Risada |
|
|
Tosse |
|
|
Bufada |
Exemplos de uso
O exemplo a seguir demonstra como combinar tags de controle e tags de linguagem rica no parâmetro text:
[excited]What a beautiful day today![laughing]Let's go out and have fun together!
Neste texto, [excited] é uma tag de controle que aplica emoção de empolgação a todo o texto subsequente. Já [laughing] é uma tag de linguagem rica que insere uma risada nessa posição antes de continuar a síntese do restante do texto.
Também é possível alternar entre diferentes emoções no mesmo texto:
[serious]Please pay attention to the safety precautions.[excited]Alright, let's get started now!
Aqui, [serious] define um tom sério para a primeira frase, enquanto [excited] muda para um tom empolgado a partir da segunda frase.
Chamadas diretas ao protocolo WebSocket
Os exemplos a seguir demonstram como se conectar diretamente ao servidor usando o protocolo WebSocket nativo, ideal para cenários em que o DashScope SDK não está disponível. Estas são implementações mínimas e executáveis. Para obter detalhes sobre o protocolo WebSocket, consulte a referência da API de cada modelo.
Aplicação em produção
Reutilização de conexão (WebSocket)
As conexões WebSocket são reutilizáveis: após a conclusão de uma tarefa de síntese, é possível iniciar a próxima tarefa na mesma conexão sem precisar restabelecê-la.
Processo de reutilização:
Qwen-Audio-TTS / Qwen-Audio-TTS/CosyVoice: O cliente envia
finish-taske, depois que o servidor retornatask-finished, o cliente pode enviarrun-taskpara iniciar uma nova tarefa.Qwen-TTS: O cliente envia
session.finishe, após o servidor retornarsession.finished, o cliente pode criar uma nova sessão para iniciar a próxima tarefa.
Aguarde o servidor retornar o evento de conclusão (
task-finishedousession.finished) antes de iniciar uma nova tarefa.Qwen-Audio-TTS, Qwen-Audio-TTS/CosyVoice exigem um
task_iddiferente para cada tarefa em uma conexão reutilizada.Se uma tarefa falhar, o servidor retorna um evento de erro e fecha a conexão. A conexão não pode ser reutilizada.
Caso nenhuma nova tarefa seja iniciada dentro de 60 segundos após o término da tarefa anterior, a conexão será fechada automaticamente.
Para obter detalhes sobre os eventos de cada modelo, consulte a referência da API correspondente.
Melhores práticas para alta concorrência
O DashScope SDK possui um pool integrado que reutiliza conexões WebSocket e objetos sintetizadores, eliminando a sobrecarga de criá-los e destruí-los repetidamente.
Modelos e regiões suportados
Singapore
Para chamar os seguintes modelos, selecione uma API Key da região de Singapore:
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 (estável, atualmente equivalente a qwen3-tts-instruct-flash-realtime-2026-01-22), qwen3-tts-instruct-flash-realtime-2026-01-22 (snapshot mais recente)
Qwen3-TTS-VD-Realtime: qwen3-tts-vd-realtime-2026-01-15 (snapshot mais recente), qwen3-tts-vd-realtime-2025-12-16 (snapshot)
Qwen3-TTS-VC-Realtime: qwen3-tts-vc-realtime-2026-01-15 (snapshot mais recente), qwen3-tts-vc-realtime-2025-11-27 (snapshot)
Qwen3-TTS-Flash-Realtime: qwen3-tts-flash-realtime (estável, atualmente equivalente a qwen3-tts-flash-realtime-2025-11-27), qwen3-tts-flash-realtime-2025-11-27 (snapshot mais recente), qwen3-tts-flash-realtime-2025-09-18 (snapshot)
China (Beijing)
Para chamar os seguintes modelos, selecione uma API Key da região de Beijing:
Qwen-Audio-TTS: qwen-audio-3.0-tts-plus, qwen-audio-3.0-tts-flash
Qwen-Audio-TTS/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 (estável, atualmente equivalente a qwen3-tts-instruct-flash-realtime-2026-01-22), qwen3-tts-instruct-flash-realtime-2026-01-22 (snapshot mais recente)
Qwen3-TTS-VD-Realtime: qwen3-tts-vd-realtime-2026-01-15 (snapshot mais recente), qwen3-tts-vd-realtime-2025-12-16 (snapshot)
Qwen3-TTS-VC-Realtime: qwen3-tts-vc-realtime-2026-01-15 (snapshot mais recente), qwen3-tts-vc-realtime-2025-11-27 (snapshot)
Qwen3-TTS-Flash-Realtime: qwen3-tts-flash-realtime (estável, atualmente equivalente a qwen3-tts-flash-realtime-2025-11-27), qwen3-tts-flash-realtime-2025-11-27 (snapshot mais recente), qwen3-tts-flash-realtime-2025-09-18 (snapshot)
Qwen-TTS-Realtime: qwen-tts-realtime (estável, atualmente equivalente a qwen-tts-realtime-2025-07-15), qwen-tts-realtime-latest (mais recente, atualmente equivalente a qwen-tts-realtime-2025-07-15), qwen-tts-realtime-2025-07-15 (snapshot)
Vozes suportadas
Diferentes modelos suportam diferentes vozes. Defina o parâmetro de requisição voice com o valor indicado na coluna parâmetro voice da lista de vozes correspondente.
Referência da API
FAQ
P: Como corrigir pronúncia incorreta na síntese de fala? Como controlar a pronúncia de caracteres polifônicos?
Substitua o caractere polifônico por um homófono para corrigir rapidamente o problema de pronúncia.
Use marcação SSML para controlar a pronúncia .
P: Como solucionar áudio mudo ao usar uma voz clonada?
-
Verifique o status da voz
Chame a interface da API de clonagem/design de voz e confirme se o
statusda voz éOK. -
Verifique a consistência da versão do modelo
Certifique-se de que o parâmetro
target_modelusado durante a clonagem da voz corresponda ao parâmetromodelusado para a síntese de fala. Por exemplo:A clonagem usou
cosyvoice-v3-plusA síntese também deve usar
cosyvoice-v3-plus
-
Verifique a qualidade do áudio de origem
Verifique se o áudio de origem usado para clonagem de voz atende aos requisitos em API de clonagem/design de voz:
Duração do áudio: 10-20 segundos
Qualidade de áudio clara
Sem ruído de fundo
-
Verifique os parâmetros da requisição
Confirme se o parâmetro
voicena requisição de síntese de fala está definido com o ID da voz clonada.
P: O que fazer se o áudio sintetizado de uma voz clonada estiver instável ou incompleto?
Se o áudio sintetizado de uma voz clonada apresentar algum dos seguintes problemas:
Reprodução de áudio incompleta, com apenas parte do texto falado
Qualidade de síntese inconsistente
Áudio contém pausas anormais ou segmentos silenciosos
Possível causa: O áudio de origem não atende aos requisitos de qualidade.
Solução: Verifique se o áudio de origem atende aos requisitos no Guia de gravação para clonagem de voz. Grave novamente o áudio seguindo as diretrizes de gravação.
P: Por que a duração real do áudio sintetizado difere da duração mostrada no arquivo WAV?
A síntese de fala usa um mecanismo de streaming que retorna dados à medida que são gerados. A duração no cabeçalho do arquivo WAV salvo é uma estimativa e pode ser imprecisa. Para obter a duração precisa, defina o formato como pcm, aguarde o resultado completo da síntese e, em seguida, adicione o cabeçalho do arquivo WAV manualmente.
P: Por que o arquivo de áudio não reproduz?
Solucione o problema com base no seu cenário:
-
Áudio salvo como arquivo completo (como xx.mp3)
Consistência do formato de áudio: O formato de áudio nos parâmetros da requisição deve corresponder à extensão do arquivo (por exemplo, se o parâmetro for wav, o arquivo deve ser .wav).
Compatibilidade do player: Confirme se o player suporta o formato de áudio e a taxa de amostragem.
-
Reprodução de áudio em streaming
Salve o fluxo de áudio como um arquivo completo e tente reproduzi-lo com um media player. Se o arquivo não reproduzir, consulte o cenário 1 acima.
Se o arquivo reproduzir corretamente, o problema está na implementação da reprodução em streaming. Confirme se o player suporta reprodução em streaming (como ffmpeg, pyaudio, AudioFormat ou MediaSource).
P: Por que a reprodução do áudio está travando?
Solucione o problema seguindo estas etapas:
Verifique a taxa de envio de texto: Certifique-se de que o intervalo de envio seja razoável para evitar que o segmento de áudio anterior termine antes que o próximo texto chegue.
-
Verifique o desempenho da função de callback:
Confirme que não existe lógica de bloqueio na função de callback.
Callbacks são executados na thread WebSocket. Operações de bloqueio afetam a recepção de dados. Escreva os dados de áudio em um buffer separado e processe-os em outra thread.
Verifique a estabilidade da rede: Flutuações na rede podem causar interrupções ou atrasos na transmissão de áudio.
P: Por que a síntese de fala está demorando muito?
Solucione o problema seguindo estas etapas:
-
Verifique os intervalos de entrada
Para síntese em streaming, verifique se o intervalo de envio de texto está muito longo. Intervalos longos aumentam o tempo total de síntese.
-
Analise as métricas de desempenho
Latência do primeiro pacote: normalmente em torno de 500 ms.
RTF (Fator de Tempo Real = tempo total de síntese / duração do áudio): deve ser menor que 1,0.
P: Como restringir uma API key apenas ao service de síntese de fala (isolamento de permissões)?
Crie um novo workspace e conceda acesso apenas a modelos específicos. Isso limita o escopo da API key. Para mais detalhes, consulte Gerenciar workspaces.