Tous les produits
Search
Centre de documentation

Alibaba Cloud Model Studio:Traduction vocale en temps réel avec qwen3.8-livetranslate-flash-realtime via AOQ

Dernière mise à jour :Nov 06, 2026

Ce tutoriel utilise Android pour montrer comment intégrer le SDK client AOQ avec qwen3.8-livetranslate-flash-realtime afin d'assurer une traduction vocale en temps réel. Les autres plateformes utilisent des flux d'intégration et de gestion des événements modèles similaires, avec leurs méthodes SDK respectives. Tous les exemples côté client ci-dessous utilisent Java.

RemarquePour les capacités du modèle, consultez Traduction vocale et audio/vidéo en temps réel. Pour les rôles des médias et des événements du modèle, consultez Connexion AOQ.

Télécharger le SDK

Pour le SDK client AOQ et le plugin audio Opus, consultez Téléchargement du SDK. Les exemples de transmission audio de ce tutoriel utilisent Opus, vous devez donc également importer le plugin correspondant à votre plateforme. Pour les régions prises en charge, les langues, les tarifs et les limites de débit, voir Traduction vocale et audio/vidéo en temps réel.

Importer le SDK

Android

Placez AoqClientSdk-release.aar dans app/libs/. Placez libPluginOpus.so dans app/libs/armeabi-v7a/ et app/libs/arm64-v8a/, selon l'ABI.

Déclarez la dépendance dans app/build.gradle :

android {
    defaultConfig {
        minSdk 21
        ndk { abiFilters 'armeabi-v7a', 'arm64-v8a' }
    }
    sourceSets { main { jniLibs.srcDirs = ['libs'] } }
}

dependencies {
    implementation fileTree(dir: 'libs', include: ['*.aar'])
}

Si le projet hôte contient déjà des bibliothèques natives portant les mêmes noms, vérifiez leurs versions et dépendances avant leur empaquetage afin d'éviter d'inclure différentes versions d'une même bibliothèque.

Déclarez les autorisations dans AndroidManifest.xml :

<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />

Demandez l'autorisation RECORD_AUDIO au moment de l'exécution et obtenez l'autorisation de l'utilisateur. La traduction vocale seule ne nécessite pas l'autorisation de la caméra. Déclarez et demandez l'autorisation CAMERA uniquement lorsque vous activez l'amélioration visuelle.

Les exemples Java utilisent les importations suivantes :

import com.alibaba.aoq.clientsdk.AoqClientEngine;
import com.alibaba.aoq.clientsdk.AoqClientEngine.*;
import com.alibaba.aoq.clientsdk.AoqClientListener;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.Map;
import org.json.JSONException;
import org.json.JSONObject;

iOS

Glissez AoqClientSdk.framework et PluginOpus.framework dans votre projet Xcode. Sous Target > General > Frameworks, Libraries, and Embedded Content, sélectionnez Embed & Sign.

Déclarez la description d'utilisation du microphone dans Info.plist :

Clé

Exemple de valeur

NSMicrophoneUsageDescription

Pour capturer la parole destinée à la traduction en temps réel

Obtenez l'autorisation du microphone avant de démarrer la capture. Ajoutez NSCameraUsageDescription et demandez l'autorisation de la caméra uniquement lorsque vous activez l'amélioration visuelle.

Accédez au SDK via le module AoqClientSdk. Utilisez import AoqClientSdk en Swift ou #import <AoqClientSdk/AoqClientSdk.h> en Objective-C. Suivez le même flux de configuration de session et de gestion des événements que les exemples Android, en utilisant les méthodes SDK iOS correspondantes.

HarmonyOS

Placez AoqClientSdk.har dans le dossier libs/ situé à la racine du projet. Ajoutez l'entrée suivante à dependencies dans entry/oh-package.json5 :

{
  "dependencies": {
    "@aoq/client-sdk": "file:../libs/AoqClientSdk.har"
  }
}

Placez libPluginOpus.so dans entry/libs/armeabi-v7a/ et entry/libs/arm64-v8a/, selon l'ABI.

Ajoutez les autorisations suivantes à l'objet module dans entry/src/main/module.json5. Définissez perm_mic_reason dans les ressources de chaînes de l'application, par exemple, "Pour capturer la parole destinée à la traduction en temps réel" :

{
  "requestPermissions": [
    { "name": "ohos.permission.INTERNET" },
    {
      "name": "ohos.permission.MICROPHONE",
      "reason": "$string:perm_mic_reason",
      "usedScene": { "abilities": ["EntryAbility"], "when": "inuse" }
    }
  ]
}

Fusionnez ces champs aux objets existants de dépendance et de module. Ne remplacez pas entièrement les fichiers de configuration par ces extraits. Si votre application utilise un nom Ability différent, remplacez EntryAbility par ce nom.

Avant de démarrer la capture, utilisez abilityAccessCtrl.createAtManager().requestPermissionsFromUser pour demander l'autorisation du microphone au moment de l'exécution. Ajoutez l'autorisation de la caméra uniquement lorsque vous activez l'amélioration visuelle.

Obtenir un jeton via votre AppServer

Ne stockez pas la clé API Model Studio sur le client. Votre AppServer utilise la clé API correspondant à la région et à l'espace de travail pour demander des informations d'identification temporaires pour chaque connexion de traduction. Il renvoie ces informations d'identification via HTTPS à un client ayant passé l'authentification de votre application.

Configurez un AppServer selon les Étapes d’authentification AOQ de Qwen-LiveTranslate-Realtime. La traduction en temps réel utilise le protocole Realtime. Indiquez les éléments suivants lors de la demande d’informations de connexion :

model=qwen3.8-livetranslate-flash-realtime

Exécutez la commande suivante uniquement sur votre AppServer. Définissez BAILIAN_ENDPOINT comme point de terminaison de votre espace de travail : {WorkspaceId}.cn-beijing.maas.aliyuncs.com ou {WorkspaceId}.ap-southeast-1.maas.aliyuncs.com. Lisez DASHSCOPE_API_KEY depuis une variable d'environnement côté serveur ou un service de gestion des secrets.

curl --fail-with-body --request POST \
  "https://${BAILIAN_ENDPOINT}/api/v1/webrtc/realtime?model=qwen3.8-livetranslate-flash-realtime" \
  --header "Content-Type: application/json" \
  --header "Authorization: Bearer ${DASHSCOPE_API_KEY}" \
  --header "x-dashscope-rtc-transport: moq" \
  --data '{}'

Si votre AppServer peut obtenir l'adresse IP publique réelle du client à partir d'une chaîne de proxy fiable, incluez clientIp dans le corps de la requête pour la planification du point d'accès.

Mappez les champs de réponse vers AoqConnectConfig sur le client :

Champ de réponse de la passerelle

Champ du SDK

Description

aoqTokenForClient

token

Jeton temporaire pour cette connexion

sid

sid

Identifiant de cette session

clientRelayCertFingerprint

certFingerprint

Utilisez l'empreinte du certificat exactement telle qu'elle est retournée

clientRelayEndpoints

relayEndpoints

Convertissez la liste complète des points d'accès en structure du SDK

extraInfo.workspaceIdHash

workspaceIdHash

Utilisez la valeur retournée. Si elle est absente, suivez les exigences du SDK

Implémenter la traduction vocale en temps réel

Flux d'interaction

AOQ real-time speech translation sequence

Créer le moteur et définir les rappels

Appelez createEngine pour créer le moteur et enregistrer les rappels pour l'état de la connexion, les messages Data et les erreurs. Votre application doit conserver les objets moteur et écouteur pendant toute la durée de la session.

// 配置引擎工作目录。
AoqCreateConfig createConfig = new AoqCreateConfig();
createConfig.workDir = appCtx.getFilesDir().getAbsolutePath();
// 创建引擎并注册事件监听对象。
engine = AoqClientEngine.createEngine(appCtx, createConfig, listener);

Implémentez AoqClientListener pour recevoir les événements de connexion, de message du modèle et de périphérique multimédia, et transmettez-le lors de la création du moteur.

AoqClientListener listener = new AoqClientListener() {
    // 接收音频采集与播放设备状态。
    @Override
    public void onAudioDeviceStateChanged(AoqAudioDeviceState state) {

    }

    // 接收耳机、蓝牙及扬声器等音频路由变化。
    @Override
    public void onAudioDeviceRouteChanged(int routeType) {
        android.util.Log.i("LiveTranslate", "音频路由: " + AoqAudioDeviceRouteType.fromValue(routeType));
    }

    // 接收音频设备中断及中断结束通知。
    @Override
    public void onAudioDeviceInterrupted(boolean interrupt) {
        android.util.Log.i("LiveTranslate", "音频设备中断: " + interrupt);
    }

    // 接收 Android 音频焦点变化。
    @Override
    public void onAudioDeviceFocusChanged(int audioFocus) {
        android.util.Log.i("LiveTranslate", "音频焦点: " + audioFocus);
    }

    // 接收 SDK 异步错误。
    @Override
    public void onError(int code, String message) {
        android.util.Log.e("LiveTranslate", "SDK 错误: " + code + ", " + message);
    }
};

Démarrer la capture et la lecture audio

Après avoir obtenu l'autorisation du microphone, préparez la capture et la lecture audio. Désactivez la transmission audio avant de démarrer la capture pour empêcher le moteur d'envoyer automatiquement du contenu multimédia après la connexion.

Démarrer la capture ne signifie pas que le modèle est prêt à recevoir de l'audio. Incitez l'utilisateur à parler uniquement après avoir reçu session.updated et activé avec succès la transmission. Ce tutoriel n'implémente pas la mise en mémoire tampon audio avant la connexion.

// 关闭音频上行,等待模型配置确认。
engine.enableSendMediaStream(AoqTrackType.AoqTrackTypeAudio, false);

AoqAudioCaptureConfig audioCaptureConfig = new AoqAudioCaptureConfig();
audioCaptureConfig.channel = 1;
audioCaptureConfig.isExternal = false;
// 启动 SDK 内置麦克风采集。
engine.startAudioCapture(audioCaptureConfig);

AoqAudioPlaybackConfig audioPlaybackConfig = new AoqAudioPlaybackConfig();
audioPlaybackConfig.channel = 1;
audioPlaybackConfig.isExternal = false;
// 启动 SDK 内置播放器,播放下行译音。
engine.startAudioPlayer(audioPlaybackConfig);

Configurer les codecs et établir la connexion

Le scénario principal nécessite les pistes suivantes :

Direction

Piste

Objectif

Publier

Audio

Envoyer la parole au modèle

Publier

Données

Envoyer la configuration de session et les événements de fin

S'abonner

Audio

Recevoir la parole traduite

S'abonner

Données

Recevoir le texte source, le texte traduit et les événements d'état

Configurez séparément l'audio entrant et sortant. L'exemple suivant utilise Opus, 16 kHz et audio mono pour la transmission.

AoqAudioCodecConfig audioEncoderConfig = new AoqAudioCodecConfig();
audioEncoderConfig.codecType = AoqEncoderType.AoqEncoderTypeAudioOpus;
audioEncoderConfig.sampleRate = 16000;
audioEncoderConfig.channel = 1;
// 设置上行音频编码参数。
engine.setAudioEncoderConfig(audioEncoderConfig);

AoqAudioCodecConfig audioDecoderConfig = new AoqAudioCodecConfig();
audioDecoderConfig.codecType = AoqEncoderType.AoqEncoderTypeAudioOpus;
audioDecoderConfig.sampleRate = 16000;
audioDecoderConfig.channel = 1;
// 设置下行音频解码参数。
engine.setAudioDecoderConfig(audioDecoderConfig);

AoqTrackParam audioTrackParam = new AoqTrackParam();
audioTrackParam.trackType = AoqTrackType.AoqTrackTypeAudio;
AoqTrackParam dataTrackParam = new AoqTrackParam();
dataTrackParam.trackType = AoqTrackType.AoqTrackTypeData;
// 发布音频和 Data 轨,发送语音及模型事件。
connectConfig.publishTracks.add(audioTrackParam);
connectConfig.publishTracks.add(dataTrackParam);
// 订阅音频和 Data 轨,接收译音及字幕事件。
connectConfig.subscribeTracks.add(audioTrackParam);
connectConfig.subscribeTracks.add(dataTrackParam);

// 发起 AOQ 连接,连接结果通过 onConnectionStatusChange 返回。
engine.connect(connectConfig);

Un retour réussi de connect signifie que l'appel a été accepté. Attendez que onConnectionStatusChange signale une connexion réussie avant d'envoyer la configuration du modèle.

Configurer la session de traduction

Après la connexion, envoyez l'événement suivant session.update via la piste Data. Cette configuration utilise la voix par défaut et n'active pas le clonage vocal :

{
  "type": "session.update",
  "session": {
    "output_modalities": ["text", "audio"],
    "audio": {
      "input": {
        "turn_detection": {
          "type": "speaker_detection",
          "threshold": 0.5
        }
      }
    },
    "translation": {
      "language": "en"
    }
  }
}

Pour le flux d'interaction, voir Traduction en temps réel. Pour tous les paramètres session.update, voir Événements client de traduction en temps réel.

Codez le JSON en octets UTF-8 et envoyez-le avec sendDataMsg.

private static final String SESSION_UPDATE_JSON =
        "{\"type\":\"session.update\",\"session\":{"
        + "\"output_modalities\":[\"text\",\"audio\"],"
        + "\"audio\":{\"input\":{\"turn_detection\":{"
        + "\"type\":\"speaker_detection\",\"threshold\":0.5}}},"
        + "\"translation\":{\"language\":\"en\"}}}";

// 接收 AOQ 连接状态变化。
@Override
public void onConnectionStatusChange(AoqConnectionStatus status) {
    if (status == AoqConnectionStatus.AoqConnectionStatusConnected) {
        AoqDataMsg message = new AoqDataMsg();
        message.data = SESSION_UPDATE_JSON.getBytes(StandardCharsets.UTF_8);
        // 通过 Data 轨发送翻译会话配置。
        engine.sendDataMsg(message);
    }
}

Activer la transmission multimédia après réception de session.updated

Analysez le type d'événement dans onDataMsg.

L'extrait de code suivant achemine les événements de contrôle. Pour les événements de texte source et texte traduit, voir Événements serveur. Après avoir reçu session.finished, définissez l'état de fin de votre application et utilisez la méthode de libération décrite dans « Déconnexion et destruction du moteur » pour effectuer le nettoyage.

// 接收 Data 轨中的模型事件。
@Override
public void onDataMsg(AoqDataMsg message) {
    if (message == null || message.data == null) { return; }
    try {
        JSONObject event = new JSONObject(new String(message.data, StandardCharsets.UTF_8));
        String type = event.optString("type");
        if ("session.updated".equals(type)) {
            // 模型配置生效后,开启音频上行。
            engine.enableSendMediaStream(AoqTrackType.AoqTrackTypeAudio, true);
        } else if ("session.finished".equals(type)) {
            android.util.Log.i("LiveTranslate", "服务端已完成本次翻译处理");
        } else if ("error".equals(type)) {
            android.util.Log.e("LiveTranslate", "翻译模型错误: " + event.optJSONObject("error"));
        }
    } catch (JSONException error) {
        android.util.Log.e("LiveTranslate", "无法解析模型事件", error);
    }
}

RemarqueDifférences entre AOQ et WebSocket : cet exemple envoie et reçoit l’audio directement sur les pistes Audio. N’envoyez pas input_audio_buffer.append pour transmettre une deuxième fois le même audio, et ne copiez pas l’exemple WebSocket en démarrant un autre lecteur à la réception de response.audio.delta. L’audio traduit est géré par l’abonnement audio et le lecteur du SDK. La piste Data transporte les sous-titres et les événements du modèle. Pour l’utilisation des pistes de médias et Data, consultez Connexion AOQ.

Terminer l'entrée et attendre la traduction finale

Lorsque l'utilisateur arrête la traduction, suivez cet ordre :

  1. Arrêtez la capture afin qu'aucune nouvelle entrée ne soit soumise.
  2. Après avoir finalisé l'entrée, désactivez l'audio montant et envoyez session.finish une seule fois. Conservez la connexion AOQ, l'abonnement Data et la lecture audio actifs.
  3. Continuez à recevoir le texte source final, le texte traduit et la parole traduite. Attendez session.finished.
  4. Déconnectez-vous et libérez les ressources selon votre politique locale de fin de lecture.

Pour les exigences relatives à l'envoi de session.finish, voir Événements client de traduction en temps réel. Pour les champs et le sens de session.finished, voir Événements serveur de traduction en temps réel.

AoqDataMsg message = new AoqDataMsg();
message.data = "{\"type\":\"session.finish\"}".getBytes(StandardCharsets.UTF_8);
// 通知模型输入结束,等待 session.finished。
engine.sendDataMsg(message);

Déconnecter et détruire le moteur

Après réception de session.finished, le serveur a terminé le traitement de cette session. Cela ne signifie pas que le haut-parleur local a terminé la lecture du dernier segment audio. Si votre application doit lire toute la parole traduite, terminez la lecture locale avant d'exécuter les méthodes suivantes.

// 停止译音播放。
engine.stopAudioPlayer();
// 断开 AOQ 连接。
engine.disconnect();
// 销毁本会话独占的引擎。
AoqClientEngine.destroy();

Documentation associée