Skip to main content
Speech synthesis

Real-time speech synthesis

Converta texto em fala 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 instantaneamente e com baixa latência.
  • Entrada e saída em streaming com baixa latência no primeiro pacote
  • Controle granular 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 taxa de amostragem de até 48 kHz
  • Suporte a Instruction control, que permite controlar a expressividade da fala por meio de instruções em linguagem natural
  • Recursos de Voice cloning e Voice Design para criação de vozes personalizadas
  • Integração com Emotion and rich language tags, possibilitando a inserção de 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 Non-real-time speech synthesis. Para orientações sobre a escolha do modelo, consulte Speech synthesis.

Pré-requisitos

Início rápido

Os exemplos a seguir demonstram a síntese de fala para cada modelo. Para ver mais exemplos e detalhes dos parâmetros, acesse API reference.
  • Qwen-Audio-TTS
  • CosyVoice
  • Qwen-TTS
Este exemplo realiza a síntese de fala utilizando uma voz do sistema.Para aproveitar o recurso Instruction control, 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 longanhuan_v3.6.
# 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 = "longanhuan_v3.6"

# 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)

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. O cliente adiciona texto sem precisar gerenciar 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 a síntese por turno em IA conversacional.
Alternar entre modos de interação:
  • WebSocket: Defina o campo mode no evento session.update.
{
    "type": "session.update",
    "session": {
        "mode": "server_commit"
    }
}
  • SDK Python: Configure o parâmetro mode no método update_session.
qwen_tts_realtime.update_session(
    voice='Cherry',
    response_format=AudioFormat.PCM_24000HZ_MONO_16BIT,
    mode='server_commit'
)
  • SDK Java: Especifique o parâmetro mode por meio de QwenTtsRealtimeConfig.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 Python SDK e Java SDK. Para detalhes sobre o ciclo de vida de eventos WebSocket e reutilização de conexões, veja WebSocket API reference.

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
  • CosyVoice
  • Qwen-TTS
Modelos suportados: qwen-audio-3.0-tts-plus, qwen-audio-3.0-tts-flashVozes do sistema e vozes clonadas: aceitam qualquer instrução.
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:
    1. Seja específico, não vago: Utilize termos que descrevam qualidades vocais, como "profundo", "nítido" ou "ritmo levemente acelerado". Evite palavras subjetivas ou imprecisas como "bonito" ou "normal".
    2. 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" é amplo demais para gerar um timbre distinto.
    3. Mantenha a objetividade: Concentre-se nas características físicas e perceptivas da voz. Por exemplo, use "tom mais agudo com energia" em vez de "minha voz favorita".
    4. 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.
    5. Seja conciso: Garanta que cada palavra tenha um propósito. Evite sinônimos repetitivos ou modificadores sem significado.
  • Referência de dimensões de descrição: 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, potente

    Caso de uso

    Telejornal, locução publicitária, audiolivro, personagem de animação, assistente de voz, narração de documentário

  • Exemplos:
    • Estilo de transmissão padrão: articulação clara e precisa com pronúncia perfeita
    • Uma voz feminina jovem e animada, com ritmo mais rápido e entonação ascendente notável, adequada 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 gentil e intelectual, por volta dos 30 anos, com tom uniforme, adequada para narração de audiolivros
    • Uma voz infantil fofa, aproximadamente de uma menina de 8 anos, falando com uma qualidade levemente 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 dialeto por modelo:
  • Qwen-Audio-TTS
  • CosyVoice
  • Qwen-TTS
  • Vozes do sistema: Selecione um dos seguintes tipos de voz:
    • Uma voz do sistema com suporte nativo a dialeto, que gera o dialeto correspondente sem configuração adicional.
    • Uma voz compatível com Instruction control, configurável para gerar um dialeto específico por meio de texto de instrução.
  • Vozes clonadas: Configure por meio do recurso Instruction control. Por exemplo, defina o texto da instrução como 请用河南话表达.
Dialetos suportados: Consulte a coluna "Idiomas suportados" de cada modelo em Qwen-Audio-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: Apenas 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 comprimento.

Tag

Descrição

[sad]

Triste

[amazed]

Espantado

[deep and loud shouting]

Grito profundo e alto

[trembling]

Trêmulo

[angry]

Com raiva

[excited]

Empolgado

[sarcastic]

Sarcástico

[curious]

Curioso

[like dracula]

Estilo Drácula (profundo, sombrio)

[bored]

Entediado

[tired]

Cansado

[scornful]

Desdenhoso

[shouting]

Gritando

[asmr]

Sussurro suave ASMR

[panicked]

Em pânico

[mischievously]

Malicioso

[empathetic]

Empático

[whispers]

Sussurro

[reluctantly]

Relutante

[crying]

Chorando

[serious]

Sério

[very slowly]

Fala muito lenta

[very fast]

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

[gasp]

Arquejo

[sighing]

Suspiro

[clears throat]

Limpeza de garganta

[giggles]

Risadinha

[laughing]

Risada

[cough]

Tosse

[snorts]

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 naquela 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 a primeira frase com um tom sério, enquanto [excited] alterna para um tom empolgado a partir da segunda frase.

Cancelar tarefa

Caso precise interromper a rodada atual de síntese durante a geração de fala em tempo real, envie um comando de cancelamento. Após o cancelamento, o servidor encerra imediatamente a tarefa atual e retorna um evento de conclusão. É possível iniciar uma nova tarefa de síntese na mesma conexão WebSocket sem precisar reconectar. Uso:
  • Python SDK: Versão 1.26.4 ou posterior, chame SpeechSynthesizer.streaming_cancel().
  • Java SDK: Versão 2.22.26 ou posterior, chame SpeechSynthesizer.streamingCancel().
  • Protocolo bruto WebSocket: Envie um evento finish-task e defina directive=cancel em input.
Limitações de modelo:
  • China (Beijing): Todos os modelos Qwen-Audio-TTS suportam este recurso. Os modelos CosyVoice exigem a versão v2 ou posterior.
  • Singapore: Todos os modelos Qwen-Audio-TTS suportam este recurso. Os modelos CosyVoice não oferecem suporte a este recurso.

Chamadas diretas ao protocolo WebSocket

Os exemplos a seguir demonstram como se conectar diretamente ao servidor usando o protocolo nativo do WebSocket, 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.
  • Qwen-TTS
  1. Crie o cliente
    • Python
    • Java
    Crie um arquivo Python chamado tts_realtime_client.py e copie o código a seguir para o arquivo:
    # -- coding: utf-8 --
    
    import asyncio
    import websockets
    import json
    import base64
    import time
    from typing import Optional, Callable, Dict, Any
    from enum import Enum
    
    class SessionMode(Enum):
        SERVER_COMMIT = "server_commit"
        COMMIT = "commit"
    
    class TTSRealtimeClient:
        """
        Client for interacting with the TTS Realtime API.
    
        This class provides methods for connecting to the TTS Realtime API, sending text data,
        receiving audio output, and managing WebSocket connections.
    
        Attributes:
            base_url (str):
                Base URL of the Realtime API.
            api_key (str):
                API Key for authentication.
            voice (str):
                Voice used for server-side speech synthesis.
            mode (SessionMode):
                Session mode, either server_commit or commit.
            audio_callback (Callable[[bytes], None]):
                Callback function for receiving audio data.
            language_type(str)
                Language for synthesized speech. Options: Chinese, English, German, Italian, Portuguese, Spanish, Japanese, Korean, French, Russian, Auto
        """
    
        def __init__(
                self,
                base_url: str,
                api_key: str,
                voice: str = "Cherry",
                mode: SessionMode = SessionMode.SERVER_COMMIT,
                audio_callback: Optional[Callable[[bytes], None]] = None,
            language_type: str = "Auto"):
            self.base_url = base_url
            self.api_key = api_key
            self.voice = voice
            self.mode = mode
            self.ws = None
            self.audio_callback = audio_callback
            self.language_type = language_type
    
            # Current response state
            self._current_response_id = None
            self._current_item_id = None
            self._is_responding = False
            self._response_done_future = None
    
        async def connect(self) -> None:
            """Establish WebSocket connection with the TTS Realtime API."""
            headers = {
                "Authorization": f"Bearer {self.api_key}"
            }
    
            self.ws = await websockets.connect(self.base_url, additional_headers=headers)
    
            # Set default session configuration
            await self.update_session({
                "mode": self.mode.value,
                "voice": self.voice,
                # To use the instruction control feature, uncomment the lines below and replace the model with qwen3-tts-instruct-flash-realtime in server_commit.py or commit.py
                # "instructions": "Speak quickly with a noticeable rising intonation, suitable for introducing fashion products.",
                # "optimize_instructions": true
                "language_type": self.language_type,
                "response_format": "pcm",
                "sample_rate": 24000
            })
    
        async def send_event(self, event) -> None:
            """Send an event to the server."""
            event['event_id'] = "event_" + str(int(time.time() * 1000))
            print(f"Sending event: type={event['type']}, event_id={event['event_id']}")
            await self.ws.send(json.dumps(event))
    
        async def update_session(self, config: Dict[str, Any]) -> None:
            """Update session configuration."""
            event = {
                "type": "session.update",
                "session": config
            }
            print("Updating session configuration: ", event)
            await self.send_event(event)
    
        async def append_text(self, text: str) -> None:
            """Send text data to the API."""
            event = {
                "type": "input_text_buffer.append",
                "text": text
            }
            await self.send_event(event)
    
        async def commit_text_buffer(self) -> None:
            """Commit text buffer to trigger processing."""
            event = {
                "type": "input_text_buffer.commit"
            }
            await self.send_event(event)
    
        async def clear_text_buffer(self) -> None:
            """Clear the text buffer."""
            event = {
                "type": "input_text_buffer.clear"
            }
            await self.send_event(event)
    
        async def finish_session(self) -> None:
            """End the session."""
            event = {
                "type": "session.finish"
            }
            await self.send_event(event)
    
        async def wait_for_response_done(self):
            """Wait for the response.done event"""
            if self._response_done_future:
                await self._response_done_future
    
        async def handle_messages(self) -> None:
            """Handle messages from the server."""
            try:
                async for message in self.ws:
                    event = json.loads(message)
                    event_type = event.get("type")
    
                    if event_type != "response.audio.delta":
                        print(f"Received event: {event_type}")
    
                    if event_type == "error":
                        print("Error: ", event.get('error', {}))
                        continue
                    elif event_type == "session.created":
                        print("Session created, ID: ", event.get('session', {}).get('id'))
                    elif event_type == "session.updated":
                        print("Session updated, ID: ", event.get('session', {}).get('id'))
                    elif event_type == "input_text_buffer.committed":
                        print("Text buffer committed, item ID: ", event.get('item_id'))
                    elif event_type == "input_text_buffer.cleared":
                        print("Text buffer cleared")
                    elif event_type == "response.created":
                        self._current_response_id = event.get("response", {}).get("id")
                        self._is_responding = True
                        # Create a new future to wait for response.done
                        self._response_done_future = asyncio.Future()
                        print("Response created, ID: ", self._current_response_id)
                    elif event_type == "response.output_item.added":
                        self._current_item_id = event.get("item", {}).get("id")
                        print("Output item added, ID: ", self._current_item_id)
                    # Handle audio delta
                    elif event_type == "response.audio.delta" and self.audio_callback:
                        audio_bytes = base64.b64decode(event.get("delta", ""))
                        self.audio_callback(audio_bytes)
                    elif event_type == "response.audio.done":
                        print("Audio generation completed")
                    elif event_type == "response.done":
                        self._is_responding = False
                        self._current_response_id = None
                        self._current_item_id = None
                        # Mark future as done
                        if self._response_done_future and not self._response_done_future.done():
                            self._response_done_future.set_result(True)
                        print("Response completed")
                    elif event_type == "session.finished":
                        print("Session finished")
    
            except websockets.exceptions.ConnectionClosed:
                print("Connection closed")
            except Exception as e:
                print("Error handling messages: ", str(e))
    
        async def close(self) -> None:
            """Close the WebSocket connection."""
            if self.ws:
                await self.ws.close()
    
  2. Selecione um modo de síntese de fala A Realtime API oferece suporte a dois modos:
    • Modo server_commit O servidor gerencia automaticamente a segmentação de texto e o tempo de síntese. O cliente apenas envia o texto. Ideal para cenários de baixa latência, como navegação GPS.
    • Modo commit O cliente adiciona texto a um buffer e aciona explicitamente a síntese. Recomendado para casos que exigem controle preciso da segmentação de frases, como transmissões de notícias.
    • Modo server_commit
    • Modo commit
    • Python
    • Java
    No mesmo diretório do arquivo tts_realtime_client.py, crie outro arquivo Python chamado server_commit.py e copie o código abaixo para ele:
    import os
    import asyncio
    import logging
    import wave
    from tts_realtime_client import TTSRealtimeClient, SessionMode
    import pyaudio
    
    # QwenTTS service configuration
    # To use the instruction control feature, replace the model with qwen3-tts-instruct-flash-realtime and uncomment instructions in tts_realtime_client.py
    # The following is the configuration for the Singapore region.
    URL = "wss://dashscope-intl.aliyuncs.com/api-ws/v1/realtime?model=qwen3-tts-flash-realtime"
    # 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: API_KEY="sk-xxx"
    API_KEY = os.getenv("DASHSCOPE_API_KEY")
    
    if not API_KEY:
        raise ValueError("Please set DASHSCOPE_API_KEY environment variable")
    
    # Collect audio data
    _audio_chunks = []
    # Real-time playback related
    _AUDIO_SAMPLE_RATE = 24000
    _audio_pyaudio = pyaudio.PyAudio()
    _audio_stream = None  # Will be opened at runtime
    
    def _audio_callback(audio_bytes: bytes):
        """TTSRealtimeClient audio callback: real-time playback and caching"""
        global _audio_stream
        if _audio_stream is not None:
            try:
                _audio_stream.write(audio_bytes)
            except Exception as exc:
                logging.error(f"PyAudio playback error: {exc}")
        _audio_chunks.append(audio_bytes)
        logging.info(f"Received audio chunk: {len(audio_bytes)} bytes")
    
    def _save_audio_to_file(filename: str = "output.wav", sample_rate: int = 24000) -> bool:
        """Save collected audio data as a WAV file"""
        if not _audio_chunks:
            logging.warning("No audio data to save")
            return False
    
        try:
            audio_data = b"".join(_audio_chunks)
            with wave.open(filename, 'wb') as wav_file:
                wav_file.setnchannels(1)  # Mono
                wav_file.setsampwidth(2)  # 16-bit
                wav_file.setframerate(sample_rate)
                wav_file.writeframes(audio_data)
            logging.info(f"Audio saved to: {filename}")
            return True
        except Exception as exc:
            logging.error(f"Failed to save audio: {exc}")
            return False
    
    async def _produce_text(client: TTSRealtimeClient):
        """Send text fragments to the server"""
        text_fragments = [
            "Alibaba Cloud's large language model platform, Model Studio, is an all-in-one platform for developing and building large language model applications.",
            "Both developers and business users can deeply participate in the design and development of large language model applications.",
            "You can develop a large language model application in five minutes using a simple interface,",
            "or train a dedicated model in a few hours, allowing you to focus more energy on application innovation.",
        ]
    
        logging.info("Sending text fragments…")
        for text in text_fragments:
            logging.info(f"Sending fragment: {text}")
            await client.append_text(text)
            await asyncio.sleep(0.1)  # Brief delay between fragments
    
        # Wait for the server to finish internal processing before ending the session
        await asyncio.sleep(1.0)
        await client.finish_session()
    
    async def _run_demo():
        """Run the complete demo"""
        global _audio_stream
        # Open PyAudio output stream
        _audio_stream = _audio_pyaudio.open(
            format=pyaudio.paInt16,
            channels=1,
            rate=_AUDIO_SAMPLE_RATE,
            output=True,
            frames_per_buffer=1024
        )
    
        client = TTSRealtimeClient(
            base_url=URL,
            api_key=API_KEY,
            voice="Cherry",
            mode=SessionMode.SERVER_COMMIT,
            audio_callback=_audio_callback
        )
    
        # Establish connection
        await client.connect()
    
        # Run message handling and text sending in parallel
        consumer_task = asyncio.create_task(client.handle_messages())
        producer_task = asyncio.create_task(_produce_text(client))
    
        await producer_task  # Wait for text sending to complete
    
        # Wait for response.done
        await client.wait_for_response_done()
    
        # Close connection and cancel consumer task
        await client.close()
        consumer_task.cancel()
    
        # Close audio stream
        if _audio_stream is not None:
            _audio_stream.stop_stream()
            _audio_stream.close()
        _audio_pyaudio.terminate()
    
        # Save audio data
        os.makedirs("outputs", exist_ok=True)
        _save_audio_to_file(os.path.join("outputs", "qwen_tts_output.wav"))
    
    def main():
        """Synchronous entry point"""
        logging.basicConfig(
            level=logging.INFO,
            format='%(asctime)s [%(levelname)s] %(message)s',
            datefmt='%Y-%m-%d %H:%M:%S'
        )
        logging.info("Starting QwenTTS Realtime Client demo…")
        asyncio.run(_run_demo())
    
    if __name__ == "__main__":
        main()
    
    Execute o arquivo server_commit.py para ouvir em tempo real o áudio gerado pela Realtime API.

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, inicie 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-task e, após o servidor retornar task-finished, o cliente pode enviar run-task para iniciar uma nova tarefa.
  • Qwen-TTS: O cliente envia session.finish e, depois que o servidor retorna session.finished, o cliente pode criar uma nova sessão para começar a próxima tarefa.
Reutilização após cancelamento: Para Qwen-Audio-TTS / Qwen-Audio-TTS/CosyVoice, caso você cancele a tarefa atual com a diretiva cancel, também é possível enviar um novo run-task na mesma conexão assim que o servidor retornar task-finished. Para mais detalhes, consulte Cancel task.
  1. Aguarde o evento de conclusão do servidor (task-finished ou session.finished) antes de iniciar uma nova tarefa.
  2. Qwen-Audio-TTS, Qwen-Audio-TTS/CosyVoice exigem um task_id diferente para cada tarefa em uma conexão reutilizada.
  3. Se uma tarefa falhar, o servidor retorna um evento de erro e encerra a conexão, impedindo sua reutilização.
  4. Caso nenhuma nova tarefa seja iniciada dentro de 60 segundos após o término da anterior, a conexão será fechada automaticamente.
Para obter detalhes sobre os eventos de cada modelo, consulte o API reference correspondente.

Limites de taxa

As chamadas aos modelos estão sujeitas a limites de taxa. Quando um limite é excedido, o servidor retorna o erro Requests rate limit exceeded, please try again later. Reduza a taxa de requisições ou a concorrência e tente novamente. Para verificar os limites de taxa de cada modelo, consulte Rate limiting.

Melhores práticas para alta concorrência

O DashScope SDK possui pooling integrado que reutiliza conexões WebSocket e objetos sintetizadores, eliminando a sobrecarga de criá-los e destruí-los repetidamente.
  • Qwen-Audio-TTS/CosyVoice
Qwen-Audio-TTS e Qwen-Audio-TTS/CosyVoice compartilham a mesma interface do SDK. Os exemplos abaixo também se aplicam aos modelos Qwen-Audio-TTS — basta substituir os parâmetros model e voice.

Pré-requisitos

  • Python SDK
  • Java SDK
O Python SDK utiliza SpeechSynthesizerObjectPool para gerenciar e reutilizar objetos SpeechSynthesizer.Esse pool cria um número especificado de instâncias SpeechSynthesizer e estabelece conexões WebSocket durante a inicialização. Ao solicitar um objeto emprestado, ele já está pronto para enviar requisições imediatamente, reduzindo a latência do primeiro pacote. Após a devolução do objeto, a conexão permanece ativa para a próxima tarefa.

Etapas de implementação

  1. Instale as dependências: Instale a dependência do DashScope (pip install -U dashscope).
  2. Crie e configure o pool de objetos Defina o tamanho do pool entre 1,5x e 2x a concorrência de pico, sem exceder o limite de QPS da sua conta. Crie um pool singleton global (o estabelecimento da conexão durante a inicialização leva algum tempo):
from dashscope.audio.tts_v2 import SpeechSynthesizerObjectPool

synthesizer_object_pool = SpeechSynthesizerObjectPool(max_size=20)
import dashscope
# The following is the configuration for the China (Beijing) region. Replace "{WorkspaceId}" with your actual workspace ID. The configuration varies by region.
dashscope.base_http_api_url = "https://{WorkspaceId}.cn-beijing.maas.aliyuncs.com/api/v1"
  • No cenário de pool de objetos, SpeechSynthesizerObjectPool estabelece conexões WebSocket com o servidor usando a dashscope.api_key global atual durante a inicialização. A chave de API é gravada no cabeçalho Authorization apenas durante o handshake WebSocket para autenticação. Mensagens subsequentes da tarefa (como run-task) não carregam a chave de API. Modificar dashscope.api_key após a criação do pool não afeta as conexões existentes — objetos obtidos via borrow_synthesizer (incluindo aqueles devolvidos e solicitados novamente) ainda usam a chave de API do handshake original. O novo valor é ignorado silenciosamente, o que pode fazer com que a identidade, cota ou atribuição de faturamento difira do esperado. Nota: borrow_synthesizer não suporta a especificação de uma chave de API como parâmetro.
  • Para usar múltiplas chaves de API, mantenha uma instância separada de SpeechSynthesizerObjectPool para cada chave.
  1. Solicite um objeto SpeechSynthesizer do pool Se a quantidade de objetos não devolvidos exceder a capacidade do pool, o sistema criará objetos adicionais. Esses objetos extras precisam estabelecer novas conexões e não se beneficiam do pooling.
speech_synthesizer = connectionPool.borrow_synthesizer(
    model='cosyvoice-v3-flash',
    voice='longanyang',
    seed=12382,
    callback=synthesizer_callback
)
  1. Execute a síntese de fala Chame o método call ou streaming_call do objeto SpeechSynthesizer para sintetizar a fala.
  2. Devolva o objeto SpeechSynthesizer Devolva o objeto após a conclusão da tarefa para disponibilizá-lo para reutilização. Não devolva objetos com tarefas incompletas ou falhas.
connectionPool.return_synthesizer(speech_synthesizer)
Código completo
Antes de usar este código: SpeechSynthesizerObjectPool estabelece conexões WebSocket e realiza autenticação usando a dashscope.api_key global atual na inicialização. Modificar dashscope.api_key após a criação do pool não afeta as conexões existentes — o novo valor é ignorado silenciosamente. Para múltiplas chaves de API, mantenha uma instância de pool separada para cada chave. Para mais detalhes, consulte a nota importante acima.
# !/usr/bin/env python3
# Copyright (C) Alibaba Group. All Rights Reserved.
# MIT License (https://opensource.org/licenses/MIT)

import os
import time
import threading

import dashscope
from dashscope.audio.tts_v2 import *

USE_CONNECTION_POOL = True
text_to_synthesize = [
    'Sentence 1: Welcome to Alibaba speech synthesis service.',
    'Sentence 2: Welcome to Alibaba speech synthesis service.',
    'Sentence 3: Welcome to Alibaba speech synthesis service.',
]
connectionPool = None

def init_dashscope_api_key():
    '''
    Set your DashScope API-key. More information:
    https://github.com/aliyun/alibabacloud-bailian-speech-demo/blob/master/PREREQUISITES.md
    '''
    # 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 '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

def synthesis_text_to_speech_and_play_by_streaming_mode(text, task_id):
    global USE_CONNECTION_POOL, connectionPool
    '''
    Synthesize speech with given text by streaming mode, async call and play the synthesized audio in real-time.
    for more information, please refer to https://www.alibabacloud.com/help/document_detail/2712523.html
    '''

    complete_event = threading.Event()

    # Define a callback to handle the result

    class Callback(ResultCallback):
        def on_open(self):
            # when using object pool, on_open will be called after task start
            self.file = open(f'result_{task_id}.mp3', 'wb')
            print(f'[task_{task_id}] start')

        def on_complete(self):
            print(f'[task_{task_id}] speech synthesis task complete successfully.')
            complete_event.set()

        def on_error(self, message: str):
            print(f'[task_{task_id}] speech synthesis task failed, {message}')

        def on_close(self):
            # when using object pool, on_open will be called after task finished
            print(f'[task_{task_id}] finished')

        def on_event(self, message):
            # print(f'recv speech synthsis message {message}')
            pass

        def on_data(self, data: bytes) -> None:
            # send to player
            # save audio to file
            self.file.write(data)

    # Call the speech synthesizer callback
    synthesizer_callback = Callback()

    # Initialize the speech synthesizer
    # you can customize the synthesis parameters, like voice, format, sample_rate or other parameters
    if USE_CONNECTION_POOL:
        speech_synthesizer = connectionPool.borrow_synthesizer(
            model='cosyvoice-v3-flash',
            voice='longanyang',
            seed=12382,
            callback=synthesizer_callback
        )
    else:
        speech_synthesizer = SpeechSynthesizer(model='cosyvoice-v3-flash',
                                               voice='longanyang',
                                               seed=12382,
                                               callback=synthesizer_callback)
    try:
        speech_synthesizer.call(text)
    except Exception as e:
        print(f'[task_{task_id}] speech synthesis task failed, {e}')
        if USE_CONNECTION_POOL:
            # close the synthesizer connection manually if task failed when using connection pool.
            speech_synthesizer.close()
        return

    print('[task_{}] Synthesized text: {}'.format(task_id, text))
    complete_event.wait()
    print('[task_{}][Metric] requestId: {}, first package delay ms: {}'.format(
        task_id,
        speech_synthesizer.get_last_request_id(),
        speech_synthesizer.get_first_package_delay()))
    if USE_CONNECTION_POOL:
        connectionPool.return_synthesizer(speech_synthesizer)

# main function
if __name__ == '__main__':
    # You must set dashscope.api_key and base_websocket_api_url before creating SpeechSynthesizerObjectPool.
    # The pool establishes WebSocket connections using the current global dashscope.api_key at initialization time.
    # Modifying dashscope.api_key after pool creation will not affect existing connections in the pool.
    # 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'
    init_dashscope_api_key()

    if USE_CONNECTION_POOL:
        print('creating connection pool')
        start_time = time.time() * 1000
        connectionPool = SpeechSynthesizerObjectPool(max_size=3)
        end_time = time.time() * 1000
        print('connection pool created, cost: {} ms'.format(end_time - start_time))

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

    for task_thread in task_thread_list:
        task_thread.start()

    for task_thread in task_thread_list:
        task_thread.join()

    if USE_CONNECTION_POOL:
        connectionPool.shutdown()

Gerenciamento de recursos e tratamento de erros

  • Tarefa bem-sucedida: Após a conclusão normal de uma tarefa de síntese, chame connectionPool.return_synthesizer(speech_synthesizer) para devolver o objeto SpeechSynthesizer ao pool para reutilização.
    Não devolva objetos SpeechSynthesizer com tarefas incompletas ou falhas.
  • Falha na tarefa: Se um erro interno do SDK ou uma exceção de lógica de negócio causar a interrupção da tarefa, feche a conexão WebSocket subjacente: speech_synthesizer.close()
  • Após a conclusão de todas as tarefas de síntese, encerre o pool: connectionPool.shutdown()
  • Quando ocorrer um erro TaskFailed no lado do servidor, nenhum tratamento adicional é necessário.

Modelos e regiões suportados

  • Singapore
  • China (Beijing)
Para chamar os modelos a seguir, 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)

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?

  1. Verifique o status da voz Chame a interface Voice cloning/design API e confirme se o status da voz é OK.
  2. Verifique a consistência da versão do modelo Certifique-se de que o parâmetro target_model usado durante a clonagem de voz corresponda ao parâmetro model usado para a síntese de fala. Por exemplo:
    • A clonagem utilizou cosyvoice-v3-plus
    • A síntese também deve utilizar cosyvoice-v3-plus
  3. Verifique a qualidade do áudio de origem Confira se o áudio de origem usado para clonagem de voz atende aos requisitos em Voice cloning/design API:
    • Duração do áudio: 10-20 segundos
    • Qualidade de áudio nítida
    • Sem ruído de fundo
  4. Verifique os parâmetros da requisição Confirme se o parâmetro voice na 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 problemas a seguir:
  • 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 em Recording guide for voice cloning. Regrave 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 utiliza 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 exata, defina o formato como pcm, aguarde o resultado completo da síntese e adicione o cabeçalho do arquivo WAV manualmente.

P: Por que o arquivo de áudio não reproduz?

Faça a solução de problemas com base no seu cenário:
  1. Áudio salvo como arquivo completo (como xx.mp3)
    1. 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).
    2. Compatibilidade do player: Confirme se o player suporta o formato de áudio e a taxa de amostragem.
  2. Reprodução de áudio em streaming
    1. 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.
    2. 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?

Faça a solução de problemas seguindo estas etapas:
  1. 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 da chegada do próximo texto.
  2. Verifique o desempenho da função de callback:
    • Confirme que não existe lógica de bloqueio na função de callback.
    • Os callbacks são executados na thread WebSocket. Operações de bloqueio afetam o recebimento de dados. Escreva os dados de áudio em um buffer separado e processe-os em outra thread.
  3. 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?

Faça a solução de problemas seguindo estas etapas:
  1. 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.
  2. 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 Manage workspaces.
Plano de Tokens
Playground de Modelos
Inferência do Modelo
Avaliação
Compressão de Modelos
Estatísticas e Monitoramento
Suporte