# 硅基数字人SDK

# 一、产品介绍

2D 数字人虚拟人SDK ,可以通过语音完成对虚拟人实时驱动。

# 1.1 应用场景

  • 部署成本低:适用于大屏终端、政务展厅、银行等无人值守场景。
  • 网络依赖小:完全本地运行,无需联网,可在地铁、偏远地区稳定运行。
  • 功能多样化:可服务于导览讲解、问答客服、智能陪伴等多种业务形态。

# 1.2 核心功能

  • 数字人形象定制与本地渲染
  • 实时语音驱动播报(支持 WAV 播放和 PCM 推送)
  • 动作播放控制(指定动作、随机动作)
  • 资源自动下载管理

# 二、SDK集成

# 2.1 支持的系统和硬件版本

项目 描述
系统 支持 Android 10+ 系统。
CPU架构 armeabi-v7a, arm64-v8a
硬件要求 要求设备 CPU8 核及以上(骁龙8 Gen2),内存 8G 及以上。可用存储空间 1GB 及以上。
网络 无(完全本地运行)
开发 IDE Android Studio Giraffe 2022.3.1 Patch 2
内存要求 可用于数字人的内存 >= 800MB

编译项目的Gradle使用的JDK版本为17,需要在File->Setting->Build,Execution,Deployment->Grade Projects->Gradle JDK: ${选择一个17版本的JDK}


# 2. SDK集成

  1. 前往下载中心 (opens new window)
  2. 引入 sdk aar 包: duix_client_sdk_release_${version}.aar
  3. app 目录新建 libs 目录,放入 aar 包,在 build.gradle 中增加配置如下
dependencies {
    // duix_client_sdk_release_${version}.aar放到libs目录下(必选)
    implementation fileTree(include: ['*.jar', '*.aar'], dir: 'libs')
    // SDK中签名需要使用 (必选)
    implementation 'com.auth0:java-jwt:3.18.1'
    // 如使用SDK中后台AI能力交互功能需要使用 (可选)
    implementation "org.java-websocket:Java-WebSocket:1.5.1"
    ...
}

权限要求, AndroidManifest.xml中,增加如下配置


<manifest xmlns:android="http://schemas.android.com/apk/res/android">
    <uses-permission android:name="android.permission.INTERNET" />
    <!-- 使用后台AI能力模式需要语音权限 -->
    <uses-permission android:name="android.permission.RECORD_AUDIO" />
</manifest>

# 三、使用流程概览

单纯的数字人渲染模式

graph LR
A[VirtualFactory初始化] --> B[检查配置与模型]
B[检查配置与模型] --> C[构建 Player 实例]
C --> D[调用 init 初始化]
D --> E[展示形象 / 渲染]
E --> F[PCM 或 WAV 音频驱动]
F --> G[播放控制与动作触发]
G --> E
G --> H[资源释放]

使用后台语音自动回复的交互模式

graph LR
A[VirtualFactory初始化] --> B[检查配置与模型]
B[检查配置与模型] --> C[构建 Player 实例]
C --> D[调用 init 初始化]
D --> E[展示形象 / 渲染]
E --> F[播报开场白]
F --> G[采集麦克风音频]
G --> H[返回语音识别内容]
G --> I[大模型回复]
I --> J[PCM音频驱动嘴形与动作触发]
J --> G
J --> K[资源释放]


使用后台语音识别功能,对接三方LLM,再使用文本驱动的交互模式

graph LR
A[VirtualFactory初始化] --> B[检查配置与模型]
B[检查配置与模型] --> C[构建 Player 实例]
C --> D[调用 init 初始化]
D --> E[展示形象 / 渲染]
E[配置开场白] --> |Yes|F[播报开场白]
E[配置开场白] --> |No|G[采集麦克风音频]
F --> G
G --> H[返回语音识别内容]
H --> I[对接三方LLM返回内容]
I --> J[文本驱动嘴形与动作触发]
J --> G
J --> K[资源释放]


# 四、SDK调用及API说明

# 4.1 初始化SDK

init接口定义: ai.guiji.duix.sdk.client.VirtualFactory

/**
 * 初始化,使用默认的工作路径(/sdcard/Android/data/包名/files/duix/)
 */
int init(Context context, String appId, String appKey);

/**
 * 自定义工作路径初始化
 */
int init(String appId, String appKey, String workPath)

参数说明:

参数 类型 描述
context Context 非空,App上下文
appId String 非空,平台生成的appId
appKey String 非空,平台生成的appKey
workPath String 自定义工作路径

返回参数说明:

取值 说明
0 初始化成功
-1000 设置文件路径失败
-1001 Context为空
-1002 appId为空
-1003 appKey为空

调用示例: 参考demo App实例 ai.guiji.duix.display.ui.activity.MainActivity

class MainActivity : BaseActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        VirtualFactory.init(mContext, mAppId, mAppKey)
    }
}

# 4.2 模型检查及下载

渲染需要下载模型文件及基础配置文件,每个模特对应一个模型文件压缩包,基础配置是运行模型需要的公共文件。SDK中提供了模型下载 的工具类VirtualModelUtil处理模型检查、下载及清理。

# 4.2.1 基础配置文件检查

函数定义:
ai.guiji.duix.sdk.client.VirtualModelUtil

/**
 * 返回基础配置是否下载完成
 */
boolean checkBaseConfig();

调用示例

if (VirtualModelUtil.checkBaseConfig()){
    // 下一步
} else {
    // 下载配置文件包
}

# 4.2.2 模型文件检查

函数定义: ai.guiji.duix.sdk.client.VirtualModelUtil

/**
 * 返回模型文件是否下载完成
 */
boolean checkModel(String name)

参数说明:

参数 类型 描述
name String 模型文件的下载连接或模型名称

调用示例

if (VirtualModelUtil.checkModel(mModelPath)){
    // 下一步
} else {
    // 下载模型
}

# 4.2.3 基础配置文件下载

函数定义:
ai.guiji.duix.sdk.client.VirtualModelUtil

/**
 * 启动基础配置文件下载
 */
void baseConfigDownload(Context context, ModelDownloadCallback callback)

void baseConfigDownload(Context context, String url, ModelDownloadCallback callback)

参数说明:

参数 类型 描述
context Context App上下文
url String baseConfig网络存储地址
callback ModelDownloadCallback 模型下载的回调接口

其中ModelDownloadCallback的接口定义:
ai.guiji.duix.sdk.client.callback.ModelDownloadCallback

/**
 * 下载完成的事件
 * @param url 下载链接
 * @param dir 模型文件夹的保存路径
 */
void onDownloadComplete(String url, File dir)

/**
 * 下载失败的事件
 * @param url 下载链接
 * @param code 异常码
 * @param msg 异常消息
 */
void onDownloadFail(String url, int code, String msg)

/**
 * 下载进度
 * @param url 下载链接
 * @param current 当前下载的字节进度
 * @param total 需要下载zip文件大小
 */
void onDownloadProgress(String url, long current, long total)

/**
 * zip文件解压进度
 * @param url 下载链接
 * @param current 当前解压的字节进度
 * @param total 需要解压的zip文件大小
 */
void onUnzipProgress(String url, long current, long total)

调用示例

VirtualModelUtil.baseConfigDownload(mContext, mBaseConfigUrl, modelDownloadCallback)

# 4.2.4 模型文件下载

函数定义:
ai.guiji.duix.sdk.client.VirtualModelUtil

/**
 * 启动基础配置文件下载
 */
void modelDownload(Context context, String modelUrl, ModelDownloadCallback callback)

参数说明:

参数 类型 描述
context Context App上下文
modelUrl String 模型文件的下载地址
callback ModelDownloadCallback 模型下载的回调接口

调用示例

VirtualModelUtil.modelDownload(mContext, mModelPath, modelDownloadCallback)

# 4.3 创建Player对象

函数定义: ai.guiji.duix.sdk.client.Player

/**
 * Player构造函数
 */
Player(Context context, RenderSink renderSink, Callback callback)

参数说明:

参数 类型 描述
context Context App上下文
renderSink RenderSink 渲染数据接口
callback Callback Player的各种回调事件

使用 SDK 提供的 DUIXRendererDUIXTextureView 可快速实现支持透明通道的渲染。也可以自己实现RenderSink接口自定义渲染逻辑。

其中RenderSink的定义如下: ai.guiji.duix.sdk.client.render.RenderSink

/**
 * 渲染管道,通过该接口返回渲染数据
 */
public interface RenderSink {

    // frame中的buffer数据以bgr顺序排列
    void onVideoFrame(ImageFrame imageFrame);

}

其中Callback的定义如下: ai.guiji.duix.sdk.client.Player$Callback

public interface Callback {
        // 数字人初始化完成并返回本地模型信息
        void onInitSuccess(ModelInfo modelInfo);
        // 数字人初始化失败
        void onInitError(int code, int subCode, String msg);
        // 数字人开始播放音频
        void onPlayStart();
        // 数字人音频播放完成
        void onPlayEnd();
        // 数字人播放音频异常
        void onPlayError(int code, String msg);
        // 数字人播放开始播放动作区间
        void onMotionStart(String name);
        // 数字人播放完成播放动作区间
        void onMotionComplete(String name);

        // --- 以下是DUIX后台AI能力相关回调 ---
        // 接收到DUIX后台开始推送音频的信号,可在此回调中调用startPush()函数让数字人准备播报
        default void onSpeakStart(){}
        // 接收到DUIX后台推送的PCM音频数据,可在此回调中调用pushPcm()函数让数字人播报音频
        default void onSpeakBuffer(ByteBuffer buffer, String content){}
        // 接收到DUIX后台推送音频结束的信号,可在此中调中调用stopPush()函数通知数字人音频推送完毕
        default void onSpeakEnd(String content){}
        // 接收到DUIX后台推送的音频数据对应的文本内容
        default void onSpeakText(String text){}
        // 接收到DUIX后台推送的请求播放动作事件
        default void onRequireMotion(String motion){}
        // 接收到DUIX后台推送的音频识别内容
        default void onAsrResult(String content, boolean end){}
        // 接收到DUIX后台推送的连接异常信息
        default void onWebsocketError(int code, String msg, boolean remote){}
        // 渲染每帧的统计信息
        default void onRenderReport(int resultCode, boolean isLip, long useTime){}
    }

调用示例:

val renderer =
    DUIXRenderer(binding.glTextureView)
binding.glTextureView.setEGLContextClientVersion(GL_CONTEXT_VERSION)
binding.glTextureView.setEGLConfigChooser(8, 8, 8, 8, 16, 0)
binding.glTextureView.isOpaque = false           // 透明
binding.glTextureView.setRenderer(renderer)
binding.glTextureView.renderMode =
    GLSurfaceView.RENDERMODE_WHEN_DIRTY      // 一定要在设置完Render之后再调用

player = Player(mContext, renderer, playerCallback)

# 4.4 启动数字人渲染

函数定义: ai.guiji.duix.sdk.client.Player

/**
 * 只使用本地渲染功能,不启动ASR,TTS,LLM等AI能力。
 * @param modelUrl 下载完成的模型URL
 */
void init(String modelUrl)

/**
 * 上传本地音频,后台AI能力驱动数字人播报的方式
 * @param conversationId 平台申请的会话ID
 * @param modelUrl 下载完成的模型URL
 */
void init(String conversationId, String modelUrl)

/**
 * 可选择不启动LLM能力,仅使用SDK的ASR
 * @param conversationId 平台申请的会话ID
 * @param modelUrl 下载完成的模型URL
 * @param enableLLM 是否启动大模型答疑,如果enableLLM=false,只启动ASR识别需自行对接大模型,再使用文本驱动。
 */
void init(String conversationId, String modelUrl, boolean enableLLM)

Callback中异步回调初始化结果

参数说明:

参数 类型 描述
conversationId String 平台申请的会话ID
modelUrl String 下载完成的模型URL
enableLLM boolean 是否开启大模型根据语音识别自动答复

调用示例:

player?.init(conversationId, modelPath)

# 4.5 上传麦克风采集的音频数据到DUIX后台

在使用conversationId开启后台AI能力时使用该接口上传PCM音频流

函数定义: ai.guiji.duix.sdk.client.Player

public void sendAudio(byte[] audio)

参数说明:

参数 类型 描述
audio byte[] 麦克风采集的音频数据

采集的PCM音频的格式: 16k采样率单通道16位深度

对接成功后会在Callback回调中返回识别结果,如开启LLM会收到大模型回调的数据。

调用示例:

player?.sendAudio(buffer)

# 4.6. 播报控制

# 4.6.1 使用流式推送PCM驱动数字人播报

PCM格式:16k采样率单通道16位深

函数定义: ai.guiji.duix.sdk.client.Player

// 通知服务开始推送音频,在DUIX后台交互模式中onSpeakStart回调时需要调用该函数
void startPush()

// 推送PCM数据,在DUIX后台交互模式中onSpeakBuffer回调时需要调用该函数
void pushPcm(byte[] buffer)

// 完成一段音频推送(音频推送完就调要该函数,而不是等播放完成再调用。),在DUIX后台交互模式中onSpeakEnd回调时需要调用该函数
void stopPush()

startPush、pushPcm、stopPush需要成对调用,pushPcm不宜过长。可以在一整段音频推送完后调用stopPush结束当前会话,下一段音频再使用startPush重新开启推送。

每段startPush到stopPush中间的音频数据最少要1秒(32000字节)否则无法触发口型驱动,可以自行使用空白帧填充。

调用示例:

val thread = Thread {
            player?.startPush()
            val inputStream = assets.open("pcm/2.pcm")
            val buffer = ByteArray(320)
            var length = 0
            while (inputStream.read(buffer).also { length = it } > 0){
                val data = buffer.copyOfRange(0, length)
                player?.pushPcm(data)
            }
    player?.stopPush()
            inputStream.close()
}
thread.start()

# 4.6.2 WAV 播放驱动

函数定义: ai.guiji.duix.sdk.client.Player

void playAudio(String wavPath) 

该函数兼容旧的wav驱动数字人接口,在内部实际是调用了PCM推流方式实现驱动。

参数说明:

参数 类型 描述
wavPath String 16k采样率单通道16位深的wav本地文件

调用示例:

player?.playAudio(wavPath)

# 4.6.3 终止当前播报

当数字人正在播报时调用该接口终止播报。

函数定义: ai.guiji.duix.sdk.client.Player

boolean stopPlayAudio();

调用示例

player?.stopPlayAudio()

# 4.7 动作控制

# 4.7.1 播放指定动作区间

模型中支持新的动作区间标注(SpecialAction.json)

函数定义: ai.guiji.duix.sdk.client.Player

/**
 * 播放指定动作区间
 * @param name 动作区间名称,在init成功回调时,可以在@{ModelInfo.getSilenceRegion()}中获取到可用的动作区间
 * @param now 是否立即播放 true: 立即播放; false: 等待当前静默区间或动作区间播放完毕后播放
 */
void startMotion(String name, boolean now)

调用示例

player?.startMotion("打招呼", true)

# 4.7.2 随机播放动作区间

随机播放场景及旧的标注协议(config.json)

函数定义: ai.guiji.duix.sdk.client.Player

/**
 * 随机播放一个动作区间
 * @param now 是否立即播放 true: 立即播放; false: 等待当前静默区间或动作区间播放完毕后播放
 */
void startRandomMotion(boolean now);

调用示例

player?.startRandomMotion(true)

# 4.8 其他驱动方式(仅针对启动DUIX后台能力时)

SDK可以选择不使用后台自带的LLM答疑,如有一些其他场景或客户端对接三方LLM可使用这些驱动方式。

# 4.8.1 文本驱动

函数定义:

void sayText(String text, boolean interrupt);

参数说明:

参数 类型 描述
text String 数字人播报的文本内容
interrupt boolean 是否终止之前的播报

调用示例

player?.sayText("你叫什么名字", true)

# 4.8.2 文本答疑驱动

函数定义:

void askText(String text, boolean interrupt);

参数说明:

参数 类型 描述
text String 提出的问题,后台大模型会根据问题生成回复内容播报
interrupt boolean 是否终止之前的播报

调用示例

player?.askText("你叫什么名字", true)

# 4.8.3 音频文件驱动

函数定义:

void sayAudio(String wavUrl, boolean interrupt);

参数说明:

参数 类型 描述
wavUrl String 可公网访问的音频文件URL
interrupt boolean 是否终止之前的播报

调用示例

player?.sayAudio("https://...../test.wav", true)

# 4.9 音量调节

函数定义:

void setVolume(float volume);
参数 类型 范围 描述
wavUrl float 0.0f ~ 1.0f 控制数字人播报的音量

调用示例

player?.setVolume(1.0F)

# 4.10 资源释放

函数定义:

void release();

调用示例

player?.release()

# 五、 Proguard配置

如果代码使用了混淆,请在proguard-rules.pro中配置:

-keep class ai.guiji.duix.DuixNcnn{*; }

# 六、注意事项

  1. 驱动渲染初始化前需要确保基础配置文件及模型下载到指定位置。
  2. 播放的PCM音频不宜过长,播放的PCM缓存在内存中,过长的音频流可能导致内存溢出。
  3. 替换预览模型可以在MainActivity.kt文件中修改modelUrl的值,使用SDK中自带的文件下载解压管理以获得完整的模型文件。
  4. 音频驱动的格式: 16k采样率单通道16位深度
  5. 设备性能不足时可能导致音频特征提取的速度跟不上音频播放的速度,可以使用player?.setReporter()函数添加一个监控观察帧渲染返回的信息。
  6. 每段startPush到stopPush中间的音频数据最少要1秒(32000字节)否则无法触发口型驱动,可以自行使用空白帧填充。

# 七、常见问题与排查指南

问题现象 可能原因 解决方案
init 回调失败 模型路径错误或未下载完成 使用 checkModel 检查模型状态
渲染黑屏 EGL 配置或纹理视图设置错误 使用 SDK 提供示例中的设置方法
PCM 无播报效果 格式不符或未调用 startPush 确保音频格式正确并调用推送方法
模型下载过慢 网络不稳定或 CDN 受限 支持自建模型文件托管服务