Todos os produtos
Search
Central de documentação

Alibaba Cloud Model Studio:Qwen-Audio-TTS/CosyVoice speech synthesis Python SDK

Última atualização: Jul 14, 2026

Utilize o DashScope Python SDK para integrar a síntese de fala em tempo real do Qwen-Audio-TTS/CosyVoice à sua aplicação por meio dos modos sem streaming, streaming unidirecional ou streaming bidirecional.

Guia do usuário: Para descrições de modelos e recomendações de seleção, consulte Síntese de fala.

Endpoint do service

O SDK utiliza o endpoint da região Beijing por padrão. Para alterar para outra região, modifique dashscope.base_websocket_api_url antes da inicialização.

Singapore

wss://{WorkspaceId}.ap-southeast-1.maas.aliyuncs.com/api-ws/v1/inference

Substitua {WorkspaceId} pelo seu ID do workspace real.

China (Beijing)

wss://{WorkspaceId}.cn-beijing.maas.aliyuncs.com/api-ws/v1/inference

Substitua {WorkspaceId} pelo seu ID do workspace real.

Alternar para a região Singapore:

import dashscope

# Set at the beginning of your code
dashscope.base_websocket_api_url = 'wss://{WorkspaceId}.ap-southeast-1.maas.aliyuncs.com/api-ws/v1/inference'
Importante

O Alibaba Cloud Model Studio lançou domínios específicos por workspace para as regiões China (Beijing) e Singapore. Os novos domínios dedicados oferecem desempenho superior e maior estabilidade para solicitações de inferência. Recomendamos migrar para os novos domínios:

  • China (Beijing): de dashscope.aliyuncs.com para {WorkspaceId}.cn-beijing.maas.aliyuncs.com

  • Singapore: de dashscope-intl.aliyuncs.com para {WorkspaceId}.ap-southeast-1.maas.aliyuncs.com

Substitua {WorkspaceId} pelo seu ID do Workspace real. Os domínios existentes permanecem totalmente funcionais.

SpeechSynthesizer

Caminho do pacote: dashscope.audio.tts_v2.SpeechSynthesizer

Construtor

SpeechSynthesizer(
    model: str,
    voice: str,
    format: AudioFormat = AudioFormat.MP3_22050HZ_MONO_256KBPS,
    volume: int = 50,
    speech_rate: float = 1.0,
    pitch_rate: float = 1.0,
    callback: ResultCallback = None)

call() - sem streaming

Assinatura do método:

def call(self, text: str) -> bytes

Parâmetros:

Parâmetro

Tipo

Obrigatório

Descrição

text

str

Sim

O texto completo a ser sintetizado. Comprimento máximo: 20.000 caracteres.

Valor de retorno: bytes contendo os dados de áudio completos.

Descrição: Esta chamada bloqueante retorna todos os dados de áudio de uma só vez. É mais adequada para textos curtos que não exigem streaming em tempo real. Reinicialize a instância SpeechSynthesizer antes de cada chamada.

streaming_call() - com streaming

Assinatura do método:

def streaming_call(self, text: str) -> None

Parâmetros:

Parâmetro

Tipo

Obrigatório

Descrição

text

str

Sim

Um segmento de texto para sintetizar. Chame este método várias vezes para anexar texto. Máximo por chamada: 20.000 caracteres. Máximo acumulado: 200.000 caracteres.

Descrição: Esta chamada de streaming bidirecional aceita texto em segmentos e entrega o áudio sintetizado por meio de callbacks em tempo real. É ideal para integração com grandes modelos de linguagem, onde o texto é gerado progressivamente. Chame streaming_complete() após enviar todo o texto.

streaming_complete() - finalizar streaming

Assinatura do método:

def streaming_complete(self) -> None

Descrição: Notifica o servidor de que todo o texto foi enviado. Bloqueia a thread atual até que o texto restante seja sintetizado e todos os dados de áudio sejam retornados. A falha ao chamar este método pode resultar na não conversão do texto final em fala.

get_last_request_id() - obter ID da solicitação

Assinatura do método:

def get_last_request_id(self) -> str

Valor de retorno: str contendo o ID da solicitação mais recente. Utilize-o para solução de problemas e rastreamento.

get_first_package_delay() - obter latência do primeiro pacote

Assinatura do método:

def get_first_package_delay(self) -> int

Valor de retorno: int representando o atraso, em milissegundos, entre o envio do texto e o recebimento do primeiro fragmento de áudio. Chame este método após a conclusão da síntese.

get_response() - obter mensagem de resposta

Assinatura do método:

def get_response(self) -> str

Valor de retorno: str contendo a mensagem de resposta formatada em JSON da tarefa de síntese mais recente, incluindo o status da solicitação e informações de saída.

Parâmetros do construtor

Os parâmetros a seguir são definidos por meio do construtor SpeechSynthesizer para controlar o modelo, a voz, o formato e as características do áudio.

Parâmetro

Tipo

Obrigatório

Descrição

model

str

Sim

O nome do modelo.

voice

str

Sim

voice string (obrigatório)

A voz utilizada para a síntese de fala.

  • Vozes do sistema: Consulte a Lista de vozes CosyVoice

  • Vozes clonadas: Vozes personalizadas criadas por meio de clonagem de voz

  • Vozes personalizadas: Vozes personalizadas criadas por meio de design de voz

format

enum

Não

Formato de codificação de áudio e taxa de amostragem.

Padrão: AudioFormat.MP3_22050HZ_MONO_256KBPS.

O enum AudioFormat está localizado em dashscope.audio.tts_v2 e suporta MP3, WAV, PCM e outros formatos.

volume

int

Não

O nível de volume.

Valor padrão: 50.

Valores válidos: [0, 100].

speech_rate

float

Não

A velocidade da fala.

Valor padrão: 1,0.

Valores válidos: [0,5, 2,0].

pitch_rate

float

Não

O tom da voz.

Valor padrão: 1,0.

Valores válidos: [0,5, 2,0].

bit_rate

int

Não

A taxa de bits de áudio em kbps. Quando o formato de áudio for opus, utilize bit_rate para ajustar a taxa de bits.

Valor padrão: 32.

Valores válidos: [6, 510].

Nota

Defina bit_rate por meio do parâmetro additional_params:

synthesizer = SpeechSynthesizer(
              model="qwen-audio-3.0-tts-flash",
              voice="longanlingxi",
              additional_params={"bit_rate": 128000}
          )

word_timestamp_enabled

bool

Não

Especifica se os carimbos de data/hora no nível de palavra devem ser ativados.

Valor padrão: false.

Disponível apenas no modo de saída com streaming. Vozes suportadas: vozes clonadas de cosyvoice-v3.5-plus, cosyvoice-v3.5-flash, cosyvoice-v3-flash, cosyvoice-v3-plus e cosyvoice-v2, além de vozes do sistema marcadas como suportadas na Lista de vozes CosyVoice. qwen-audio-3.0-tts-plus, qwen-audio-3.0-tts-flash e vozes clonadas de outros modelos não suportam este recurso.

Nota

Defina word_timestamp_enabled por meio do parâmetro additional_params:

synthesizer = SpeechSynthesizer(
              model="qwen-audio-3.0-tts-flash",
              voice="your_voice",
              additional_params={"word_timestamp_enabled": True}
          )

seed

int

Não

Uma semente aleatória para controlar a variação na saída da síntese. Quando a versão do modelo, o texto, a voz e outros parâmetros permanecem inalterados, o uso da mesma semente produz resultados idênticos.

Valor padrão: 0.

Valores válidos: [0, 65535].

language_hints

list[str]

Não

Importante
  • Este parâmetro é um array, mas a versão atual processa apenas o primeiro elemento. Passe um único valor.

  • Este parâmetro especifica o idioma alvo para a síntese de fala. Ele não tem relação com o idioma da amostra de áudio usada na clonagem de voz. Para definir o idioma de origem de uma tarefa de clonagem, consulte a referência da API de clonagem de voz.

Especifica o idioma alvo para a síntese de fala visando melhorar a qualidade da saída.

Quando a pronúncia de dígitos, expansão de abreviações, leitura de símbolos ou síntese em idiomas minoritários não atender às expectativas, utilize este parâmetro. Por exemplo:

  • Pronúncia inesperada de dígitos: "olá, isto é 110" é lido como "olá, isto é um zero" em vez da pronúncia chinesa esperada

  • Pronúncia imprecisa de símbolos: "@" é lido como o equivalente chinês em vez de "at"

  • Baixa qualidade na síntese de idiomas secundários, resultando em saídas pouco naturais

Valores válidos:

  • zh: Chinês

  • en: Inglês

  • fr: Francês

  • de: Alemão

  • ja: Japonês

  • ko: Coreano

  • ru: Russo

  • pt: Português

  • th: Tailandês

  • id: Indonésio

  • vi: Vietnamita

  • it: Italiano

  • ms: Malaio

instruction

str

Não

Controla características da síntese, como dialeto, emoção ou estilo de fala.

Para detalhes de uso, consulte Controle por instrução.

enable_aigc_tag

bool

Não

Especifica se uma marca d'água AIGC deve ser incorporada ao áudio gerado. Quando definido como true, a marca d'água é inserida em arquivos de áudio nos formatos suportados (wav/mp3/opus).

Valor padrão: false.

Apenas qwen-audio-3.0-tts-plus, qwen-audio-3.0-tts-flash, cosyvoice-v3-flash, cosyvoice-v3-plus e cosyvoice-v2 suportam este recurso.

Nota

Defina enable_aigc_tag, aigc_propagator e aigc_propagate_id por meio do parâmetro additional_params:

synthesizer = SpeechSynthesizer(
              model="qwen-audio-3.0-tts-flash",
              voice="longanlingxi",
              additional_params={
                  "enable_aigc_tag": True,
                  "aigc_propagator": "your_propagator",
                  "aigc_propagate_id": "your_propagate_id"
              }
          )

aigc_propagator

str

Não

Define o campo ContentPropagator na marca d'água AIGC, identificando o propagador do conteúdo. Só entra em vigor quando enable_aigc_tag é true.

Valor padrão: UID da Alibaba Cloud.

Apenas qwen-audio-3.0-tts-plus, qwen-audio-3.0-tts-flash, cosyvoice-v3-flash, cosyvoice-v3-plus e cosyvoice-v2 suportam este recurso.

Defina por meio do parâmetro additional_params. Consulte o exemplo de enable_aigc_tag.

aigc_propagate_id

str

Não

Define o campo PropagateID na marca d'água AIGC, identificando exclusivamente uma ação de propagação específica. Só entra em vigor quando enable_aigc_tag é true.

Valor padrão: O ID da solicitação atual de síntese de fala.

Apenas qwen-audio-3.0-tts-plus, qwen-audio-3.0-tts-flash, cosyvoice-v3-flash, cosyvoice-v3-plus e cosyvoice-v2 suportam este recurso.

Defina por meio do parâmetro additional_params. Consulte o exemplo de enable_aigc_tag.

hot_fix

dict

Não

Configura correções de pronúncia e substituições de texto aplicadas antes da síntese.

Este recurso não é suportado por qwen-audio-3.0-tts-plus, qwen-audio-3.0-tts-flash, cosyvoice-v2.

Parâmetros:

  • pronunciation: Pronúncia personalizada. Especifica anotações pinyin para palavras a fim de corrigir pronúncias padrão imprecisas.

  • replace: Substituição de texto. Substitui palavras especificadas pelo texto alvo antes da síntese. O texto substituído é usado como entrada real para a síntese.

Exemplo:

synthesizer = SpeechSynthesizer(
    model="qwen-audio-3.0-tts-flash",
    voice="your_voice", # Replace with your qwen-audio-3.0-tts-flash cloned voice
    hot_fix={
        "pronunciation": [{"weather": "tian1 qi4"}],
        "replace": [{"today": "gold day"}]
    }
)

enable_markdown_filter

bool

Não

Importante

Apenas vozes clonadas de cosyvoice-v3-flash suportam este recurso.

Especifica se a filtragem de Markdown deve ser ativada. Quando ativado, o sistema remove automaticamente os símbolos de marcação Markdown do texto de entrada antes da síntese, evitando que sejam lidos em voz alta.

Valor padrão: false.

Valores válidos:

  • true: Ativar filtragem de Markdown

  • false: Desativar filtragem de Markdown

Nota

Defina enable_markdown_filter por meio do parâmetro additional_params:

synthesizer = SpeechSynthesizer(
              model="qwen-audio-3.0-tts-flash",
              voice="your_voice", # Replace with your qwen-audio-3.0-tts-flash cloned voice
              additional_params={"enable_markdown_filter": True}
          )

callback

ResultCallback

Não

Uma instância de callback para receber áudio sintetizado e notificações de eventos de forma assíncrona. Quando definido, call() executa no modo streaming e entrega o áudio por meio do callback on_data. Quando não definido, call() executa no modo sem streaming e retorna o áudio completo como bytes.

ResultCallback

Caminho do pacote: dashscope.audio.tts_v2.ResultCallback

on_open() - conexão estabelecida

Assinatura do método:

def on_open(self) -> None

Acionado quando: A conexão WebSocket é estabelecida com sucesso. Utilize este callback para inicializar fluxos de saída de áudio ou abrir recursos de arquivo.

on_event() - receber resposta do servidor

Assinatura do método:

def on_event(self, message: str) -> None

Parâmetros:

Parâmetro

Tipo

Obrigatório

Descrição

message

str

Sim

Um evento de resposta do servidor em formato JSON contendo header (informações da solicitação) e payload (informações de saída). O campo payload.output contém o tipo de evento, texto original e outros detalhes. Consulte campo output nas mensagens on_event.

Acionado quando: Uma resposta do servidor é recebida. A mensagem é uma string JSON contendo a saída do evento de síntese (tipo de evento, texto original, informações da frase). Analise com json.loads(message) e acesse payload.output para obter detalhes.

on_complete() - síntese concluída

Assinatura do método:

def on_complete(self) -> None

Acionado quando: Todo o texto foi sintetizado e todos os dados de áudio foram entregues via on_data. Utilize este callback para chamar get_first_package_delay() e obter métricas de desempenho.

on_data() - receber dados de áudio

Assinatura do método:

def on_data(self, data: bytes) -> None

Parâmetros:

Parâmetro

Tipo

Obrigatório

Descrição

data

bytes

Sim

Um fragmento de dados binários de áudio no formato especificado pelo parâmetro format do construtor.

Acionado quando: Um fragmento de dados de áudio é recebido. Este callback é invocado várias vezes durante a síntese. Utilize-o para gravar dados em um arquivo ou enviá-los a um dispositivo de reprodução.

on_error() - erro ocorrido

Assinatura do método:

def on_error(self, message: str) -> None

Parâmetros:

Parâmetro

Tipo

Obrigatório

Descrição

message

str

Sim

Uma descrição do erro contendo o código de erro e o motivo detalhado.

Acionado quando: Ocorre um erro durante a síntese. A conexão é fechada automaticamente após a execução deste callback. Registre o erro em log para solução de problemas.

on_close() - conexão fechada

Assinatura do método:

def on_close(self) -> None

Acionado quando: A conexão WebSocket é fechada (seja normalmente ou devido a um erro). Utilize este callback para liberar recursos, como dispositivos de reprodução de áudio.

Campo output nas mensagens on_event

A mensagem JSON recebida pelo callback on_event contém um campo payload.output com informações sobre o evento de síntese. Utilize este campo para acompanhar o progresso da síntese e recuperar detalhes por frase. A estrutura do campo output é a seguinte:

Campo

Tipo

Descrição

type

str

Tipo de evento. Valores: sentence-begin (síntese da frase iniciada), sentence-synthesis (síntese da frase em andamento) ou sentence-end (síntese da frase concluída).

original_text

str

O texto original da frase atual. Retornado nos eventos sentence-begin e sentence-end.

sentence

dict

Informações da frase. Contém index (número sequencial da frase) e words (lista de palavras com informações de carimbo de data/hora quando word_timestamp_enabled está ativado).

Exemplo de mensagem:

{
  "header": {
    "task_id": "xxx",
    "event": "result-generated",
    "attributes": {}
  },
  "payload": {
    "output": {
      "type": "sentence-begin",
      "original_text": "How is the weather today?",
      "sentence": {
        "index": 0,
        "words": []
      }
    }
  }
}

Exemplo de análise:

import json

def on_event(self, message):
    data = json.loads(message)
    output = data.get('payload', {}).get('output', {})
    event_type = output.get('type', '')
    original_text = output.get('original_text', '')
    if event_type:
        print(f'Event type: {event_type}, Original text: {original_text}')

Exemplos de código

O SDK suporta os seguintes modos de síntese:

  • Sem streaming: Uma chamada bloqueante que envia o texto completo de uma só vez e retorna o áudio integral diretamente. Mais adequado para síntese de fala de textos curtos.

  • Streaming unidirecional: Uma chamada não bloqueante que envia o texto completo de uma só vez e entrega dados de áudio (potencialmente em fragmentos) por meio de uma função de callback. Ideal para cenários de texto curto que exigem baixa latência.

  • Streaming bidirecional: Uma chamada não bloqueante que envia texto em múltiplos segmentos e entrega áudio sintetizado incrementalmente por meio de uma função de callback em tempo real. Projetado para cenários de texto longo que exigem baixa latência.

Sem streaming

image

O texto enviado em uma única chamada não deve exceder 20.000 caracteres. Exceder esse limite causa um erro.

Importante

Reinicialize a instância SpeechSynthesizer antes de cada call.

# coding=utf-8

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

# The API Keys for Singapore and Beijing regions are different. Get API Key: https://www.alibabacloud.com/help/zh/model-studio/get-api-key
# If environment variable is not configured, replace the following line with your Model Studio API Key: dashscope.api_key = "sk-xxx"
dashscope.api_key = os.environ.get('DASHSCOPE_API_KEY')

# The following configuration is 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
model = "qwen-audio-3.0-tts-flash"
# Voice
voice = "longanlingxi"

# Instantiate SpeechSynthesizer, passing request parameters such as model and voice in the constructor
synthesizer = SpeechSynthesizer(model=model, voice=voice)
# Send text for synthesis and get 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 the connection setup time
print('[Metric] requestId: {}, first packet latency: {} ms'.format(
    synthesizer.get_last_request_id(),
    synthesizer.get_first_package_delay()))

# Save audio to local file
with open('output.mp3', 'wb') as f:
    f.write(audio)

Streaming unidirecional

image

O texto enviado em uma única chamada não deve exceder 20.000 caracteres. Exceder esse limite causa um erro.

Importante

Reinicialize a instância SpeechSynthesizer antes de cada call.

# coding=utf-8

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

from datetime import datetime

def get_timestamp():
    now = datetime.now()
    formatted_timestamp = now.strftime("[%Y-%m-%d %H:%M:%S.%f]")
    return formatted_timestamp

# The API Keys for Singapore and Beijing regions are different. Get API Key: https://www.alibabacloud.com/help/zh/model-studio/get-api-key
# If environment variable is not configured, replace the following line with your Model Studio API Key: dashscope.api_key = "sk-xxx"
dashscope.api_key = os.environ.get('DASHSCOPE_API_KEY')

# The following configuration is 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
model = "qwen-audio-3.0-tts-flash"
# Voice
voice = "longanlingxi"

# Define callback interface
class Callback(ResultCallback):
    _player = None
    _stream = None

    def on_open(self):
        self.file = open("output.mp3", "wb")
        print("Connection established: " + get_timestamp())

    def on_complete(self):
        print("Speech synthesis completed, all results received: " + get_timestamp())
        # After the task completes (on_complete callback triggered), you can call get_first_package_delay to get the latency
        # The first text submission requires establishing a WebSocket connection, so the first packet latency includes the connection setup time
        print('[Metric] requestId: {}, first packet latency: {} ms'.format(
            synthesizer.get_last_request_id(),
            synthesizer.get_first_package_delay()))

    def on_error(self, message: str):
        print(f"Speech synthesis error: {message}")

    def on_close(self):
        print("Connection closed: " + get_timestamp())
        self.file.close()

    def on_event(self, message):
        # Parse server-side events and get output information
        data = json.loads(message)
        output = data.get('payload', {}).get('output', {})
        event_type = output.get('type', '')
        original_text = output.get('original_text', '')
        if event_type:
            print(f"Event type: {event_type}, original text: {original_text}")

    def on_data(self, data: bytes) -> None:
        print(get_timestamp() + " Binary audio length: " + str(len(data)))
        self.file.write(data)

callback = Callback()

# Instantiate SpeechSynthesizer, passing request parameters such as model and voice in the constructor
synthesizer = SpeechSynthesizer(
    model=model,
    voice=voice,
    callback=callback,
)

# Send text for synthesis and get binary audio in real time via the on_data callback method
synthesizer.call("How is the weather today?")

Streaming bidirecional

image

O texto enviado por chamada não deve exceder 20.000 caracteres. O texto acumulado não deve ultrapassar 200.000 caracteres.

  • Durante a entrada em streaming, chame streaming_call várias vezes para enviar segmentos de texto em sequência. O servidor realiza automaticamente a segmentação de frases no texto recebido:

    • Frases completas são sintetizadas imediatamente

    • Frases incompletas são armazenadas em buffer até serem concluídas

    Quando streaming_complete é chamado, o servidor força a síntese de todo o texto recebido, mas ainda não processado (incluindo frases incompletas).

  • O intervalo entre segmentos de texto não deve exceder 23 segundos. Caso contrário, uma exceção de "tempo limite de solicitação após 23 segundos" será gerada.

    Se não houver texto para enviar, chame streaming_complete prontamente para encerrar a tarefa.

    Importante

    Sempre chame streaming_complete. A falha ao fazer isso pode impedir que o texto final seja convertido em fala.

    O servidor impõe um tempo limite de 23 segundos. Esse valor não pode ser modificado no lado do cliente.
# coding=utf-8
#
# pyaudio installation instructions:
# For macOS, run the following commands:
#   brew install portaudio
#   pip install pyaudio
# For Debian/Ubuntu, run the following commands:
#   sudo apt-get install python-pyaudio python3-pyaudio
#   or
#   pip install pyaudio
# For CentOS, run the following commands:
#   sudo yum install -y portaudio portaudio-devel && pip install pyaudio
# For Microsoft Windows, run the following command:
#   python -m pip install pyaudio

import os
import time
import pyaudio
import json
import dashscope
from dashscope.api_entities.dashscope_response import SpeechSynthesisResponse
from dashscope.audio.tts_v2 import *

from datetime import datetime

def get_timestamp():
    now = datetime.now()
    formatted_timestamp = now.strftime("[%Y-%m-%d %H:%M:%S.%f]")
    return formatted_timestamp

# The API Keys for Singapore and Beijing regions are different. Get API Key: https://www.alibabacloud.com/help/zh/model-studio/get-api-key
# If environment variable is not configured, replace the following line with your Model Studio API Key: dashscope.api_key = "sk-xxx"
dashscope.api_key = os.environ.get('DASHSCOPE_API_KEY')

# The following configuration is 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
model = "qwen-audio-3.0-tts-flash"
# Voice
voice = "longanlingxi"

# Define callback interface
class Callback(ResultCallback):
    _player = None
    _stream = None

    def on_open(self):
        print("Connection established: " + get_timestamp())
        self._player = pyaudio.PyAudio()
        self._stream = self._player.open(
            format=pyaudio.paInt16, channels=1, rate=22050, output=True
        )

    def on_complete(self):
        print("Speech synthesis completed, all results received: " + get_timestamp())

    def on_error(self, message: str):
        print(f"Speech synthesis error: {message}")

    def on_close(self):
        print("Connection closed: " + get_timestamp())
        # Stop the player
        self._stream.stop_stream()
        self._stream.close()
        self._player.terminate()

    def on_event(self, message):
        # Parse server-side events and get output information
        data = json.loads(message)
        output = data.get('payload', {}).get('output', {})
        event_type = output.get('type', '')
        original_text = output.get('original_text', '')
        if event_type:
            print(f"Event type: {event_type}, original text: {original_text}")

    def on_data(self, data: bytes) -> None:
        print(get_timestamp() + " Binary audio length: " + str(len(data)))
        self._stream.write(data)

callback = Callback()

test_text = [
    "Streaming text-to-speech SDK,",
    "converts input text",
    "into binary audio data.",
    "Compared with non-streaming speech synthesis,",
    "streaming synthesis offers better real-time performance.",
    "Users hear near-synchronous audio output while typing,",
    "greatly improving interaction experience",
    "and reducing wait time.",
    "Ideal for large language model (LLM) integration,",
    "where text is streamed for speech synthesis.",
]

# Instantiate SpeechSynthesizer, passing request parameters such as model and voice in the constructor
synthesizer = SpeechSynthesizer(
    model=model,
    voice=voice,
    format=AudioFormat.PCM_22050HZ_MONO_16BIT,
    callback=callback,
)

# Send text for streaming synthesis. Get binary audio in real time via the on_data callback method
for text in test_text:
    synthesizer.streaming_call(text)
    time.sleep(0.1)
# End streaming speech synthesis
synthesizer.streaming_complete()

# The first text submission requires establishing a WebSocket connection, so the first packet latency includes the connection setup time
print('[Metric] requestId: {}, first packet latency: {} ms'.format(
    synthesizer.get_last_request_id(),
    synthesizer.get_first_package_delay()))