Skip to main content

Guía de conexión WebSocket

Acceda al servicio de síntesis de voz en tiempo real de CosyVoice 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 cosyvoice-v3-flash y la voz longanyang. 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\": \"cosyvoice-v3-flash\", \"parameters\": { \"text_type\": \"PlainText\", \"voice\": \"longanyang\", \"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();
        }
    }
}