Skip to main content

SDK de Java

Este tema describe las interfaces clave y los parámetros de solicitud para llamar a los modelos de la serie Qwen-TTS-Realtime con DashScope Java SDK.

Requisitos previos

Requiere DashScope Java SDK 2.22.7 o posterior.

Introducción

  • Modo de confirmación del servidor
  • Modo de confirmación
appendText()
import com.alibaba.dashscope.audio.qwen_tts_realtime.*;
import com.alibaba.dashscope.exception.NoApiKeyException;
import com.google.gson.JsonObject;
import javax.sound.sampled.LineUnavailableException;
import javax.sound.sampled.SourceDataLine;
import javax.sound.sampled.AudioFormat;
import javax.sound.sampled.DataLine;
import javax.sound.sampled.AudioSystem;
import java.io.*;
import java.util.Base64;
import java.util.Queue;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicReference;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.atomic.AtomicBoolean;

public class Main {
    static String[] textToSynthesize = {
            "Right? I really love this kind of supermarket.",
            "Especially during the Chinese New Year.",
            "Going to the supermarket.",
            "It just makes me feel.",
            "Super, super happy!",
            "I want to buy so many things!"
    };
    public static QwenTtsRealtimeAudioFormat ttsFormat = QwenTtsRealtimeAudioFormat.PCM_24000HZ_MONO_16BIT;

    // Real-time PCM audio player
    public static class RealtimePcmPlayer {
        private int sampleRate;
        private SourceDataLine line;
        private AudioFormat audioFormat;
        private Thread decoderThread;
        private Thread playerThread;
        private AtomicBoolean stopped = new AtomicBoolean(false);
        private Queue<String> b64AudioBuffer = new ConcurrentLinkedQueue<>();
        private Queue<byte[]> RawAudioBuffer = new ConcurrentLinkedQueue<>();
        private ByteArrayOutputStream totalAudioStream = new ByteArrayOutputStream();

        // Initialize the audio format and audio line.
        public RealtimePcmPlayer(int sampleRate) throws LineUnavailableException {
            this.sampleRate = sampleRate;
            this.audioFormat = new AudioFormat(this.sampleRate, 16, 1, true, false);
            DataLine.Info info = new DataLine.Info(SourceDataLine.class, audioFormat);
            line = (SourceDataLine) AudioSystem.getLine(info);
            line.open(audioFormat);
            line.start();
            decoderThread = new Thread(new Runnable() {
                @Override
                public void run() {
                    while (!stopped.get()) {
                        String b64Audio = b64AudioBuffer.poll();
                        if (b64Audio != null) {
                            byte[] rawAudio = Base64.getDecoder().decode(b64Audio);
                            RawAudioBuffer.add(rawAudio);
                            // Write audio data to totalAudioStream.
                            try {
                                totalAudioStream.write(rawAudio);
                            } catch (IOException e) {
                                throw new RuntimeException(e);
                            }
                        } else {
                            try {
                                Thread.sleep(100);
                            } catch (InterruptedException e) {
                                throw new RuntimeException(e);
                            }
                        }
                    }
                }
            });
            playerThread = new Thread(new Runnable() {
                @Override
                public void run() {
                    while (!stopped.get()) {
                        byte[] rawAudio = RawAudioBuffer.poll();
                        if (rawAudio != null) {
                            try {
                                playChunk(rawAudio);
                            } catch (IOException e) {
                                throw new RuntimeException(e);
                            } catch (InterruptedException e) {
                                throw new RuntimeException(e);
                            }
                        } else {
                            try {
                                Thread.sleep(100);
                            } catch (InterruptedException e) {
                                throw new RuntimeException(e);
                            }
                        }
                    }
                }
            });
            decoderThread.start();
            playerThread.start();
        }

        // Play an audio chunk and block until playback completes.
        private void playChunk(byte[] chunk) throws IOException, InterruptedException {
            if (chunk == null || chunk.length == 0) return;

            int bytesWritten = 0;
            while (bytesWritten < chunk.length) {
                bytesWritten += line.write(chunk, bytesWritten, chunk.length - bytesWritten);
            }
            int audioLength = chunk.length / (this.sampleRate*2/1000);
            // Wait for the buffered audio to finish playing.
            Thread.sleep(audioLength - 10);
        }

        public void write(String b64Audio) {
            b64AudioBuffer.add(b64Audio);
        }

        public void cancel() {
            b64AudioBuffer.clear();
            RawAudioBuffer.clear();
        }

        public void waitForComplete() throws InterruptedException {
            while (!b64AudioBuffer.isEmpty() || !RawAudioBuffer.isEmpty()) {
                Thread.sleep(100);
            }
            line.drain();
        }

        public void shutdown() throws InterruptedException, IOException {
            stopped.set(true);
            decoderThread.join();
            playerThread.join();

            // Save the complete audio file.
            File file = new File("TotalAudio_"+ttsFormat.getSampleRate()+"."+ttsFormat.getFormat());
            try (FileOutputStream fos = new FileOutputStream(file)) {
                fos.write(totalAudioStream.toByteArray());
            }

            if (line != null && line.isRunning()) {
                line.drain();
                line.close();
            }
        }
    }

    public static void main(String[] args) throws InterruptedException, LineUnavailableException, IOException {
        QwenTtsRealtimeParam param = QwenTtsRealtimeParam.builder()
                .model("qwen3-tts-flash-realtime")
                // China (Beijing) region
                .url("wss://dashscope-intl.aliyuncs.com/api-ws/v1/realtime")
                // API keys differ between Singapore and China (Beijing). See https://www.alibabacloud.com/help/model-studio/get-api-key.
                .apikey(System.getenv("DASHSCOPE_API_KEY"))
                .build();
        AtomicReference<CountDownLatch> completeLatch = new AtomicReference<>(new CountDownLatch(1));
        final AtomicReference<QwenTtsRealtime> qwenTtsRef = new AtomicReference<>(null);

        // Create a real-time audio player instance.
        RealtimePcmPlayer audioPlayer = new RealtimePcmPlayer(24000);

        QwenTtsRealtime qwenTtsRealtime = new QwenTtsRealtime(param, new QwenTtsRealtimeCallback() {
            @Override
            public void onOpen() {
                // Handle connection establishment.
            }
            @Override
            public void onEvent(JsonObject message) {
                String type = message.get("type").getAsString();
                switch(type) {
                    case "session.created":
                        // Handle session creation.
                        if (message.has("session")) {
                            String eventId = message.get("event_id").getAsString();
                            String sessionId = message.get("session").getAsJsonObject().get("id").getAsString();
                            System.out.println("[onEvent] session.created, session_id: "
                                    + sessionId + ", event_id: " + eventId);
                        }
                        break;
                    case "response.audio.delta":
                        String recvAudioB64 = message.get("delta").getAsString();
                        // Play audio in real time.
                        audioPlayer.write(recvAudioB64);
                        break;
                    case "response.done":
                        // Handle response completion.
                        break;
                    case "session.finished":
                        // Handle session termination.
                        completeLatch.get().countDown();
                    default:
                        break;
                }
            }
            @Override
            public void onClose(int code, String reason) {
                // Handle connection closure.
            }
        });
        qwenTtsRef.set(qwenTtsRealtime);
        try {
            qwenTtsRealtime.connect();
        } catch (NoApiKeyException e) {
            throw new RuntimeException(e);
        }
        QwenTtsRealtimeConfig config = QwenTtsRealtimeConfig.builder()
                .voice("Cherry")
                .responseFormat(ttsFormat)
                .mode("server_commit")
                .build();
        qwenTtsRealtime.updateSession(config);
        for (String text:textToSynthesize) {
            qwenTtsRealtime.appendText(text);
            Thread.sleep(100);
        }
        qwenTtsRealtime.finish();
        completeLatch.get().await();
        qwenTtsRealtime.close();

        // Wait for audio playback to complete, then shut down the player.
        audioPlayer.waitForComplete();
        audioPlayer.shutdown();
        System.exit(0);
    }
}
Para más muestras, consulte el repositorio de GitHub.

Parámetros de solicitud

Configure los siguientes parámetros de solicitud con los métodos de encadenamiento o setters de un objeto QwenTtsRealtimeParam, y luego pase el objeto al constructor QwenTtsRealtime.

Parámetro

Tipo

Obligatorio

Descripción

model

String

Sí

Nombre del modelo (consulte Modelos compatibles).

url

String

Sí

China (Pekín): wss://dashscope.aliyuncs.com/api-ws/v1/realtime

Singapur: wss://dashscope-intl.aliyuncs.com/api-ws/v1/realtime

Configure los siguientes parámetros de solicitud con los métodos de encadenamiento o setters de un objeto QwenTtsRealtimeConfig, y luego pase el objeto al método updateSession.

Parámetro

Tipo

Obligatorio

Descripción

voice

String

Sí

La voz para la síntesis de voz. Para obtener más información, consulte Lista de voces de Qwen-TTS.

languageType

String

No

Especifica el idioma del audio sintetizado. El valor predeterminado es Auto.

  • Auto: Utilice este valor cuando el idioma del texto sea incierto o contenga varios idiomas. El modelo asocia automáticamente la pronunciación para diferentes segmentos de idioma en el texto, pero no puede garantizar una precisión perfecta.

  • Idioma específico: Utilice esta opción para texto en un solo idioma. Especificar un idioma mejora significativamente la calidad de la síntesis y generalmente produce mejores resultados que Auto. Los valores válidos incluyen los siguientes:

    • Chinese

    • English

    • German

    • Italian

    • Portuguese

    • Spanish

    • Japanese

    • Korean

    • French

    • Russian

mode

String

No

El modo de interacción. Valores válidos:

  • server_commit (predeterminado): El servidor determina automáticamente cuándo sintetizar la voz, equilibrando latencia y calidad. Este modo se recomienda para la mayoría de los escenarios.

  • commit: El cliente activa manualmente la síntesis. Este modo tiene la latencia más baja, pero usted debe gestionar la integridad de las oraciones por su cuenta.

format

String

No

El formato de la salida de audio del modelo.

Formatos admitidos:

  • pcm (predeterminado)

  • wav

  • mp3

  • opus

sampleRate

int

No

La frecuencia de muestreo de la salida de audio del modelo, en Hz.

Frecuencias de muestreo admitidas:

  • 8000

  • 16000

  • 24000 (predeterminado)

  • 48000

speechRate

float

No

La velocidad de habla del audio. Un valor de 1.0 es la velocidad normal. Un valor menor que 1.0 es más lento y un valor mayor que 1.0 es más rápido.

Valor predeterminado: 1.0.

Rango válido: [0.5, 2.0].

volume

int

No

El volumen del audio.

Rango válido: [0, 100].

pitchRate

float

No

El tono del audio sintetizado.

Valor predeterminado: 1.0.

Rango válido: [0.5, 2.0].

bitRate

int

No

Especifica la tasa de bits del audio en kbps. Una tasa de bits más alta resulta en una mejor calidad de audio y un tamaño de archivo mayor. Este parámetro está disponible solo cuando el formato de audio (response_format) se establece en opus.

Valor predeterminado: 128.

Rango válido: [6, 510].

Interfaces clave

Clase QwenTtsRealtime

Para importar:
import com.alibaba.dashscope.audio.qwen_tts_realtime.QwenTtsRealtime;
MétodoFirmaEventos del servidorDescripción
connect
public void connect() throws NoApiKeyException, InterruptedException
session.created
Sesión creada
session.updated
Configuración de sesión actualizada
Abre una conexión WebSocket con el servidor.
updateSession
public void updateSession(QwenTtsRealtimeConfig config)
session.updated
Configuración de sesión actualizada
Actualiza la configuración de la sesión. Consulte Parámetros de solicitud.Después de conectarse, el servidor devuelve configuraciones de entrada y salida predeterminadas para la sesión. Llame a este método inmediatamente después de connect() para anular los valores predeterminados.El servidor valida los parámetros al recibir un evento session.update. Si algún parámetro no es válido, el servidor devuelve un error. De lo contrario, actualiza la configuración de la sesión en el lado del servidor.
appendText
public void appendText(String text)
NoneAgrega un segmento de texto al búfer de entrada del lado del servidor. El búfer almacena el texto hasta que usted lo envía.
  • En el modo server_commit, el servidor decide cuándo confirmar y sintetizar el texto almacenado en el búfer.
  • En el modo commit, el cliente debe activar la síntesis llamando a commit.
clearAppendedText
public void clearAppendedText()
input_text_buffer.cleared
Borra el texto recibido por el servidor
Borra todo el texto en el búfer de entrada del lado del servidor.
commit
public void commit()
input_text_buffer.committed
Confirma el texto y activa la síntesis de voz
response.output_item.added
Aparece nuevo contenido de salida en la respuesta
response.content_part.added
Nuevo contenido de salida agregado al elemento de mensaje del asistente
response.audio.delta
El modelo genera audio incrementalmente
response.audio.done
Generación de audio completada
response.content_part.done
La transmisión del contenido de audio para el mensaje del asistente ha finalizado
response.output_item.done
La transmisión del elemento de salida completo para el mensaje del asistente ha terminado
response.done
Respuesta completada
Confirma el texto agregado previamente al búfer del lado del servidor y sintetiza todo el texto inmediatamente. Devuelve un error si el búfer está vacío.
  • En el modo server_commit, el cliente no necesita llamar a este método. El servidor confirma automáticamente.
  • En el modo commit, el cliente debe llamar a commit para activar la síntesis.
finish
public void finish()
session.finished
Respuesta completada
Detiene la tarea actual.
close
public void close()
NoneCierra la conexión.
getSessionId
public String getSessionId()
NoneDevuelve el ID de sesión de la tarea actual.
getResponseId
public String getResponseId()
NoneDevuelve el ID de respuesta de la respuesta más reciente.
getFirstAudioDelay
public long getFirstAudioDelay()
NoneDevuelve la latencia del primer paquete de audio en milisegundos.

Interfaz de callback (QwenTtsRealtimeCallback)

MétodoParámetrosValor de retornoDescripción
public void onOpen()
NoneNoneSe llama inmediatamente después de establecer la conexión WebSocket.
public abstract void onEvent(JsonObject message)
message: evento de respuesta del lado del servidor.NoneSe llama cuando el servidor envía un evento, incluidas las respuestas a llamadas de API y el audio generado por el modelo. Consulte Eventos del lado del servidor.
public abstract void onClose(int code, String reason)
code: Código de estado de cierre de WebSocket.reason: Motivo de cierre.NoneLlamado después de que el servidor cierra la conexión.