Todos los productos
Search
Centro de documentos

Alibaba Cloud Model Studio:SDK de Java de Qwen-Audio-TTS

Última actualización: Oct 12, 2026

Sintetice voz con Qwen-Audio-TTS 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.com a {WorkspaceId}.cn-beijing.maas.aliyuncs.com
  • Singapur: de dashscope-intl.aliyuncs.com a {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 con SpeechSynthesisParam.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ámetroTipoObligatorioDescripción
textStringSí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ámetroTipoObligatorioDescripción
textStringSí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.

callAsFlowable() - Síntesis por streaming unidireccional (reactiva)

Firma del método:

public Flowable<SpeechSynthesisResult> callAsFlowable(String text)

Parámetros:

ParámetroTipoObligatorioDescripción
textStringSí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ámetroTipoObligatorioDescripción
textStreamFlowable<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ámetroTipoObligatorioDescripción
codeintSíCódigo de cierre.
reasonStringSí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("qwen-audio-3.0-tts-flash") // Model
    .voice("longanhuan_v3.6") // 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étodoTipo de parámetroObligatorioDescripción
model(String)StringSíEl nombre del modelo.
voice(String)StringSíLa voz utilizada para la síntesis de habla.
  • Voces del sistema: Consulte la lista de voces de Qwen-Audio-TTS
  • Voces clonadas: Voces personalizadas creadas mediante clonación de voz
  • Voces personalizadas: Voces personalizadas creadas mediante diseño de voz
format(SpeechSynthesisAudioFormat)enumNoFormato de codificación de audio y tasa de muestreo.

Predeterminado: SpeechSynthesisAudioFormat.MP3_22050HZ_MONO_256KBPS.

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

volume(int)intNoEl nivel de volumen.

Valor predeterminado: 50.

Valores válidos: [0, 100].

speechRate(float)floatNoLa velocidad del habla.

Valor predeterminado: 1.0.

Valores válidos: [0.5, 2.0].

pitchRate(float)floatNoEl tono.

Valor predeterminado: 1.0.

Valores válidos: [0.5, 2.0].

enableWordTimestamp(boolean)booleanNoEspecifica si se deben habilitar las marcas de tiempo a nivel de palabra.

Valor predeterminado: false.

Disponible solo en modo de salida por streaming. Se admiten voces clonadas. Para voces del sistema compatibles, consulte la lista de voces de Qwen-Audio-TTS.

seed(int)intNoUna 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 seed mediante parámetros adicionales.

languageHints(List<String>)List<String>No

Importante

  • Este parámetro es una matriz, pero la versión actual solo procesa el primer elemento. Pase un único valor.
  • Este parámetro especifica el idioma de destino para la síntesis de voz. No está relacionado con el idioma de la muestra de audio utilizada en la clonación de voz. Para establecer el idioma de origen de una tarea de clonación, consulte la referencia de la API de clonación de voz.

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:

  • Pronunciación inesperada de dígitos: "hola, esto es 110" se lee como "hola, esto es uno cero" en lugar de la pronunciación china esperada
  • Pronunciación incorrecta de símbolos: "@" se lee como su equivalente en chino en lugar de "arroba"
  • Mala calidad de síntesis en idiomas minoritarios con resultados poco naturales

Valores válidos

  • zh: Chino
  • en: Inglés
  • fr: Francés
  • de: Alemán
  • ja: Japonés
  • ko: Coreano
  • ru: Ruso
  • pt: Portugués
  • th: Tailandés
  • id: Indonesio
  • vi: Vietnamita
  • es: Español
  • it: Italiano
  • ms: Malayo
  • fil: Filipino
  • ar: Árabe
instruction(String)StringNoControla 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)ParamHotFixNoConfigura correcciones de pronunciación y reemplazos de texto aplicados antes de la síntesis.

Parámetros:

  • pronunciation: Pronunciación personalizada. Especifica anotaciones pinyin para palabras con el fin de corregir pronunciaciones predeterminadas inexactas.
  • replace: Reemplazo de texto. Reemplaza palabras específicas con texto de destino antes de la síntesis. El texto reemplazado se utiliza como entrada real de la síntesis.

Ejemplo:

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("longanhuan_v3.6") // Voice
                        .hotFix(paramHotFix)
                        .build();
parameter(String key, Object value)String, ObjectNoEstablece parámetros adicionales.
parameters(Map<String, Object>)MapNoEstablece parámetros adicionales.

Parámetros adicionales

Se establece mediante parameter() o parameters().

Ejemplo:

SpeechSynthesisParam param = SpeechSynthesisParam.builder()
  .model("qwen-audio-3.0-tts-flash")
  .voice("longanhuan_v3.6")
  .parameter("bit_rate", 32)
  .build();
ParámetroTipoObligatorioDescripción
bit_rateintegerNoLa 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_tagbooleanNoEspecifica 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.

aigc_propagatorStringNoEstablece 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.

aigc_propagate_idStringNoEstablece 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.

ResultCallback

Paquete: com.alibaba.dashscope.common.ResultCallback

onEvent() - Recibir datos de audio

Firma del método:

public void onEvent(SpeechSynthesisResult result)

Parámetros:

ParámetroTipoObligatorioDescripción
resultSpeechSynthesisResultSí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ámetroTipoObligatorioDescripción
eExceptionSí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:

CampoTipoDescripción
typeStringTipo 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_textStringTexto original de la oración actual. Se devuelve en los eventos sentence-begin y sentence-end.
sentenceJsonObjectInformació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

imagen

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 = "qwen-audio-3.0-tts-flash";
    // Voice
    private static String voice = "longanhuan_v3.6";

    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

imagen

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 = "qwen-audio-3.0-tts-flash";
    // Voice
    private static String voice = "longanhuan_v3.6";

    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

imagen

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 streamingCall varias 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 streamingComplete rá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 = "qwen-audio-3.0-tts-flash"; // Model
    private static String voice = "longanhuan_v3.6"; // 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 = "qwen-audio-3.0-tts-flash"; // Model
    private static String voice = "longanhuan_v3.6"; // 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 = "qwen-audio-3.0-tts-flash";
    private static String voice = "longanhuan_v3.6";

    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.