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";
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.compara{WorkspaceId}.cn-beijing.maas.aliyuncs.comSingapore: de
dashscope-intl.aliyuncs.compara{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 |
|
|
String |
Sim |
Nome do modelo. |
|
|
String |
Sim |
voice Voz usada na síntese de fala.
|
|
|
enum |
Não |
Formato de codificação de áudio e taxa de amostragem. Padrão: SpeechSynthesisAudioFormat.MP3_22050HZ_MONO_256KBPS. Pacote SpeechSynthesisAudioFormat: |
|
|
int |
Não |
Nível de volume. Valor padrão: 50. Valores válidos: [0, 100]. |
|
|
float |
Não |
Velocidade da fala. Valor padrão: 1,0. Valores válidos: [0,5, 2,0]. |
|
|
float |
Não |
Tom da voz. Valor padrão: 1,0. Valores válidos: [0,5, 2,0]. |
|
|
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. |
|
|
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 |
|
|
List<String> |
Não |
Importante
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:
Valores válidos:
|
|
|
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. |
|
|
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:
Exemplo:
|
|
|
String, Object |
Não |
Define Parâmetros adicionais. |
|
|
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 |
|
|
integer |
Não |
Taxa de bits do áudio em kbps. Quando o formato de áudio for opus, use Valor padrão: 32. Valores válidos: [6, 510]. |
|
|
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. |
|
|
String |
Não |
Define o campo 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. |
|
|
String |
Não |
Define o campo 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. |
|
|
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:
|
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 |
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: |
|
original_text |
String |
Texto original da sentença atual. Retornado nos eventos |
|
sentence |
JsonObject |
Informações da sentença, contendo o índice da sentença ( |
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
O comprimento do texto por solicitação não deve exceder 20.000 caracteres.
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
O comprimento do texto por solicitação não deve exceder 20.000 caracteres.
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
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
streamingCallvá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
streamingCompleteprontamente para encerrar a tarefa.ImportanteSempre 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.