Sintetice voz con CosyVoice utilizando el SDK de Java de DashScope.
Endpoints de servicio
De forma predeterminada, el SDK se conecta al endpoint de la región de Pekín. Para usar una región diferente, configure Constants.baseWebsocketApiUrl antes de inicializar el SDK.
Singapore
wss://{WorkspaceId}.ap-southeast-1.maas.aliyuncs.com/api-ws/v1/inference
Reemplace {WorkspaceId} con su ID de espacio de trabajo real.
China (Beijing)
wss://{WorkspaceId}.cn-beijing.maas.aliyuncs.com/api-ws/v1/inference
Reemplace {WorkspaceId} con su ID de espacio de trabajo real.
ImportanteAlibaba Cloud Model Studio ha lanzado dominios específicos del espacio de trabajo para las regiones China (Pekín) y Singapore. Los nuevos dominios dedicados ofrecen un rendimiento superior y una mayor estabilidad para las solicitudes de inferencia. Recomendamos migrar a los nuevos dominios:
- China (Pekín): de
dashscope.aliyuncs.coma{WorkspaceId}.cn-beijing.maas.aliyuncs.com - Singapur: de
dashscope-intl.aliyuncs.coma{WorkspaceId}.ap-southeast-1.maas.aliyuncs.com
SpeechSynthesizer
Paquete: com.alibaba.dashscope.audio.ttsv2.SpeechSynthesizer
Constructor
public SpeechSynthesizer(SpeechSynthesisParam param, ResultCallback<SpeechSynthesisResult> callback)
Parámetros:
param: Parámetros de síntesis de voz, construidos conSpeechSynthesisParam.builder()callback: Devolución de llamada para llamadas por streaming. Pase null para llamadas sin streaming.
call() - Síntesis sin streaming/streaming unidireccional
Firma del método:
public ByteBuffer call(String text)
Parámetros:
| Parámetro | Tipo | Obligatorio | Descripción |
|---|---|---|---|
text | String | Sí | Texto a sintetizar. Longitud máxima: 20,000 caracteres. |
Valor de retorno: ByteBuffer o null. Para llamadas sin streaming, devuelve los datos de audio completos. Para llamadas de streaming unidireccional, este método devuelve null; el audio se entrega a través de la devolución de llamada.
streamingCall() - Síntesis por streaming bidireccional
Firma del método:
public void streamingCall(String text)
Parámetros:
| Parámetro | Tipo | Obligatorio | Descripción |
|---|---|---|---|
text | String | Sí | Texto a sintetizar. Longitud máxima: 20,000 caracteres. Puede llamar a este método varias veces para agregar texto. |
streamingComplete() - Finalizar streaming bidireccional
Firma del método:
public void streamingComplete()
Finaliza la llamada de streaming bidireccional y notifica al servidor que se ha enviado todo el texto.
streamingCancel() - Cancelar streaming bidireccional
Firma del método:
public void streamingCancel()
Descripción: Cancela la tarea actual de síntesis de voz por streaming bidireccional. Después de llamar a este método, el SDK finaliza inmediatamente la tarea actual. Puede iniciar una nueva tarea de síntesis en la misma conexión sin volver a inicializar la instancia de SpeechSynthesizer.
ImportanteRequisito de versión: Esta función requiere Java SDK 2.22.26 o posterior.
ImportanteLimitaciones del modelo:
- China (Beijing): Los modelos CosyVoice requieren v2 o posterior.
- Singapur: Los modelos CosyVoice no admiten esta función.
callAsFlowable() - Síntesis por streaming unidireccional (reactiva)
Firma del método:
public Flowable<SpeechSynthesisResult> callAsFlowable(String text)
Parámetros:
| Parámetro | Tipo | Obligatorio | Descripción |
|---|---|---|---|
text | String | Sí | Texto a sintetizar. |
Valor de retorno: Flujo reactivo Flowable<SpeechSynthesisResult>.
streamingCallAsFlowable() - Síntesis por streaming bidireccional (reactiva)
Firma del método:
public Flowable<SpeechSynthesisResult> streamingCallAsFlowable(Flowable<String> textStream)
Parámetros:
| Parámetro | Tipo | Obligatorio | Descripción |
|---|---|---|---|
textStream | Flowable<String> | Sí | Flujo reactivo de texto. |
Valor de retorno: Flujo reactivo Flowable<SpeechSynthesisResult>.
getDuplexApi().close() - Cerrar conexión WebSocket
Firma del método:
public boolean getDuplexApi().close(int code, String reason)
Parámetros:
| Parámetro | Tipo | Obligatorio | Descripción |
|---|---|---|---|
code | int | Sí | Código de cierre. |
reason | String | Sí | Motivo de cierre. |
Valor de retorno: boolean. Devuelve true si la conexión se cerró correctamente, false en caso contrario.
getLastRequestId() - Obtener ID de solicitud
Firma del método:
public String getLastRequestId()
Valor de retorno: String, el ID de solicitud.
getFirstPackageDelay() - Obtener latencia del primer paquete
Firma del método:
public long getFirstPackageDelay()
Valor de retorno: long. La latencia del primer paquete en milisegundos, medida desde el envío del primer segmento de texto hasta la recepción del primer paquete de audio.
SpeechSynthesisParam
Paquete: com.alibaba.dashscope.audio.ttsv2.SpeechSynthesisParam
Ejemplo:
SpeechSynthesisParam param = SpeechSynthesisParam.builder()
.model("cosyvoice-v3-plus") // Model
.voice("longanyang") // 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 del builder
| Método | Tipo de parámetro | Obligatorio | Descripción |
|---|---|---|---|
model(String) | String | Sí | El nombre del modelo. |
voice(String) | String | Sí | La voz utilizada para la síntesis de habla.
|
format(SpeechSynthesisAudioFormat) | enum | No | Formato de codificación de audio y tasa de muestreo. Predeterminado: SpeechSynthesisAudioFormat.MP3_22050HZ_MONO_256KBPS. Paquete SpeechSynthesisAudioFormat: |
volume(int) | int | No | El nivel de volumen. Valor predeterminado: 50. Valores válidos: [0, 100]. |
speechRate(float) | float | No | La velocidad del habla. Valor predeterminado: 1.0. Valores válidos: [0.5, 2.0]. |
pitchRate(float) | float | No | El tono. Valor predeterminado: 1.0. Valores válidos: [0.5, 2.0]. |
enableWordTimestamp(boolean) | boolean | No | Especifica si se deben habilitar las marcas de tiempo a nivel de palabra. Valor predeterminado: false. Disponible solo en modo de salida por streaming. Voces compatibles: voces clonadas de cosyvoice-v3.5-plus, cosyvoice-v3.5-flash, cosyvoice-v3-flash, cosyvoice-v3-plus, cosyvoice-v2 y voces del sistema marcadas como compatibles en la CosyVoice lista de voces. Las voces clonadas de otros modelos no admiten esta función. |
seed(int) | int | No | Una semilla aleatoria para controlar la variación en la salida de la síntesis. Cuando la versión del modelo, el texto, la voz y otros parámetros no cambian, usar la misma semilla produce resultados idénticos. Valor predeterminado: 0. Valores válidos: [0, 65535]. Para versiones del SDK anteriores a 2.21.7, establezca |
languageHints(List<String>) | List<String> | No | Importante
Especifica el idioma objetivo para la síntesis de voz con el fin de mejorar la calidad de salida. Cuando la pronunciación de dígitos, la expansión de abreviaturas, la lectura de símbolos o la síntesis en idiomas minoritarios no cumpla con las expectativas, utilice este parámetro. Por ejemplo:
Valores válidos
|
instruction(String) | String | No | Controla las características de la síntesis, como el dialecto, la emoción o el estilo de habla. Para detalles de uso, consulte Control de instrucciones. |
hotFix(ParamHotFix) | ParamHotFix | No | Configura correcciones de pronunciación y reemplazos de texto aplicados antes de la síntesis. NotaEsta función no es compatible con cosyvoice-v2. Parámetros:
Ejemplo: |
parameter(String key, Object value) | String, Object | No | Establece parámetros adicionales. |
parameters(Map<String, Object>) | Map | No | Establece parámetros adicionales. |
Parámetros adicionales
Se establece mediante parameter() o parameters().
Ejemplo:
SpeechSynthesisParam param = SpeechSynthesisParam.builder()
.model("cosyvoice-v3-plus")
.voice("longanyang")
.parameter("bit_rate", 32)
.build();
| Parámetro | Tipo | Obligatorio | Descripción |
|---|---|---|---|
bit_rate | integer | No | La tasa de bits de audio en kbps. Cuando el formato de audio es mp3 u opus, utilice bit_rate para ajustar la tasa de bits.Valor predeterminado: 32. Valores válidos: [6, 510]. |
enable_aigc_tag | boolean | No | Especifica si se debe incrustar una marca de agua AIGC en el audio generado. Cuando se establece en true, la marca de agua se incrusta en archivos de audio de formatos compatibles (wav/mp3/opus). Valor predeterminado: false. NotaSolo cosyvoice-v3-flash, cosyvoice-v3-plus y cosyvoice-v2 admiten esta función. |
aigc_propagator | String | No | Establece el campo ContentPropagator en la marca de agua AIGC, identificando al propagador del contenido. Surte efecto solo cuando enable_aigc_tag es true.Valor predeterminado: UID de Alibaba Cloud. NotaSolo cosyvoice-v3-flash, cosyvoice-v3-plus y cosyvoice-v2 admiten esta función. |
aigc_propagate_id | String | No | Establece el campo PropagateID en la marca de agua AIGC, identificando de manera única una acción de propagación específica. Surte efecto solo cuando enable_aigc_tag es true.Valor predeterminado: El ID de solicitud de la solicitud actual de síntesis de voz. NotaSolo cosyvoice-v3-flash, cosyvoice-v3-plus y cosyvoice-v2 admiten esta función. |
enable_markdown_filter | boolean | No | NotaSolo las voces clonadas de cosyvoice-v3-flash admiten esta función. Especifica si se debe habilitar el filtrado de Markdown. Cuando está habilitado, el sistema elimina automáticamente los símbolos de marcado Markdown del texto de entrada antes de la síntesis, evitando que se lean en voz alta. Valor predeterminado: false. Valores válidos:
|
ResultCallback
Paquete: com.alibaba.dashscope.common.ResultCallback
onEvent() - Recibir datos de audio
Firma del método:
public void onEvent(SpeechSynthesisResult result)
Parámetros:
| Parámetro | Tipo | Obligatorio | Descripción |
|---|---|---|---|
result | SpeechSynthesisResult | Sí | Se activa cuando se recibe un evento de síntesis. Contiene el cuadro de audio, información de marcas de tiempo e información de salida (tipo de evento, texto original, etc.). |
onComplete() - Síntesis completada
Firma del método:
public void onComplete()
Se activa cuando se completa la síntesis de voz.
onError() - Manejo de errores
Firma del método:
public void onError(Exception e)
Parámetros:
| Parámetro | Tipo | Obligatorio | Descripción |
|---|---|---|---|
e | Exception | Sí | Se activa cuando ocurre un error. Contiene la información de la excepción. |
SpeechSynthesisResult
Paquete: com.alibaba.dashscope.audio.tts.SpeechSynthesisResult
getAudioFrame() - Obtener trama de datos de audio
Firma del método:
public ByteBuffer getAudioFrame()
Valor de retorno: ByteBuffer, la trama de datos de audio.
getTimestamp() - Obtener información de marca de tiempo
Firma del método:
public Sentence getTimestamp()
Valor de retorno: Sentence, la información de marca de tiempo.
getOutput() - Obtener información de salida
Firma del método:
public JsonObject getOutput()
Valor de retorno: com.google.gson.JsonObject, la información de salida del evento de síntesis, que contiene el tipo de evento y el contenido del texto. Requiere una versión del SDK >= 2.22.0.
Información de marcas de tiempo a nivel de oración (Sentence)
Sentence encapsula información de marcas de tiempo a nivel de oración.
getBeginTime() - Obtener tiempo de inicio de oración
Firma del método:
public int getBeginTime()
Valor de retorno: Tiempo de inicio de la oración en milisegundos.
getEndTime() - Obtener tiempo de fin de oración
Firma del método:
public int getEndTime()
Valor de retorno: Tiempo de fin de la oración en milisegundos.
getWords() - Obtener marcas de tiempo a nivel de palabra
Firma del método:
public List<Word> getWords()
Valor de retorno: Una List de objetos Word que contiene información de marcas de tiempo a nivel de palabra. Puede estar vacía.
Información de marcas de tiempo a nivel de palabra (Word)
Word encapsula información de marcas de tiempo a nivel de palabra.
getBeginTime() - Obtener tiempo de inicio de palabra
Firma del método:
public int getBeginTime()
Valor de retorno: Tiempo de inicio de la palabra en milisegundos.
getEndTime() - Obtener tiempo de fin de palabra
Firma del método:
public int getEndTime()
Valor de retorno: Tiempo de fin de la palabra en milisegundos.
getText() - Obtener texto
Firma del método:
public String getText()
Valor de retorno: String, el contenido del texto.
getPhonemes() - Obtener marcas de tiempo a nivel de fonema
Firma del método:
public List<Phoneme> getPhonemes()
Valor de retorno: Una List de objetos Phoneme que contiene información de marcas de tiempo a nivel de fonema. Puede estar vacía.
Información de marcas de tiempo a nivel de fonema (Phoneme)
Phoneme encapsula información de marcas de tiempo a nivel de fonema.
getBeginTime() - Obtener tiempo de inicio de fonema
Firma del método:
public int getBeginTime()
Valor de retorno: Tiempo de inicio del fonema en milisegundos.
getEndTime() - Obtener tiempo de fin de fonema
Firma del método:
public int getEndTime()
Valor de retorno: Tiempo de fin del fonema en milisegundos.
getText() - Obtener texto
Firma del método:
public String getText()
Valor de retorno: String, el contenido del texto.
getTone() - Obtener tono
Firma del método:
public int getTone()
Valor de retorno: El valor del tono.
- En inglés, 0, 1 y 2 representan sílaba átona, acento primario y acento secundario respectivamente.
- En pinyin chino, 1, 2, 3, 4 y 5 representan el primer, segundo, tercer, cuarto y tono neutro respectivamente.
Información de salida (output)
getOutput() devuelve un JsonObject que encapsula la información de salida del evento de síntesis. Recupérela en la devolución de llamada onEvent o en el flujo Flowable. Contiene los siguientes campos:
| Campo | Tipo | Descripción |
|---|---|---|
type | String | Tipo de evento. Valores posibles: sentence-begin (inicio de oración; devuelve el texto a sintetizar), sentence-synthesis (síntesis de audio en curso; devuelve un fragmento de datos de audio), sentence-end (fin de oración; devuelve contenido de texto y marcas de tiempo a nivel de palabra). |
original_text | String | Texto original de la oración actual. Se devuelve en los eventos sentence-begin y sentence-end. |
sentence | JsonObject | Información de la oración, que contiene el índice de la oración (index) y las marcas de tiempo a nivel de palabra (words). El evento sentence-end incluye información completa de marcas de tiempo a nivel de palabra. |
Código de ejemplo
El SDK admite los siguientes modos de síntesis:
- No streaming: Una llamada bloqueante que envía el texto completo de una sola vez y devuelve el audio completo directamente. Ideal para la síntesis de voz de texto corto.
- Streaming unidireccional: Una llamada no bloqueante que envía el texto completo de una sola vez y entrega datos de audio (potencialmente en fragmentos) a través de una función de devolución de llamada. Ideal para escenarios de texto corto que requieren baja latencia.
- Streaming bidireccional: Una llamada no bloqueante que envía texto en múltiples segmentos y entrega audio sintetizado incrementalmente a través de una función de devolución de llamada en tiempo real. Ideal para escenarios de texto largo que requieren baja latencia.
Llamadas sin streaming
La longitud del texto por solicitud no debe exceder los 20,000 caracteres.
ImportanteReinicialice la instancia de SpeechSynthesizer antes de cada llamada al 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 = "cosyvoice-v3-plus";
// Voice
private static String voice = "longanyang";
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/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);
}
}
Llamadas de streaming unidireccional
La longitud del texto por solicitud no debe exceder los 20,000 caracteres.
ImportanteReinicialice la instancia de SpeechSynthesizer antes de cada llamada al 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 = "cosyvoice-v3-plus";
// Voice
private static String voice = "longanyang";
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/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);
}
}
Llamadas de streaming bidireccional
La longitud del texto por llamada individual no debe exceder los 20,000 caracteres, y la longitud acumulada del texto en todas las llamadas no debe exceder los 200,000 caracteres.
-
Durante la entrada en streaming, llame a
streamingCallvarias veces para enviar segmentos de texto en orden. El servidor divide automáticamente el texto recibido en oraciones:- Las oraciones completas se sintetizan inmediatamente.
- Las oraciones incompletas se almacenan en búfer hasta formar una oración completa.
Cuando se llama a
streamingComplete, el servidor fuerza la síntesis de todos los segmentos de texto recibidos pero no procesados, incluidas las oraciones incompletas. -
El intervalo entre segmentos de texto consecutivos no debe exceder los 23 segundos; superar este límite desencadena una excepción de "tiempo de espera de solicitud agotado después de 23 segundos".
Si no hay texto pendiente, llame a
streamingCompleterápidamente para finalizar la tarea.ImportanteLlame siempre al método
streamingComplete. De lo contrario, es posible que los segmentos de texto finales no se conviertan a voz.El servidor aplica un tiempo de espera de 23 segundos que no se puede modificar desde el lado del 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 = "cosyvoice-v3-plus"; // Model
private static String voice = "longanyang"; // 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/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);
}
}
Llamadas basadas en Flowable
Flowable es un tipo de RxJava que representa un flujo reactivo compatible con contrapresión. Para obtener más información, consulte la documentación de Flowable de RxJava.
Antes de usar Flowable, asegúrese de que la biblioteca RxJava esté integrada y de comprender los conceptos básicos de la programación reactiva.
La longitud del texto por llamada individual no debe exceder los 20,000 caracteres, y la longitud acumulada del texto en todas las llamadas no debe exceder los 200,000 caracteres.
Llamadas de streaming unidireccional
El siguiente ejemplo muestra cómo usar la interfaz blockingForEach de un objeto Flowable para recuperar cada objeto SpeechSynthesisResult transmitido de manera 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 = "cosyvoice-v3-plus"; // Model
private static String voice = "longanyang"; // 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/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);
}
}
Llamadas de streaming bidireccional
El siguiente ejemplo muestra cómo usar un objeto Flowable como parámetro de entrada para el streaming de texto, y usar la interfaz blockingForEach del objeto Flowable devuelto para recuperar cada objeto SpeechSynthesisResult transmitido de manera 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 = "cosyvoice-v3-plus";
private static String voice = "longanyang";
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/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);
}
}
Llamadas de alta concurrencia
El SDK de Java de DashScope utiliza la agrupación de conexiones de OkHttp3 para reducir la sobrecarga de establecer conexiones repetidamente. Para obtener más información, consulte Mejores prácticas de alta concurrencia.