Todos os produtos
Search
Central de documentação

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

Última atualização: Jul 16, 2026

Sintetize fala com Qwen-Audio-TTS/CosyVoice usando o DashScope Java SDK.

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

Endpoints de service

Por padrão, o SDK conecta-se ao endpoint da região de Beijing. Para usar outra região, defina Constants.baseWebsocketApiUrl antes de inicializar o SDK.

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 de Singapore:

import com.alibaba.dashscope.utils.Constants;

// Set this before any SDK initialization
Constants.baseWebsocketApiUrl = "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 nas solicitações de inferência. Recomendamos a migração 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

Pacote: com.alibaba.dashscope.audio.ttsv2.SpeechSynthesizer

Construtor

public SpeechSynthesizer(SpeechSynthesisParam param, ResultCallback<SpeechSynthesisResult> callback)

Parâmetros:

  • param: Parâmetros de síntese de fala, construídos com SpeechSynthesisParam.builder()

  • callback: Callback para chamadas em streaming. Passe null para chamadas sem streaming.

call() - Síntese sem streaming ou streaming unidirecional

Assinatura do método:

public ByteBuffer call(String text)

Parâmetros:

Parâmetro

Tipo

Obrigatório

Descrição

text

String

Sim

Texto a sintetizar. Comprimento máximo: 20.000 caracteres.

Valor de retorno: ByteBuffer ou null. Em chamadas sem streaming, retorna os dados de áudio completos. Em chamadas com streaming unidirecional, este método retorna null; o áudio é entregue via callback.

streamingCall() - Síntese com streaming bidirecional

Assinatura do método:

public void streamingCall(String text)

Parâmetros:

Parâmetro

Tipo

Obrigatório

Descrição

text

String

Sim

Texto a sintetizar. Comprimento máximo: 20.000 caracteres. Chame este método várias vezes para anexar texto.

streamingComplete() - Encerrar streaming bidirecional

Assinatura do método:

public void streamingComplete()

Encerra a chamada de streaming bidirecional e notifica o servidor de que todo o texto foi enviado.

callAsFlowable() - Síntese com streaming unidirecional (reativo)

Assinatura do método:

public Flowable<SpeechSynthesisResult> callAsFlowable(String text)

Parâmetros:

Parâmetro

Tipo

Obrigatório

Descrição

text

String

Sim

Texto a sintetizar.

Valor de retorno: Stream reativo Flowable<!--@xref {"id":"xref-flowable-result1","data-node":"5490310","href":"#sec-7m5k9p2r","data-tag":"xref"}-->SpeechSynthesisResult>.

streamingCallAsFlowable() - Síntese com streaming bidirecional (reativo)

Assinatura do método:

public Flowable<SpeechSynthesisResult> streamingCallAsFlowable(Flowable<String> textStream)

Parâmetros:

Parâmetro

Tipo

Obrigatório

Descrição

textStream

Flowable<String>

Sim

Stream reativo de texto.

Valor de retorno: Stream reativo Flowable<!--@xref {"id":"xref-flowable-result2","data-node":"5490310","href":"#sec-7m5k9p2r","data-tag":"xref"}-->SpeechSynthesisResult>.

getDuplexApi().close() - Fechar conexão WebSocket

Assinatura do método:

public boolean getDuplexApi().close(int code, String reason)

Parâmetros:

Parâmetro

Tipo

Obrigatório

Descrição

code

int

Sim

Código de fechamento.

reason

String

Sim

Motivo do fechamento.

Valor de retorno: boolean. Retorna true se a conexão for fechada com sucesso; caso contrário, retorna false.

getLastRequestId() - Obter ID da solicitação

Assinatura do método:

public String getLastRequestId()

Valor de retorno: String, o ID da solicitação.

getFirstPackageDelay() - Obter latência do primeiro pacote

Assinatura do método:

public long getFirstPackageDelay()

Valor de retorno: long. Latência do primeiro pacote em milissegundos, medida do envio do primeiro segmento de texto até o recebimento do primeiro pacote de áudio.

SpeechSynthesisParam

Pacote: com.alibaba.dashscope.audio.ttsv2.SpeechSynthesisParam

Exemplo:

SpeechSynthesisParam param = SpeechSynthesisParam.builder()
    .model("qwen-audio-3.0-tts-flash") // Model
    .voice("longanlingxi") // Voice
    .format(SpeechSynthesisAudioFormat.WAV_8000HZ_MONO_16BIT) // Audio encoding format and sample rate
    .volume(50) // Volume. Value range: [0, 100]
    .speechRate(1.0f) // Speech rate. Value range: [0.5, 2]
    .pitchRate(1.0f) // Pitch. Value range: [0.5, 2]
    .build();

Métodos do Builder

Método

Tipo do parâmetro

Obrigatório

Descrição

model(String)

String

Sim

Nome do modelo.

voice(String)

String

Sim

voice string (obrigatório)

Voz usada na síntese de fala.

  • Vozes do sistema: Consulte a Lista de vozes CosyVoice

  • Vozes clonadas: Vozes personalizadas criadas por clonagem de voz

  • Vozes personalizadas: Vozes personalizadas criadas por design de voz

format(SpeechSynthesisAudioFormat)

enum

Não

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

Padrão: SpeechSynthesisAudioFormat.MP3_22050HZ_MONO_256KBPS.

Pacote SpeechSynthesisAudioFormat: com.alibaba.dashscope.audio.ttsv2.SpeechSynthesisAudioFormat.

volume(int)

int

Não

Nível de volume.

Valor padrão: 50.

Valores válidos: [0, 100].

speechRate(float)

float

Não

Velocidade da fala.

Valor padrão: 1,0.

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

pitchRate(float)

float

Não

Tom da voz.

Valor padrão: 1,0.

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

enableWordTimestamp(boolean)

boolean

Não

Define 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 em streaming. Vozes compatíveis: 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 compatíveis na Lista de vozes CosyVoice. Os modelos qwen-audio-3.0-tts-plus, qwen-audio-3.0-tts-flash e vozes clonadas de outros modelos não oferecem suporte a este recurso.

seed(int)

int

Não

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

Valor padrão: 0.

Valores válidos: [0, 65535].

Para versões do SDK anteriores à 2.21.7, defina seed por meio de parâmetros adicionais.

languageHints(List<String>)

List<String>

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 e não se relaciona ao idioma da amostra de áudio usada na clonagem de voz. Para definir o idioma de source 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.

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

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

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

  • Baixa qualidade na síntese de idiomas minoritários, com resultados 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(String)

String

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.

hotFix(ParamHotFix)

ParamHotFix

Não

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

Os modelos qwen-audio-3.0-tts-plus, qwen-audio-3.0-tts-flash e cosyvoice-v2 não oferecem suporte a este recurso.

Parâmetros:

  • pronunciation: Pronúncia personalizada. Especifica anotações fonéticas para palavras visando 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 da síntese.

Exemplo:

List<ParamHotFix.PronunciationItem> pronunciationItems = new ArrayList<>();
pronunciationItems.add(new ParamHotFix.PronunciationItem("weather", "tian1 qi4"));

List<ParamHotFix.ReplaceItem> replaceItems = new ArrayList<>();
replaceItems.add(new ParamHotFix.ReplaceItem("today", "gold day"));

ParamHotFix paramHotFix = new ParamHotFix();
paramHotFix.setPronunciation(pronunciationItems);
paramHotFix.setReplace(replaceItems);

SpeechSynthesisParam param = SpeechSynthesisParam.builder()
                        .model("qwen-audio-3.0-tts-flash") // Model
                        .voice("your_voice") // Replace with a qwen-audio-3.0-tts-flash cloned voice
                        .hotFix(paramHotFix)
                        .build();

parameter(String key, Object value)

String, Object

Não

Define Parâmetros adicionais.

parameters(Map<String, Object>)

Map

Não

Define Parâmetros adicionais.

Parâmetros adicionais

Definidos por meio de parameter() ou parameters().

Exemplo:

SpeechSynthesisParam param = SpeechSynthesisParam.builder()
  .model("qwen-audio-3.0-tts-flash")
  .voice("longanlingxi")
  .parameter("enable_markdown_filter", true)
  .build();

Parâmetro

Tipo

Obrigatório

Descrição

bit_rate

integer

Não

Taxa de bits do áudio em kbps. Quando o formato de áudio for opus, use bit_rate para ajustar a taxa de bits.

Valor padrão: 32.

Valores válidos: [6, 510].

enable_aigc_tag

boolean

Não

Define se uma marca d'água AIGC deve ser incorporada ao áudio gerado. Quando definido como true, a marca d'água é incorporada em arquivos de áudio dos formatos compatíveis (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 oferecem suporte a este recurso.

aigc_propagator

String

Não

Define o campo ContentPropagator na marca d'água AIGC, identificando o propagador do conteúdo. Tem efeito apenas quando enable_aigc_tag é true.

Valor padrão: UID do Alibaba Cloud.

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

aigc_propagate_id

String

Não

Define o campo PropagateID na marca d'água AIGC, identificando exclusivamente uma ação de propagação específica. Tem efeito apenas quando enable_aigc_tag é true.

Valor padrã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 oferecem suporte a este recurso.

enable_markdown_filter

boolean

Não

Importante

Apenas vozes clonadas de cosyvoice-v3-flash oferecem suporte a este recurso.

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

Valor padrão: false.

Valores válidos:

  • true: Ativar filtragem de Markdown

  • false: Desativar filtragem de Markdown

ResultCallback

Pacote: com.alibaba.dashscope.common.ResultCallback

onEvent() - Receber dados de áudio

Assinatura do método:

public void onEvent(SpeechSynthesisResult result)

Parâmetros:

Parâmetro

Tipo

Obrigatório

Descrição

result

SpeechSynthesisResult

Sim

Acionado ao receber um evento de síntese. Contém o quadro de áudio, informações de carimbo de data/hora e informações de saída (tipo de evento, texto original, etc.).

onComplete() - Síntese concluída

Assinatura do método:

public void onComplete()

Acionado quando a síntese de fala termina.

onError() - Tratamento de erros

Assinatura do método:

public void onError(Exception e)

Parâmetros:

Parâmetro

Tipo

Obrigatório

Descrição

e

Exception

Sim

Acionado quando ocorre um erro. Contém as informações da exceção.

SpeechSynthesisResult

Pacote: com.alibaba.dashscope.audio.tts.SpeechSynthesisResult

getAudioFrame() - Obter quadro de dados de áudio

Assinatura do método:

public ByteBuffer getAudioFrame()

Valor de retorno: ByteBuffer, o quadro de dados de áudio.

getTimestamp() - Obter informações de carimbo de data/hora

Assinatura do método:

public Sentence getTimestamp()

Valor de retorno: Sentence, as informações de carimbo de data/hora.

getOutput() - Obter informações de saída

Assinatura do método:

public JsonObject getOutput()

Valor de retorno: com.google.gson.JsonObject, as informações de saída do evento de síntese, contendo tipo de evento e conteúdo de texto. Requer versão do SDK >= 2.22.0.

Informações de carimbo de data/hora no nível de sentença (Sentence)

Sentence encapsula informações de carimbo de data/hora no nível de sentença.

getBeginTime() - Obter hora de início da sentença

Assinatura do método:

public int getBeginTime()

Valor de retorno: Hora de início da sentença em milissegundos.

getEndTime() - Obter hora de término da sentença

Assinatura do método:

public int getEndTime()

Valor de retorno: Hora de término da sentença em milissegundos.

getWords() - Obter carimbos de data/hora no nível de palavra

Assinatura do método:

public List<Word> getWords()

Valor de retorno: Uma List de objetos Word contendo informações de carimbo de data/hora no nível de palavra. Pode estar vazia.

Informações de carimbo de data/hora no nível de palavra (Word)

Word encapsula informações de carimbo de data/hora no nível de palavra.

getBeginTime() - Obter hora de início da palavra

Assinatura do método:

public int getBeginTime()

Valor de retorno: Hora de início da palavra em milissegundos.

getEndTime() - Obter hora de término da palavra

Assinatura do método:

public int getEndTime()

Valor de retorno: Hora de término da palavra em milissegundos.

getText() - Obter texto

Assinatura do método:

public String getText()

Valor de retorno: String, o conteúdo de texto.

getPhonemes() - Obter carimbos de data/hora no nível de fonema

Assinatura do método:

public List<Phoneme> getPhonemes()

Valor de retorno: Uma List de objetos Phoneme contendo informações de carimbo de data/hora no nível de fonema. Pode estar vazia.

Informações de carimbo de data/hora no nível de fonema (Phoneme)

Phoneme encapsula informações de carimbo de data/hora no nível de fonema.

getBeginTime() - Obter hora de início do fonema

Assinatura do método:

public int getBeginTime()

Valor de retorno: Hora de início do fonema em milissegundos.

getEndTime() - Obter hora de término do fonema

Assinatura do método:

public int getEndTime()

Valor de retorno: Hora de término do fonema em milissegundos.

getText() - Obter texto

Assinatura do método:

public String getText()

Valor de retorno: String, o conteúdo de texto.

getTone() - Obter tom

Assinatura do método:

public int getTone()

Valor de retorno: Valor do tom.

  • Em inglês, 0, 1 e 2 representam sílaba átona, tônica primária e tônica secundária, respectivamente.

  • No pinyin chinês, 1, 2, 3, 4 e 5 representam o primeiro, segundo, terceiro, quarto e tons neutros, respectivamente.

Informações de saída (output)

getOutput() retorna um JsonObject que encapsula as informações de saída do evento de síntese. Recupere-o no callback onEvent ou no stream Flowable. Ele contém os seguintes campos:

Campo

Tipo

Descrição

type

String

Tipo de evento. Valores possíveis: sentence-begin (início da sentença; retorna o texto a sintetizar), sentence-synthesis (síntese de áudio em andamento; retorna um bloco de dados de áudio), sentence-end (fim da sentença; retorna conteúdo de texto e carimbos de data/hora no nível de palavra).

original_text

String

Texto original da sentença atual. Retornado nos eventos sentence-begin e sentence-end.

sentence

JsonObject

Informações da sentença, contendo o índice da sentença (index) e carimbos de data/hora no nível de palavra (words). O evento sentence-end inclui informações completas de carimbo de data/hora no nível de palavra.

Código de exemplo

O SDK oferece suporte aos seguintes modos de síntese:

  • Sem streaming: Chamada bloqueante que envia o texto completo de uma vez e retorna o áudio completo diretamente. Ideal para síntese de fala de textos curtos.

  • Streaming unidirecional: Chamada não bloqueante que envia o texto completo de uma vez e entrega dados de áudio (potencialmente em blocos) via função de callback. Ideal para cenários de texto curto que exigem baixa latência.

  • Streaming bidirecional: Chamada não bloqueante que envia texto em vários segmentos e entrega áudio sintetizado incrementalmente via função de callback em tempo real. Ideal para cenários de texto longo que exigem baixa latência.

Chamadas sem streaming

image

O comprimento do texto por solicitação não deve exceder 20.000 caracteres.

Importante

Reinicialize a instância SpeechSynthesizer antes de cada chamada ao método call.

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
    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 Keys for the Singapore and Beijing regions are different. Get an API Key: https://www.alibabacloud.com/help/en/model-studio/get-api-key
                        // If the environment variable is not configured, replace the following line with your 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("What's the weather like today?");
        } catch (Exception e) {
            throw new RuntimeException(e);
        } finally {
            // Close the WebSocket connection after the task is completed
            synthesizer.getDuplexApi().close(1000, "bye");
        }
        if (audio != null) {
            // Save the audio data to a local file "output.mp3"
            File file = new File("output.mp3");
            // The first text transmission requires establishing a WebSocket connection, so the first packet latency includes the connection setup time
            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 configuration is 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);
    }
}

Chamadas com streaming unidirecional

image

O comprimento do texto por solicitação não deve exceder 20.000 caracteres.

Importante

Reinicialize a instância SpeechSynthesizer antes de cada chamada ao método call.

import com.alibaba.dashscope.audio.tts.SpeechSynthesisResult;
import com.alibaba.dashscope.audio.ttsv2.SpeechSynthesisParam;
import com.alibaba.dashscope.audio.ttsv2.SpeechSynthesizer;
import com.alibaba.dashscope.common.ResultCallback;
import com.alibaba.dashscope.utils.Constants;

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.concurrent.CountDownLatch;

class TimeUtils {
    private static final DateTimeFormatter formatter =
            DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSS");

    public static String getTimestamp() {
        return LocalDateTime.now().format(formatter);
    }
}

public class Main {
    // Model
    private static String model = "qwen-audio-3.0-tts-flash";
    // Voice
    private static String voice = "longanlingxi";

    public static void streamAudioDataToSpeaker() {
        CountDownLatch latch = new CountDownLatch(1);

        // Implement the ResultCallback interface
        ResultCallback<SpeechSynthesisResult> callback = new ResultCallback<SpeechSynthesisResult>() {
            @Override
            public void onEvent(SpeechSynthesisResult result) {
                if (result.getAudioFrame() != null) {
                    // Implement the logic to save audio data locally here
                    System.out.println(TimeUtils.getTimestamp() + " Audio received");
                }
                // Get output information, including event type and original text
                if (result.getOutput() != null && result.getOutput().has("type")) {
                    System.out.println("Event type: " + result.getOutput().get("type").getAsString()
                            + ", Original text: " + (result.getOutput().has("original_text") ? result.getOutput().get("original_text").getAsString() : ""));
                }
            }

            @Override
            public void onComplete() {
                System.out.println(TimeUtils.getTimestamp() + " Complete received, speech synthesis finished");
                latch.countDown();
            }

            @Override
            public void onError(Exception e) {
                System.out.println("Exception occurred: " + e.toString());
                latch.countDown();
            }
        };

        // Request parameters
        SpeechSynthesisParam param =
                SpeechSynthesisParam.builder()
                        // The API Keys for the Singapore and Beijing regions are different. Get an API Key: https://www.alibabacloud.com/help/en/model-studio/get-api-key
                        // If the environment variable is not configured, replace the following line with your Model Studio API Key: .apiKey("sk-xxx")
                        .apiKey(System.getenv("DASHSCOPE_API_KEY"))
                        .model(model) // Model
                        .voice(voice) // Voice
                        .build();
        // Passing "callback" as the second parameter enables asynchronous mode
        SpeechSynthesizer synthesizer = new SpeechSynthesizer(param, callback);
        // Non-blocking call that returns null immediately (actual results are delivered asynchronously via the callback interface). Binary audio is received in real time through the onEvent method of the callback interface
        try {
            synthesizer.call("What's the weather like today?");
            // Wait for synthesis to complete
            latch.await();
            // Wait for playback thread to finish playing
        } catch (Exception e) {
            throw new RuntimeException(e);
        } finally {
            // Close the WebSocket connection after the task is completed
            synthesizer.getDuplexApi().close(1000, "bye");
        }
        // The first text transmission requires establishing a WebSocket connection, so the first packet latency includes the connection setup time
        System.out.println(
                "[Metric] requestId: "
                        + synthesizer.getLastRequestId()
                        + ", first packet latency (ms): "
                        + synthesizer.getFirstPackageDelay());
    }

    public static void main(String[] args) {
        // The following configuration is 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);
    }
}

Chamadas com streaming bidirecional

image

O comprimento do texto por chamada individual não deve exceder 20.000 caracteres, e o comprimento acumulado do texto em todas as chamadas não deve exceder 200.000 caracteres.

  • Durante a entrada em streaming, chame streamingCall várias vezes para enviar segmentos de texto em ordem. O servidor divide automaticamente o texto recebido em sentenças:

    • Sentenças completas são sintetizadas imediatamente.

    • Sentenças incompletas são armazenadas em buffer até formarem uma sentença completa.

    Quando streamingComplete é chamado, o servidor força a síntese de todos os segmentos de texto recebidos, mas ainda não processados, incluindo sentenças incompletas.

  • O intervalo entre segmentos de texto consecutivos não deve exceder 23 segundos; exceder esse limite aciona uma exceção de "tempo limite da solicitação após 23 segundos".

    Se não houver texto pendente, chame streamingComplete prontamente para encerrar a tarefa.

    Importante

    Sempre chame o método streamingComplete. Caso contrário, segmentos de texto finais podem não ser convertidos em fala.

    O servidor impõe um tempo limite de 23 segundos que não pode ser alterado no lado do cliente.
import com.alibaba.dashscope.audio.tts.SpeechSynthesisResult;
import com.alibaba.dashscope.audio.ttsv2.SpeechSynthesisAudioFormat;
import com.alibaba.dashscope.audio.ttsv2.SpeechSynthesisParam;
import com.alibaba.dashscope.audio.ttsv2.SpeechSynthesizer;
import com.alibaba.dashscope.common.ResultCallback;
import com.alibaba.dashscope.utils.Constants;

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;

class TimeUtils {
    private static final DateTimeFormatter formatter =
            DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSS");

    public static String getTimestamp() {
        return LocalDateTime.now().format(formatter);
    }
}

public class Main {
    private static String[] textArray = {"Streaming text-to-speech SDK, ",
            "can convert input text ", "into audio binary data. ", "Compared to non-streaming speech synthesis, ",
            "streaming synthesis offers better real-time performance. ", "While users input text, ",
            "they can hear nearly synchronous audio output, ", "greatly enhancing the interactive experience ",
            "and reducing user wait time. ", "It is suitable for calling large-scale ", "language models (LLMs) to ",
            "perform speech synthesis ", "with streaming text input."};
    private static String model = "qwen-audio-3.0-tts-flash"; // Model
    private static String voice = "longanlingxi"; // Voice

    public static void streamAudioDataToSpeaker() {
        // Configure the callback function
        ResultCallback<SpeechSynthesisResult> callback = new ResultCallback<SpeechSynthesisResult>() {
            @Override
            public void onEvent(SpeechSynthesisResult result) {
                // System.out.println("Message received: " + result);
                if (result.getAudioFrame() != null) {
                    // Implement the logic to process audio data here
                    System.out.println(TimeUtils.getTimestamp() + " Audio received");
                }
            }

            @Override
            public void onComplete() {
                System.out.println(TimeUtils.getTimestamp() + " Complete received, speech synthesis finished");
            }

            @Override
            public void onError(Exception e) {
                System.out.println("Exception occurred: " + e.toString());
            }
        };

        // Request parameters
        SpeechSynthesisParam param =
                SpeechSynthesisParam.builder()
                        // The API Keys for the Singapore and Beijing regions are different. Get an API Key: https://www.alibabacloud.com/help/en/model-studio/get-api-key
                        // If the environment variable is not configured, replace the following line with your Model Studio API Key: .apiKey("sk-xxx")
                        .apiKey(System.getenv("DASHSCOPE_API_KEY"))
                        .model(model)
                        .voice(voice)
                        .format(SpeechSynthesisAudioFormat
                                .PCM_22050HZ_MONO_16BIT) // Use PCM or MP3 for streaming synthesis
                        .build();
        SpeechSynthesizer synthesizer = new SpeechSynthesizer(param, callback);
        // The call method with Callback will not block the current thread
        try {
            for (String text : textArray) {
                // Send text fragments and receive binary audio in real time through the onEvent method of the callback interface
                synthesizer.streamingCall(text);
            }
            // Wait for streaming speech synthesis to finish
            synthesizer.streamingComplete();
        } catch (Exception e) {
            throw new RuntimeException(e);
        } finally {
            // Close the WebSocket connection after the task is completed
            synthesizer.getDuplexApi().close(1000, "bye");
        }

        // The first text transmission requires establishing a WebSocket connection, so the first packet latency includes the connection setup time
        System.out.println(
                "[Metric] requestId: "
                        + synthesizer.getLastRequestId()
                        + ", first packet latency (ms): "
                        + synthesizer.getFirstPackageDelay());
    }

    public static void main(String[] args) {
        // The following configuration is 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);
    }
}

Chamadas baseadas em Flowable

Flowable é um tipo RxJava que representa um stream reativo com suporte a backpressure. Para mais informações, consulte a Documentação do RxJava Flowable.

Antes de usar Flowable, certifique-se de que a biblioteca RxJava esteja integrada e que você compreenda os conceitos básicos de programação reativa.

O comprimento do texto por chamada individual não deve exceder 20.000 caracteres, e o comprimento acumulado do texto em todas as chamadas não deve exceder 200.000 caracteres.

Chamadas com streaming unidirecional

O exemplo a seguir mostra como usar a interface blockingForEach de um objeto Flowable para recuperar cada objeto SpeechSynthesisResult transmitido de forma bloqueante.

import com.alibaba.dashscope.audio.ttsv2.SpeechSynthesisParam;
import com.alibaba.dashscope.audio.ttsv2.SpeechSynthesizer;
import com.alibaba.dashscope.exception.NoApiKeyException;
import com.alibaba.dashscope.utils.Constants;

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;

class TimeUtils {
    private static final DateTimeFormatter formatter =
            DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSS");

    public static String getTimestamp() {
        return LocalDateTime.now().format(formatter);
    }
}

public class Main {
    private static String model = "qwen-audio-3.0-tts-flash"; // Model
    private static String voice = "longanlingxi"; // Voice

    public static void streamAudioDataToSpeaker() throws NoApiKeyException {
        // Request parameters
        SpeechSynthesisParam param =
                SpeechSynthesisParam.builder()
                        // The API Keys for the Singapore and Beijing regions are different. Get an API Key: https://www.alibabacloud.com/help/en/model-studio/get-api-key
                        // If the environment variable is not configured, replace the following line with your Model Studio API Key: .apiKey("sk-xxx")
                        .apiKey(System.getenv("DASHSCOPE_API_KEY"))
                        .model(model) // Model
                        .voice(voice) // Voice
                        .build();
        SpeechSynthesizer synthesizer = new SpeechSynthesizer(param, null);
        synthesizer.callAsFlowable("What's the weather like today?").blockingForEach(result -> {
            if (result.getAudioFrame() != null) {
                // Implement the logic to process audio data here
                System.out.println(TimeUtils.getTimestamp() + " Audio received");
            }
            // Get output information, including event type and original text
            if (result.getOutput() != null && result.getOutput().has("type")) {
                System.out.println("Event type: " + result.getOutput().get("type").getAsString()
                        + ", Original text: " + (result.getOutput().has("original_text") ? result.getOutput().get("original_text").getAsString() : ""));
            }
        });
        // Close the WebSocket connection after the task is completed
        synthesizer.getDuplexApi().close(1000, "bye");
        // The first text transmission requires establishing a WebSocket connection, so the first packet latency includes the connection setup time
        System.out.println(
                "[Metric] requestId: "
                        + synthesizer.getLastRequestId()
                        + ", first packet latency (ms): "
                        + synthesizer.getFirstPackageDelay());
    }

    public static void main(String[] args) throws NoApiKeyException {
        // The following configuration is 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);
    }
}

Chamadas com streaming bidirecional

O exemplo a seguir mostra como usar um objeto Flowable como parâmetro de entrada para streaming de texto e usar a interface blockingForEach do objeto Flowable retornado para recuperar cada objeto SpeechSynthesisResult transmitido de forma bloqueante.

import com.alibaba.dashscope.audio.ttsv2.SpeechSynthesisParam;
import com.alibaba.dashscope.audio.ttsv2.SpeechSynthesizer;
import com.alibaba.dashscope.exception.NoApiKeyException;
import com.alibaba.dashscope.utils.Constants;
import io.reactivex.BackpressureStrategy;
import io.reactivex.Flowable;

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;

class TimeUtils {
    private static final DateTimeFormatter formatter =
            DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSS");

    public static String getTimestamp() {
        return LocalDateTime.now().format(formatter);
    }
}

public class Main {
    private static String[] textArray = {"Streaming text-to-speech SDK, ",
            "can convert input text ", "into audio binary data. ", "Compared to non-streaming speech synthesis, ",
            "streaming synthesis offers better real-time performance. ", "While users input text, ",
            "they can hear nearly synchronous audio output, ", "greatly enhancing the interactive experience ",
            "and reducing user wait time. ", "It is suitable for calling large-scale ", "language models (LLMs) to ",
            "perform speech synthesis ", "with streaming text input."};
    private static String model = "qwen-audio-3.0-tts-flash";
    private static String voice = "longanlingxi";

    public static void streamAudioDataToSpeaker() throws NoApiKeyException {
        // Simulate streaming input
        Flowable<String> textSource = Flowable.create(emitter -> {
            new Thread(() -> {
                for (int i = 0; i < textArray.length; i++) {
                    emitter.onNext(textArray[i]);
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        throw new RuntimeException(e);
                    }
                }
                emitter.onComplete();
            }).start();
        }, BackpressureStrategy.BUFFER);

        // Request parameters
        SpeechSynthesisParam param =
                SpeechSynthesisParam.builder()
                        // The API Keys for the Singapore and Beijing regions are different. Get an API Key: https://www.alibabacloud.com/help/en/model-studio/get-api-key
                        // If the environment variable is not configured, replace the following line with your Model Studio API Key: .apiKey("sk-xxx")
                        .apiKey(System.getenv("DASHSCOPE_API_KEY"))
                        .model(model) // Model
                        .voice(voice) // Voice
                        .build();
        SpeechSynthesizer synthesizer = new SpeechSynthesizer(param, null);
        synthesizer.streamingCallAsFlowable(textSource).blockingForEach(result -> {
            if (result.getAudioFrame() != null) {
                // Implement the logic to play audio here
                System.out.println(
                        TimeUtils.getTimestamp() +
                                " Binary audio size: " + result.getAudioFrame().capacity());
            }
            // Get output information, including event type and original text
            if (result.getOutput() != null && result.getOutput().has("type")) {
                System.out.println("Event type: " + result.getOutput().get("type").getAsString()
                        + ", Original text: " + (result.getOutput().has("original_text") ? result.getOutput().get("original_text").getAsString() : ""));
            }
        });
        synthesizer.getDuplexApi().close(1000, "bye");
        // The first text transmission requires establishing a WebSocket connection, so the first packet latency includes the connection setup time
        System.out.println(
                "[Metric] requestId: "
                        + synthesizer.getLastRequestId()
                        + ", first packet latency (ms): "
                        + synthesizer.getFirstPackageDelay());
    }

    public static void main(String[] args) throws NoApiKeyException {
        // The following configuration is 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);
    }
}

Chamadas de alta concorrência

O DashScope Java SDK usa pool de conexões OkHttp3 para reduzir a sobrecarga de estabelecimento repetido de conexões. Para mais informações, consulte as Melhores práticas de alta concorrência.