Skip to main content

Guía de conexión WebSocket

Acceda al servicio de síntesis de voz en tiempo real de Qwen-Audio-TTS mediante una conexión WebSocket. Cubre endpoints de servicio, encabezados de solicitud y el flujo de trabajo de interacción cliente-servidor.

Límites de velocidad: Las llamadas al modelo están sujetas a límites de velocidad. Cuando se supera un límite, el servidor devuelve el error Requests rate limit exceeded, please try again later. Reduzca su tasa de solicitudes o concurrencia y vuelva a intentarlo. Para conocer los límites de velocidad de cada modelo, consulte Limitación de velocidad.

Endpoints de servicio

URL fija de WebSocket:
  • Singapore
  • China (Beijing)
wss://{WorkspaceId}.ap-southeast-1.maas.aliyuncs.com/api-ws/v1/inferenceReemplace {WorkspaceId} con su ID de espacio de trabajo real.
Alibaba 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

Encabezados de solicitud

Incluya los siguientes encabezados en la solicitud:
ParámetroTipoObligatorioDescripción
AuthorizationstringSíFormato: Bearer <API key>. Reemplace <API key> con su clave API.
user-agentstringNoIdentificador de cliente para el seguimiento de solicitudes.
X-DashScope-WorkSpacestringNoID de espacio de trabajo de Alibaba Cloud Model Studio workspace ID.
X-DashScope-DataInspectionstringNoHabilita la inspección de datos. Valor predeterminado: enable. Déjelo sin configurar a menos que sea necesario.
La autorización se verifica durante el protocolo de enlace de WebSocket. Si la clave API no es válida o falta, el protocolo de enlace falla con un error HTTP 401/403.

Flujo de trabajo de interacción

imagen
Para obtener detalles sobre los eventos del lado del cliente y del servidor, consulte Eventos del cliente y Eventos del lado del servidor. Secuencia de interacción cliente-servidor:
  1. Establecer una conexión: Cree una conexión WebSocket con el servidor.
  2. Iniciar una tarea: Enviar un evento run-task.
  3. Esperar confirmación: Reciba un evento task-started del servidor antes de continuar.
  4. Enviar texto para síntesis: Envíe uno o más eventos continue-task con el texto a sintetizar, en secuencia. El servidor devuelve un evento result-generated con una secuencia de audio después de cada oración completa. Para conocer los límites de longitud de texto, consulte el campo text en el evento continue-task.
    Envíe múltiples eventos continue-task con segmentos de texto en orden. El servidor divide el texto en oraciones:
    • Las oraciones completas se sintetizan y se devuelven como audio inmediatamente.
    • Las oraciones incompletas se almacenan en búfer hasta completarse.
    Un evento finish-task fuerza la síntesis de todo el contenido almacenado en búfer.
  5. Recibir audio: Lea la secuencia de audio desde el canal binary.
  6. Finalizar la tarea: Después de enviar todo el texto, envíe un evento finish-task y continúe recibiendo audio. Este paso es obligatorio; omitirlo puede producir audio incompleto.
  7. Recibir finalización de tarea: Un evento task-finished del servidor confirma que la tarea está completa.
  8. Cerrar la conexión: Desconectar el WebSocket.
Reutilice la conexión WebSocket entre tareas en lugar de crear una nueva para cada una.
Todos los eventos en una tarea de síntesis (run-task, continue-task, finish-task) deben compartir el mismo task_id. Genere un nuevo task_id (como un UUID) para cada tarea. Los valores no coincidentes provocan corrupción de audio o fallo de la tarea.

Ejemplos de código

Estos ejemplos usan bibliotecas WebSocket genéricas para enviar eventos directamente con qwen-audio-3.1-tts-flash y la voz longanhuan_v3.1. Obtenga una clave API, defina DASHSCOPE_API_KEY y sustituya {WorkspaceId} por el ID del workspace de la región. Al cambiar de región, cambie tanto el endpoint como la clave API.
  • Java
  • Python
  • Go
  • C#
  • PHP
  • Node.js
Recomendamos utilizar el SDK de Java DashScope para el desarrollo. Consulte Qwen-Audio-TTS SDK de Java / CosyVoice SDK de Java.El siguiente es un ejemplo de conexión directa WebSocket en Java. Importe estas dependencias antes de ejecutar:
  • Java-WebSocket
  • jackson-databind
Utilice Maven o Gradle para gestionar las dependencias:
pom.xml
<dependencies>
    <!-- WebSocket Client -->
    <dependency>
        <groupId>org.java-websocket</groupId>
        <artifactId>Java-WebSocket</artifactId>
        <version>1.5.3</version>
    </dependency>

    <!-- JSON Processing -->
    <dependency>
        <groupId>com.fasterxml.jackson.core</groupId>
        <artifactId>jackson-databind</artifactId>
        <version>2.13.0</version>
    </dependency>
</dependencies>
Código Java:
import com.fasterxml.jackson.databind.ObjectMapper;

import org.java_websocket.client.WebSocketClient;
import org.java_websocket.handshake.ServerHandshake;

import java.io.FileOutputStream;
import java.io.IOException;
import java.net.URI;
import java.nio.ByteBuffer;
import java.util.*;

public class TTSWebSocketClient extends WebSocketClient {
    private final String taskId = UUID.randomUUID().toString();
    private final String outputFile = "output_" + System.currentTimeMillis() + ".mp3";
    private boolean taskFinished = false;

    public TTSWebSocketClient(URI serverUri, Map<String, String> headers) {
        super(serverUri, headers);
    }

    @Override
    public void onOpen(ServerHandshake serverHandshake) {
        System.out.println("Connection established");

        // Send the run-task event
        // If enable_ssml is set to true, only one continue-task event can be sent; otherwise the error "Text request limit violated, expected 1." will be returned
        String runTaskCommand = "{ \"header\": { \"action\": \"run-task\", \"task_id\": \"" + taskId + "\", \"streaming\": \"duplex\" }, \"payload\": { \"task_group\": \"audio\", \"task\": \"tts\", \"function\": \"SpeechSynthesizer\", \"model\": \"qwen-audio-3.1-tts-flash\", \"parameters\": { \"text_type\": \"PlainText\", \"voice\": \"longanhuan_v3.1\", \"format\": \"mp3\", \"sample_rate\": 22050, \"volume\": 50, \"rate\": 1, \"pitch\": 1, \"enable_ssml\": false }, \"input\": {} }}";
        send(runTaskCommand);
    }

    @Override
    public void onMessage(String message) {
        System.out.println("Received message from server: " + message);
        try {
            // Parse JSON message
            Map<String, Object> messageMap = new ObjectMapper().readValue(message, Map.class);

            if (messageMap.containsKey("header")) {
                Map<String, Object> header = (Map<String, Object>) messageMap.get("header");

                if (header.containsKey("event")) {
                    String event = (String) header.get("event");

                    if ("task-started".equals(event)) {
                        System.out.println("Received task-started event from server");

                        List<String> texts = Arrays.asList(
                                "Before my bed, moonlight shines bright,I suspect it\'s frost upon the ground.",
                                "I raise my eyes to gaze at the bright moon,then bow my head, thinking of home."
                        );

                        for (String text : texts) {
                            // Send the continue-task event
                            sendContinueTask(text);
                        }

                        // Send the finish-task event
                        sendFinishTask();
                    } else if ("task-finished".equals(event)) {
                        System.out.println("Received task-finished event from server");
                        taskFinished = true;
                        closeConnection();
                    } else if ("task-failed".equals(event)) {
                        System.out.println("Task failed: " + message);
                        closeConnection();
                    }
                }
            }
        } catch (Exception e) {
            System.err.println("Exception occurred: " + e.getMessage());
        }
    }

    @Override
    public void onMessage(ByteBuffer message) {
        System.out.println("Received binary audio data of size: " + message.remaining());

        try (FileOutputStream fos = new FileOutputStream(outputFile, true)) {
            byte[] buffer = new byte[message.remaining()];
            message.get(buffer);
            fos.write(buffer);
            System.out.println("Audio data written to local file " + outputFile);
        } catch (IOException e) {
            System.err.println("Failed to write audio data to local file: " + e.getMessage());
        }
    }

    @Override
    public void onClose(int code, String reason, boolean remote) {
        System.out.println("Connection closed: " + reason + " (" + code + ")");
    }

    @Override
    public void onError(Exception ex) {
        System.err.println("Error: " + ex.getMessage());
        ex.printStackTrace();
    }

    private void sendContinueTask(String text) {
        String command = "{ \"header\": { \"action\": \"continue-task\", \"task_id\": \"" + taskId + "\", \"streaming\": \"duplex\" }, \"payload\": { \"input\": { \"text\": \"" + text + "\" } }}";
        send(command);
    }

    private void sendFinishTask() {
        String command = "{ \"header\": { \"action\": \"finish-task\", \"task_id\": \"" + taskId + "\", \"streaming\": \"duplex\" }, \"payload\": { \"input\": {} }}";
        send(command);
    }

    private void closeConnection() {
        if (!isClosed()) {
            close();
        }
    }

    public static void main(String[] args) {
        try {
            // The API Key differs between the Singapore and Beijing regions. Get your API Key: https://www.alibabacloud.com/help/model-studio/get-api-key
            // If you have not configured the environment variable, replace the next line with your Model Studio API Key: String apiKey = "sk-xxx"
            String apiKey = System.getenv("DASHSCOPE_API_KEY");
            if (apiKey == null || apiKey.isEmpty()) {
                System.err.println("Please set the DASHSCOPE_API_KEY environment variable");
                return;
            }

            Map<String, String> headers = new HashMap<>();
            headers.put("Authorization", "bearer " + apiKey);
            // The following is the configuration for the Singapore region. Replace "{WorkspaceId}" with your actual workspace ID. The configuration varies by region.
            TTSWebSocketClient client = new TTSWebSocketClient(new URI("wss://{WorkspaceId}.ap-southeast-1.maas.aliyuncs.com/api-ws/v1/inference"), headers);

            client.connect();

            while (!client.isClosed() && !client.taskFinished) {
                Thread.sleep(1000);
            }
        } catch (Exception e) {
            System.err.println("Failed to connect to WebSocket service: " + e.getMessage());
            e.printStackTrace();
        }
    }
}