### FUStaSDK集成文档 本 SDK 集成了 FaceUnity 的语音驱动形象引擎(Speeach to Animation) ,能够快速构建并驱动虚拟形象说话,营造更加真实自然的人机交互场景。 ## 版本信息 * **版本号**:2.1 * **更新日期**:2022-01-21 * **更新内容**: 1. 优化CPU性能以及查询效率 2. 新增动画播放速度控制接口 3. 更新BS系数57-47 4. 修复静音帧问题 5. 新增设置旋转角度 6. 优化阴影锯齿算法 7. 增加详细日志接口 * **增量大小**: SDK 体积以及apk增量数据(单位:M): * v7a + v8a 22.8M app 增量:32.6M SDK 内部内置资源共计6.57M,其中包含: 1. `controller_cpp.bundle` 共1.46M,graphics相关。 2. `new_BSConfig` 共4.11M,bs表情系数数据包。 3. 其他资源 约1M 备注: 目前SDK只支持当前两个架构,如有特殊需求可打包对应架构的SDK。 ## 配置要求 * **SDK支持安卓版本:`API 21` 及以上,必须在硬件加速的窗口中使用** ## 快速集成 1. 首先要获取证书 `authpack.java`,放到 `faceunity` 模块 `com.faceunity.fustademo` 包下;语音校准工具包`data_ali.bin`放到`assets`目录下(ALIGN方式查询口型,可不设置),语音识别工具包`data_asr.bin`放到`assets`目录下(ASR方式查询口型,可不设置),以上工具包都不设置默认只支持音频时间戳查询口型系数。 2. 构建工程,运行到手机上,即可体验 demo 功能。 3. 想要体验`流式音频播报`功能,可申请`标贝`TTS的`client_id`和`client_secret`或者对接其他三方的TTS服务。 ## 使用说明 * **FU语音助手** **功能模块**: 1. 非流式音频播报 播报一整段音频文件,查询音频对应的口型系数,并播放音频。 2. 流式音频播报 文本输入通过语音合成(TTS)(由第三方服务商实现)生成音频传入SDK,查询音频对应的口型系数,并播放音频。 3. 歌曲演唱 播放歌曲驱动形象,歌词是带有音素的文本,查询音频对应的口型系数,并播放音频。 **Demo资源集成**: 以下是当前Demo的Avatar资源相关和STA相关数据包的集成的解析,便于开发者理解Demo和SDK调用。但不仅仅限于当前集成方式,开发者可根据项目需求自行定义集成方式。 1. assets 目录 该目录存放所有的形象资源以及STA相关数据包资源,不同资源都存放在对应的子文件夹中。 - fusta_demo/src/main/assets/sta:该目录为所有Avatar资源的存放目录 - fusta_demo/src/main/assets/sta/animation:该目录为所有Avatar的动画资源的存放目录 - fusta_demo/src/main/assets/sta/body:该目录为所有Avatar的body资源的存放目录 - fusta_demo/src/main/assets/sta/builtin:该目录结构下有一个 avatar_list.json 文件,主要用来配置 avatar 列表,其格式如下: ``` [ { "name": "3D·卡通女",// avatar 名称,可以不配置。 "dirName": "cartoon_female_moren"// avatar 文件夹名称,该文件夹位于 avatar 目录的根目录。 }, { "name": "3D·卡通男", "dirName": "cartoon_male_moren" } ] ``` 如 avatar_list.json 配置,每个元素对应一个avatar文件夹,在文件夹中保存着形象的 avatar.json 配置文件、素材文件以及图标等相关文件,avatar.json 的格式如下: ``` { "gender": 0,// 性别,0男、1女、2通用,必须配置 "components": [// 身体各个部件,需要配置部件名称加路径 { "name": "body", "path": "@assets/body/STA_avatar_kt_def_female_moren.bundle" } ], "animations": [// 形象动画,需要配置动画名称加路径 { "name": "默认呼吸", "path": "@assets/animation/STA_anim_kt_def_female_weixiao.bundle" } ], "position": {// 形象默认的坐标 "x": 10.0, "y": 50.0, "z": -600.0 }, "scene": {// 场景配置,包括背景(background)、相机(camera)、灯光(light),需要配置名称加路径 "background": { "name": "background", "path": "@assets/STA_default_bg.bundle" }, "light": { "name": "light", "path": "@assets/STA_default_light.bundle" } }, "custom": ""//Demo业务逻辑相关的配置,主要是Avatar名称、动作列表和标签列表的定义 } ``` 备注:路径指定的前缀@assets表示资源存在的assets下。 - fusta_demo/src/main/assets/sta_kit:该目录存放所有STA相关数据包资源,该文件的STA相关资源包数据: `data_ali.bin`语音自动校准工具包(Align模式需要设置) `data_asr.bin`设置文字编码功能数据文件(Align模式和文本时间戳需要设置) `data_decoder.bin`设置语音识别工具包(ASR方式需要设置) 在`fusta_demo/src/main/java/com/faceunity/fustademo/util/StaKitUtils.java`的`init`方法中按需加载。 - fusta_demo/src/main/assets/sta_kit/emotion:该目录存放自定义表情bs文件。 - fusta_demo/src/main/assets/sta_kit/song:该目录存放歌曲相关文件,用于歌曲播放能力展示。 2. 形象展示 `com.faceunity.fu_data.data.FUDataCenter`会根据配置的 avatar 目录路径,读取 avatar_list.json 以遍历所有形象文件夹中的 avatar.json ,生成 avatar 模型列表,上层业务调用者只需要传入下标便可轻松选择要展示的形象。具体调用示例如下: ``` FUDataCenter fuDataCenter = new FUDataCenter(mContext); fuDataCenter.initWithAvatarDirAndAssetsPath(null, FUConstant.STA_ASSENTS_PATH); ArrayList avatarModels = fuDataCenter.loadAvatarLists(); ``` `com.faceunity.fustademo.data.AvatarDataFactory`持有`avatarModels`列表数据,调用`setAvatar(int index)`拿到对应的`FUAvatarModel`进而实现Avatar加载或切换。 * **证书** demo中的`authpack.java`是我司发布的SDK证书,FUStaSDK需要通过`authpack`鉴权并初始化。无法独立于证书运行,因此需要将`authpack.java`替换到你的工程中。 * **未鉴权的bundle** (离线鉴权内容,在线鉴权模式请忽略)assets目录下的`fustaengine\offline\***.bundle`数据是未鉴权的bundle数据,离线鉴权过程首先需要使用未鉴权的bundle数据联网进行一次在线鉴权,鉴权成功后生成`key.bundle`, * **key.bundle** (离线鉴权内容,在线鉴权模式请忽略)`key.bundle`是SDK内部自动生成的bundle,有了该bundle后续开发者可使用该文件进行离线权限,不再需要联网操作。`key.bundle`是第一次联网鉴权成功后生成的,保存在SD卡目录下的\FaceUnity\FUStaKit\key.bundle * **FUStaKit** `FUStaKit`是SDK对外的门面类。通过`FUStaKit`对象调用SDK相关功能。 * **道具资源** 除了SDK和证书,我们还需要道具资源供SDK加载,Demo中的`assets`文件夹包含了一些预置的资源文件(背景、形象、动作、扩展功能数据包等)。由于部分道具资源文件较大,实际开发过程中可以选择通过网络下载的方式集成到你的项目中。你可以暂先将Demo中的`assets`文件夹拖入到你的工程中使用免费道具 ## 集成 SDK ### 1. 添加依赖 在工程目录下`build.gradle`->`repositories`下添加` maven { url 'http://maven.faceunity.com/repository/maven-public/' }`。 在项目目录下的`build.gradle`下添加依赖库`api 'com.faceunity:sta-full-featured:2.1.0-RELEASES'` ### 2. FUStaKit SDK 配置初始化数据 在FUStaKit SDK初始化之前,一般放在`Application`中配置。鉴权方式分为在线鉴权和离线鉴权,可根据业务需求自行选择: #### 配置在线鉴权初始化数据 **示例代码:** ``` public void initOnline(@NonNull Context context) { mContext = context.getApplicationContext(); FUStaKit.Builder builder = new FUStaKit //传入上下文,必要 .Builder(mContext) //验证证书,必要 .setAuth(authpack.A()) //设置tts查询方式,必要(ASR方式需要设置:FUTtsType.ASR;Align方式需要设置:FUTtsType.ALIGNMENT) .setFUTtsType(FUTtsType.ALIGNMENT) //设置语音识别工具包(ASR方式需要设置) // .setAsrData(bytesAsr) //设置语音自动校准工具包(Align模式需要设置) .setAlignData(bytesAlign) //设置文字编码功能数据文件(Align模式和文本时间戳需要设置) .setCharacterDecoder(bytesDecoder); mFUStaKit = builder.build(); } ``` 备注: #### 配置离线鉴权初始化数据 **示例代码:** ``` public void initOffline(@NonNull Context context) { mContext = context.getApplicationContext(); FUStaKit.Builder builder = new FUStaKit //传入上下文,必要 .Builder(mContext) //验证证书,必要 .setAuth(authpack.A()) //设置tts查询方式,必要(ASR方式需要设置:FUTtsType.ASR;Align方式需要设置:FUTtsType.ALIGNMENT) .setFUTtsType(FUTtsType.ALIGNMENT) //设置语音识别工具包(ASR方式需要设置) // .setAsrData(bytesAsr) //设置语音自动校准工具包(Align模式需要设置) .setAlignData(bytesAlign) //设置文字编码功能数据文件(Align模式和文本时间戳需要设置) .setCharacterDecoder(bytesDecoder) //设置离线鉴权数据 .setOffLineData(offLineAuth); mFUStaKit = builder.build(); } ``` 备注: 离线鉴权除了设置`authpack.A()` 鉴权数据,还需要设置离线鉴权数据包数据:`setOffLineAuth(offLineAuth)`,该逻辑可参考集成Demo中的示例代码:[StaKitUtils.java: Lines 82-150](../app/src/main/java/com/faceunity/fustademo/util/StaKitUtils.java#L82-L150)。 注意离线鉴权还需要有WRITE_EXTERNAL_STORAGE权限。 ### 3. FUStaKit SDK 鉴权 FUStaKit SDK初始化,必须放在 FUStaKit SDK配置初始化数据之后,鉴权方式分为在线鉴权和离线鉴权,可根据业务需求自行选择: #### 配置在线鉴权初始化数据 方法:`init(FUAuthType.ONLINE, StaKitInitCallback listener)` `init(FUAuthType.ONLINE, null)` 参数:`FUAuthType.ONLINE`:在线鉴权; `FUStaKit.StaKitInitCallback`,SDK初始化状态的回调。参数可为空。 **示例代码:** 1.有初始化完成回调的方式,SDK初始化完成的回调,进入功能页面 // 初始状态监听器,监听init是否成功,回调在非UI线程 private FUStaKit.StaKitInitCallback mInitListener = new FUStaKit.StaKitInitCallback() { @Override public void onInitComplete(int code, @NotNull String msg) { } @Override public void onError(int errCode, @Nullable String errMsg) { } }; // SDK 初始化 mFUStaKit.init(FUAuthType.ONLINE, mInitListener); 2.没有初始化完成回调的方式,如果调用该方法之后紧接着调用口型驱动方法,口型驱动任务会等待初始化任务完成才开始 // SDK 初始化 mFUStaKit.init(null); 备注: init 初始化方法,务必在后续操作前调用,否则无法驱动口型。 SDK初始化方法是耗时的,SDK内部已将任务添加到异步线程处理。如果SDK初始化任务尚未处理完毕,后续口型驱动任务回调加到任务队列等待该方法处理完毕再按照顺序操作,用户端表现为第一次口型驱动耗时时间长。 该步骤可参考集成Demo中的示例代码:[SplashActivity.java: Lines 72-117](../app/src/main/java/com/faceunity/fustademo/ui/SplashActivity.java#L72-L117) #### 配置离线鉴权初始化数据 方法:`init(FUAuthType.OFFLINE_BUNDLE, StaKitInitCallback listener)` `init(FUAuthType.OFFLINE_BUNDLE, null)` 参数:`FUAuthType.OFFLINE_BUNDLE`:离线鉴权; `FUStaKit.StaKitInitCallback`:SDK初始化状态的回调。参数可为空。 **示例代码:** 1.有初始化完成回调的方式,SDK初始化完成的回调,进入功能页面 // SDK 初始化 mFUStaKit.init(FUAuthType.OFFLINE_BUNDLE, mInitListener); 2.没有初始化完成回调的方式,如果调用该方法之后紧接着调用口型驱动方法,口型驱动任务会等待初始化任务完成才开始 // SDK 初始化 mFUStaKit.init(null); 备注: ### 4. FUStaKit SDK 设置默认FUAvatar 设置默认的FUAvatar,推荐在开始渲染前设置默认FUAvatar。 方法:`setAvatar(FUAvatar avatar, FUAvatarType avatarType, OnAvatarStateListener listener)` 方法传入参数主要是: `FUAvatar`:FUStaKit SDK Avatar人物数据模型; `FUAvatarType`:Avatar类型的枚举:`FUAvatarType.CARTOON`、`FUAvatarType.REAL`,默认是`FUAvatarType.CARTOON`,该参数主要用来区别SDK内置表情。缺省参数。 `OnAvatarStateListener`:Avatar执行状态接口,方法`onAvatarComplete()`表示Avatar加载完成。缺省参数。 **示例代码:** ``` FUAvatar fuAvatar = new FUAvatar.Builder() .setAvatar(avatar)// 切换形象 .setCoordinate(x, y, z)// 设置形象位置 .setBackground(backGround)// 设置背景 .setCamera(camera)// 设置相机位 .setLight(light)// 设置光照 .enableShadow(enable)// 设置阴影 .build(); // 切换Avatar mFUStaKit.setAvatar(fuAvatar); 或 mFUStaKit.setAvatar(fuAvatar, FUAvatarType.CARTOON); 或 mFUStaKit.setAvatar(fuAvatar, FUAvatarType.CARTOON, onAvatarStateListener); ``` 备注: 设置默认的Avatar和Avatar切换调用方式一致,调用时机可以在开始渲染前设置也可以在渲染后设置,Avatar切换其对应的特征也会有变化,例如切换Avatar也会切换对应的背景、光照、位置等等。 该步骤可参考集成Demo中的示例代码:[AvatarDataFactory.java: Lines 250-278](../app/src/main/java/com/faceunity/fustademo/data/AvatarDataFactory.java#L250-L278) ### 5. FUStaKit SDK 开始渲染 SDK配置渲染相关参数并开始渲染。 方法:`requestRender(GLTextureView glTextureView)` 参数: `GLTextureView` SDK提供的渲染的组件 **示例代码:** mFUStaKit.requestRender(glTextureView);// 请求渲染 mFUStaKit.setCustomRenderResolution(renderWidth, renderHeight);// 设置渲染分辨率,renderWidth:宽度,默认1280;renderHeight:高度,默认720 // Activity onDestroy 调用 Activity.onDestroy() { super.onDestroy(); mFUStaKit.onDestroy(); } 备注: 该步骤可参考集成Demo中的示例代码:[AvatarDataFactory.java: Lines 149-155](../app/src/main/java/com/faceunity/fustademo/data/AvatarDataFactory.java#L149-L155) ### 6. 形象口型查询和驱动 根据传入的音频数据或者是时间戳数据查询口型并驱动口型,开发者可根据业务场景和需求选择合适的驱动方式: 根据音频数据或者时间戳数据的返回方式可分为`流式音频驱动`和`非流式音频驱动`; 根据能够提供的数据资源可分为`时间戳驱动`、`Align驱动`、`ASR驱动`; 根据是否由SDK播放器播报音频可分为`SDK内置播放器播报`、`自定义播放器播报`。 文档以`SDK内置播放器播报`、`自定义播放器播报`两种模式分类讲解。 #### SDK 内置播放器播报 内置播放器播报,需要在口型查询之前初始化内部播放器。 方法:`initStaPlayer(new FUPlayerConfig.Builder().setAudioSampleRate(sampleRate).build())` 参数: `FUPlayerConfig`:内部播放器的参数配置类,主要配置采样率、声道数和位深。 **示例代码:** mFUStaKit.initStaPlayer(new FUPlayerConfig.Builder().setAudioSampleRate(16000).build());// 设置采样率为16k 备注: 内置播放器播报,必须要先初始化播放器,接下来才能开始口型的查询和驱动。 * **流式音频,无时间戳** 针对流式返回的音频数据且无时间戳的场景`(有时间戳肯定要采用时间戳方式,因为时间戳查询效率高,口型更精准)`,支持音频类型:`pcm`,根据音频数据信息查询口型系数,播放音频并驱动对应的口型。 根据能不能提供音频数据对应的文本`(后文统一称之为AlignText)`分为:`Align`方式 与 `ASR` 方式。 1、`ALIGN` 方式 `Align`方式查询口型系数是靠音频数据和与之对应的AlignText,在流式的查询中,音频数据是一段段返回,但是AlignText是完整的一段文本,`Align`方式查询速度较`ASR`方式更快,也可以支持高级的动作表情的定制化,但是也有限制,例如AlignText不支持繁体中文。 **示例代码:** // 流式处理开始 mFUStaKit.notifyStaProcessStart(); 1、`ALIGN` 方式 `Align`方式查询口型系数是靠音频数据和与之对应的AlignText,在流式的查询中,音频数据是一段段返回,但是AlignText是完整的一段文本,`Align`方式查询速度较`ASR`方式更快,也可以支持高级的动作表情的定制化,但是也有限制,例如AlignText不支持繁体中文。 **示例代码:** // 流式处理开始 mFUStaKit.notifyStaProcessStart(); ... // 口型查询、音频播放并驱动对应口型,这段是多次调用的,即每次有流式音频返回都会重新设置`FUStaParams`并调用`staProcess(params)` FUStaParams params = new FUStaParams.Builder() .setStreamMode(1)// 1 流式模式, 0 非流式模式 默认非流式 .setAudioData(data)// 音频数据,流式查询要求只能是PCM .setAudioType(FUAudioType.PCM)// 音频类型 流式查询只支持pcm 默认pcm .setAlignText(align)// AlignText 注意此处设置的是整段的文本而不是与该段音频对应的文本 .setTimestampType(FUTimestampType.PHONE)// 时间戳类型,`ALIGN` 方式设置为`FUTimestampType.PHONE` .build(); mFUStaKit.staProcess(params); ... // 流式处理结束 mFUStaKit.notifyStaProcessFinish(); 备注: 音频时间戳有两种类型:音素时间戳和文字时间戳,分别对应 `FUTimestampType.PHONE` 和 `FUTimestampType.CHARACTER`,有关音素时间戳和文本时间戳的区别可参考`12. FUStaParams 参数配置类`里面对时间戳的示例。 流式无时间戳采用`ALIGN`方式查询口型系数,sdk内部会根据音频数据和生成整段音频的文本计算出音素时间戳,故`setTimestampType(FUTimestampType.PHONE)`必须设置。 关于流式`ALIGN`方式,比流式`ASR`方式需要多设置生成音频数据的文本,注意此处设置的是整段的文本而不是与该段音频对应的文本。 该步骤可参考集成Demo中的示例代码com.faceunity.fustademo.ui.TtsActivity.DataBakerTtsCallback。 2、`ASR`方式 `ASR`方式查询口型系数是靠音频数据,`ASR`方式查询速度较慢,单位音频处理速度稍耗时,但流式查询场景下,SDK底层库查询处理n秒的音频速度远远大于n秒音频播放时长,因此可忽略性能差异。 **示例代码:** // 流式处理开始 mFUStaKit.notifyStaProcessStart(); ... // 口型查询、音频播放并驱动对应口型,这段是多次调用的,即每次有流式音频返回都会重新设置`FUStaParams`并调用`staProcess(params)` FUStaParams params = new FUStaParams.Builder() .setStreamMode(1)// 1 流式模式, 0 非流式模式 默认非流式 .setAudioData(data)// 音频数据,流式查询要求只能是PCM .setAudioType(FUAudioType.PCM)// 音频类型 流式查询只支持pcm 默认pcm .setTimestampType(FUTimestampType.PHONE)// 时间戳类型,`ASR` 方式设置为`FUTimestampType.PHONE` .build(); mFUStaKit.staProcess(params); ... // 流式处理结束 mFUStaKit.notifyStaProcessFinish(); 备注: 音频时间戳有两种类型:音素时间戳和文字时间戳,分别对应 `FUTimestampType.PHONE` 和 `FUTimestampType.CHARACTER`,有关音素时间戳和文本时间戳的区别可参考`12. FUStaParams 参数配置类`里面对时间戳的示例。 流式无时间戳采用`ASR`方式查询口型系数,sdk内部会根据音频数据计算出音素时间戳,故`setTimestampType(FUTimestampType.PHONE)`必须设置。 该步骤可参考集成Demo中的示例代码com.faceunity.fustademo.ui.TtsActivity.DataBakerTtsCallback。 * **流式音频,有时间戳** 针对流式返回的音频数据且有时间戳的场景,查询耗时最少,有时间戳数据优先采用时间戳方式。支持音频类型:`pcm`,根据音频数据信息查询口型系数,播放音频并驱动对应的口型。 **示例代码:** // 流式处理开始 mFUStaKit.notifyStaProcessStart(); ... // 口型查询、音频播放并驱动对应口型,这段是多次调用的,即每次有流式音频返回都会重新设置`FUStaParams`并调用`staProcess(params)` FUStaParams params = new FUStaParams.Builder() .setStreamMode(1)// 1 流式模式, 0 非流式模式 默认非流式 .setAudioData(data)// 音频数据,流式查询要求只能是PCM .setAudioType(FUAudioType.PCM)// 音频类型 流式查询只支持pcm 默认pcm .setTimestamp(timestamp)// 时间戳数据 .setTimestampType(FUTimestampType.PHONE)// 时间戳类型,具体类型要根据时间戳的类型来定 .build(); mFUStaKit.staProcess(params); ... // 流式处理结束 mFUStaKit.notifyStaProcessFinish(); 备注: 音频时间戳有两种类型:音素时间戳和文字时间戳,分别对应 `FUTimestampType.PHONE` 和 `FUTimestampType.CHARACTER`,有关音素时间戳和文本时间戳的区别可参考`12. FUStaParams 参数配置类`里面对时间戳的示例。 该步骤可参考集成Demo中的示例代码com.faceunity.fustademo.ui.TtsActivity.DataBakerTtsCallback。 * **非流式音频,无时间戳** 针对非流式返回的音频数据且无时间戳的场景`(有时间戳肯定要采用时间戳方式,因为时间戳查询效率高,口型更精准)`,支持音频类型:`pcm\wav`,根据音频数据信息查询口型系数,播放音频并驱动对应的口型。 根据能不能提供音频数据对应的文本`(后文统一称之为AlignText)`分为:`Align`方式 与 `ASR` 方式。 1、`ALIGN` 方式 `Align`方式查询口型系数是靠音频数据和与之对应的AlignText,在非流式的查询中,音频数据是一整段返回,AlignText是音频对应的文本,`Align`方式查询速度较`ASR`方式更快,也可以支持高级的动作表情的定制化,但是也有限制,例如AlignText不支持繁体中文。 **示例代码:** // 口型查询、音频播放并驱动对应口型,这段是单次调用的 FUStaParams params = new FUStaParams.Builder() .setAudioData(data)// 音频数据,非流式查询支持PCM和WAV .setAudioType(FUAudioType.PCM)// 音频类型 非流式查询支持pcm、wav 默认pcm .setAlignText(align)// AlignText 注意此处设置的是整段的文本而不是与该段音频对应的文本 .setTimestampType(FUTimestampType.PHONE)// 时间戳类型,`ALIGN` 方式设置为`FUTimestampType.PHONE` .build(); mFUStaKit.staProcess(params); 备注: 音频时间戳有两种类型:音素时间戳和文字时间戳,分别对应 `FUTimestampType.PHONE` 和 `FUTimestampType.CHARACTER`,有关音素时间戳和文本时间戳的区别可参考`12. FUStaParams 参数配置类`里面对时间戳的示例。 `setTimestampType(FUTimestampType.PHONE)`,非流式无时间戳采用`ALIGN`方式查询口型系数,sdk内部会根据音频数据和生成整段音频的文本计算出音素时间戳,故`setTimestampType(FUTimestampType.PHONE)`必须设置。 该步骤可参考集成Demo中的示例代码com.faceunity.fustademo.ui.SimpleActivity#sendText。 2、`ASR` 方式 `ASR`方式查询口型系数是靠音频数据,`ASR`方式查询速度较慢,单位音频处理速度稍耗时, 单位音频处理速度不如`ALIGN`方式,但内部实现了音频分割提高效率。 **示例代码:** // 口型查询、音频播放并驱动对应口型,这段是单次调用的 FUStaParams params = new FUStaParams.Builder() .setAudioData(data)// 音频数据,非流式查询支持PCM和WAV .setAudioType(FUAudioType.PCM)// 音频类型 非流式查询支持pcm、wav 默认pcm .setTimestampType(FUTimestampType.PHONE)// 时间戳类型,`ASR` 方式设置为`FUTimestampType.PHONE` .build(); mFUStaKit.staProcess(params); 备注: 音频时间戳有两种类型:音素时间戳和文字时间戳,分别对应 `FUTimestampType.PHONE` 和 `FUTimestampType.CHARACTER`,有关音素时间戳和文本时间戳的区别可参考`12. FUStaParams 参数配置类`里面对时间戳的示例。 非流式无时间戳采用`ASR`方式查询口型系数,sdk内部会根据音频数据计算出音素时间戳,故`setTimestampType(FUTimestampType.PHONE)`必须设置。 该步骤可参考集成Demo中的示例代码com.faceunity.fustademo.ui.SimpleActivity#sendText。 * **非流式音频,有时间戳** 针对非流式返回的音频数据且有时间戳的场景,查询耗时最少,有时间戳数据优先采用时间戳方式。支持音频类型:`pcm\wav`,根据音频数据信息查询口型系数,播放音频并驱动对应的口型。 **示例代码:** // 口型查询、音频播放并驱动对应口型 FUStaParams params = new FUStaParams.Builder() .setAudioData(data)// 音频数据,非流式查询支持PCM和WAV .setAudioType(FUAudioType.PCM)// 音频类型 非流式查询支持pcm、wav 默认pcm .setTimestamp(timestamp)// 时间戳数据 .setTimestampType(FUTimestampType.PHONE)// 时间戳类型,`ASR` 方式设置为`FUTimestampType.PHONE` .build(); mFUStaKit.staProcess(params); 备注: 音频时间戳有两种类型:音素时间戳和文字时间戳,分别对应 `FUTimestampType.PHONE` 和 `FUTimestampType.CHARACTER`,有关音素时间戳和文本时间戳的区别可参考`12. FUStaParams 参数配置类`里面对时间戳的示例。 该步骤可参考集成Demo中的示例代码com.faceunity.fustademo.ui.SimpleActivity#sendText。 * **设置内部播放器播放状态接口** 以上方式均采用内部播放器播放音频,SDK内部播放状态的接口为`OnStaPlayerListener`; 方法:`setStaPlayerListener(OnStaPlayerListener listener)`,方法传入参数主要是`OnStaPlayerListener`。 **示例代码:** mFUStaKit.setStaPlayerListener(new OnStaPlayerListener() { @Override public void onPrepared() { // 播放开始 } @Override public void onCompleted() { // 播放结束 } @Override public void onCancel() { // 播放取消 } @Override public void onError(@Nullable String message) { // 播放出错 } }); 备注: 该步骤可参考集成Demo中的示例代码[AvatarDataFactory.java: Lines 98-105](../app/src/main/java/com/faceunity/fustademo/data/AvatarDataFactory.java#L98-L105) #### 自定义播放器播报 用户自定义播放器播报,先查询口型系数,查询完成或者首段查询完成即可以开始自定义的播放器播放,然后通过`OnExternalPlayerListener`接口的`updateCurrentPosition()`返回播放进度给SDK, SDK拿到播放进度驱动对应的口型,具体步骤为: #### 1、自定义播放器查询 `自定义播放器播报`查询方法与`内置播放器播报`不同,不过`FUStaParams`参数设置方式都一致。这里简单列举`流式时间戳查询`和`非流式时间戳查询`两种方式。 ###### 流式时间戳查询 针对流式返回的音频数据且有时间戳的场景,查询耗时最少,有时间戳数据优先采用时间戳方式。支持音频类型:`pcm`,根据音频数据信息查询口型系数,播放音频并驱动对应的口型。 **示例代码:** // 流式处理开始 mFUStaKit.notifyStaProcessStart(); ... // 口型查询、音频播放并驱动对应口型,这段是多次调用的,即每次有流式音频返回都会重新设置`FUStaParams`并调用`staProcess(params)` FUStaParams params = new FUStaParams.Builder() .setStreamMode(1)// 1 流式模式, 0 非流式模式 默认非流式 .setAudioType(FUAudioType.PCM)// 音频类型 流式查询只支持pcm 默认pcm .setTimestamp(timestamp)// 时间戳数据 .setTimestampType(FUTimestampType.PHONE)// 时间戳类型,具体类型要根据时间戳的类型来定 .build(); mFUStaKit.staProcessNoPlayer(params); ... // 流式处理结束 mFUStaKit.notifyStaProcessFinish(); 备注: 音频时间戳有两种类型:音素时间戳和文字时间戳,分别对应 `FUTimestampType.PHONE` 和 `FUTimestampType.CHARACTER`,有关音素时间戳和文本时间戳的区别可参考`12. FUStaParams 参数配置类`里面对时间戳的示例。 ###### 非流式时间戳查询 针对非流式返回的音频数据且有时间戳的场景,查询耗时最少,有时间戳数据优先采用时间戳方式。支持音频类型:`pcm\wav`,根据音频数据信息查询口型系数,播放音频并驱动对应的口型。 **示例代码:** // 口型查询、音频播放并驱动对应口型,这段是多次调用的,即每次有流式音频返回都会重新设置`FUStaParams`并调用`staProcess(params)` FUStaParams params = new FUStaParams.Builder() .setAudioType(FUAudioType.PCM)// 音频类型 流式查询只支持pcm 默认pcm .setTimestamp(timestamp)// 时间戳数据 .setTimestampType(FUTimestampType.PHONE)// 时间戳类型,具体类型要根据时间戳的类型来定 .build(); mFUStaKit.staProcessNoPlayer(params); 备注: 音频时间戳有两种类型:音素时间戳和文字时间戳,分别对应 `FUTimestampType.PHONE` 和 `FUTimestampType.CHARACTER`,有关音素时间戳和文本时间戳的区别可参考`12. FUStaParams 参数配置类`里面对时间戳的示例。 该步骤可参考集成Demo中的示例代码:com.faceunity.fustademo.ui.SongActivity.SongSelectedListener#play。 #### 2、设置SDK口型查询状态接口 在`1、自定义播放器查询`中查询口型系数数据,无论流式查询还是非流式查询,我们都能够通过`OnStaProcessListener`接口获得查询一次口型系数数据的回调, 有个这个回调,我们可以认为现在SDK内部已经有口型系数的缓存了,这时候就可以开始自定义播放器的播放。 **示例代码:** mFUStaKit.setStaProcessListener(new OnStaProcessListener() { @Override public void onStaProcess(FUAudioProgressType audioProgressType, byte[] date) { // 伪代码 CustomPlayer.startPlay(); } }); 备注: 对于流式查询,我们可以在该次查询首次`onStaProcess()`方法回调时调用外部播放器的播放方法;对于非流式查询,我们可以在`onStaProcess()`方法回调时调用外部播放器的播放方法。 `onStaProcess()`方法回调在非UI线程。 #### 3、外部播放器播放状态同步 在`2、设置SDK口型查询状态接口`我们通过`OnStaProcessListener`接口知道了自定义播放器的播放时机,接下来进行外部播放器和SDK的状态同步才能驱动口型。 * **自定义播放器播报状态同步** 外部播放器播放的状态`is playing`需要向SDK同步,SDK只有接收到`playing`信号才认为外部播放器开始播放,进而根据播放进度驱动口型。 CustomPlayer.startPlay() // 外部播放器播放(伪代码) mFUStaKit.onExternalPlayerStart(); // 播放器播放,通知SDK播放器状态:playing CustomPlayer.pause(); // 外部播放器暂停(伪代码) mFUStaKit.onExternalPlayerStop(); // 播放器暂停,通知SDK播放器状态:no playing CustomPlayer.resume(); // 外部播放器恢复播放(伪代码) mFUStaKit.onExternalPlayerStart(); // 播放器复播放,通知SDK播放器状态:playing CustomPlayer.stop(); // 外部播放器停止播放(伪代码) mFUStaKit.onExternalPlayerStop(); // 播放器停止播放,通知SDK播放器状态:no playing 备注: * **自定义播放器播报进度同步** 外部播放器播放的进度通过`OnExternalPlayerListener`的`updateCurrentPosition()`同步播放进度给SDK,SDK接收到`playing`信号根据播放进度驱动口型。 **示例代码:** mFUStaKit.setExternalPlayerListener(new OnExternalPlayerListener() { @Override public long updateCurrentPosition() { return position; } }); 备注:该步骤可参考集成Demo中的示例代码:[AvatarDataFactory.java: Lines 195-202](../app/src/main/java/com/faceunity/fustademo/data/AvatarDataFactory.java#L195-L202)。 ### 7. Align方式TAG标签功能 高级用法,通过配置`标签映射表`和在AlignText中设置固定标记位,播放过程中用来精确控制形象动作行为的能力,支持流式和非流式。 主要步骤如下: #### 1、配置标签映射表 `标签映射表`,以JSON表示,JSON中字段不可随意更改 方法:`setAlignTagConfig(String tagConfig, String defaultAnimation)`, 方法传入参数主要是`tagConfig`:标签映射表;`defaultAnimation`:标签动作播放完毕会切换到的动作路径。 **示例代码:** JSON: { "tag": "#%欢迎#%",// 对应标签,对应值为固定格式的字段,我们规定的标签格式:`#%字符#%`; "emotion": "welcome emotion path",// 对应表情bs路径,动态切换表情 "animation": "sta/animation/STA_anim_real_def_chuchu_v3_huanying.bundle"// 对应动作路径,对应值为动作的绝对路径。 }, { "tag": "#%单手强调#%", "animation": "sta/animation/STA_anim_real_def_chuchu_v3_danshouqiangdiao.bundle" }, { "tag": "#%指引走#%", "animation": "sta/animation/STA_anim_real_def_chuchu_v3_zhiyinzou.bundle" }, { "tag": "#%比心#%", "emotion": "default emotion path", "animation": "sta/animation/STA_anim_real_def_chuchu_v3_bixin.bundle" } // 更新标签配置表 mFUStaKit.setAlignTagConfig(JSON, defaultAnimation); 备注: `setAlignTagConfig`必须要传入`defaultAnimation`默认动画,执行完标签动作后会循环执行`defaultAnimation`动画。 #### 2、设置AlignText 通过该文本插入`标签映射表`中配置的标签,后续音频驱动时音频播放到插入标签关键字时就会切换该标签匹配的动作和表情。 **示例代码:** 音频播放内容:"大家好~我是小玉。下面由我来介绍一下公司:我们专注于智能图形的创新与应用,为移动互联网提供3D内容生成与互动的行业解决方案。致力于将好莱坞电影级的特效技术在消费级应用中的普及。"; AlignText:"大家好~我是小玉。下面由我来#%欢迎#%介绍一下公司:#%单手强调#%我们专注于智能图形的创新与应用,#%指引走#%为移动互联网提供3D内容生成与互动的行业解决方案。#%比心#%致力于将好莱坞电影级的特效技术在消费级应用中的普及。"; FUStaParams params = new FUStaParams.Builder() .setStreamMode(1)// 1 流式模式, 0 非流式模式 默认非流式 .setAudioData(data)// 音频数据,流式查询要求只能是PCM .setAudioType(FUAudioType.PCM)// 音频类型 流式查询只支持pcm 默认pcm .setAlignText(align)// AlignText 注意此处设置的是整段的文本而不是与该段音频对应的文本 .setTimestampType(FUTimestampType.PHONE)// 时间戳类型,`ALIGN` 方式设置为`FUTimestampType.PHONE` .build(); mFUStaKit.staProcess(params); 备注: 上述示例中,播放内容时,会在“来”关键字切换#%欢迎#%对应的“STA_anim_real_def_chuchu_v3_huanying.bundle”动作和对应表情。 “相”关键字切换#%单手强调#%对应的“STA_anim_real_def_chuchu_v3_danshouqiangdiao.bundle”动作和对应表情。后续现象均为读到所插入标签关键字时切换对应动作和对应表情。 最后播放“致力于”关键字之前切换#%比心#%对应的动作和表情,此时音频播放完成动作还没播放完成的话也会继续播放动作,等动作播放完毕会切换到默认动作。 ### 8. Avatar动画切换功能 Avatar切换并播放其支持的动画,支持单次播放和循环播放,支持多道具和多道具动作和Avatar动画同时执行。 * **循环播放动画** 方法:`playAnimation(String path, String[] prop, String[] propAnimation)` 方法传入参数: `path`:Avatar要切换的目标动画的路径。 `prop`:Avatar要加载的道具的路径数组,缺省参数。 `propAnimation`:Avatar要加载的道具动作的路径数组,缺省参数,缺省参数。 **示例代码:** // 切换动作 mFUStaKit.playAnimation(animPath, prop, propAnimation); 或 mFUStaKit.playAnimation(animPath); 备注: 该步骤可参考集成Demo中的示例代码:[AvatarDataFactory.java: Lines 290-305](../app/src/main/java/com/faceunity/fustademo/data/AvatarDataFactory.java#L290-L305)。 * **播放单次动画** 方法:`playAnimationOnce(String path, String[] prop, String[] propAnimation)` 方法传入参数: `path`:Avatar要切换的目标动画的路径。 `prop`:Avatar要加载的道具的路径数组,缺省参数。 `propAnimation`:Avatar要加载的道具动作的路径数组,缺省参数,缺省参数。 **示例代码:** // 切换动作 mFUStaKit.playAnimationOnce(animPath, prop, propAnimation); 或 mFUStaKit.playAnimationOnce(animPath); 备注: 该步骤可参考集成Demo中的示例代码:[AvatarDataFactory.java: Lines 290-305](../app/src/main/java/com/faceunity/fustademo/data/AvatarDataFactory.java#L290-L305)。 ### 9. Avatar表情切换功能 Avatar切换表情,支持SDK内置表情和自定义表情。 * **切换SDK内置表情** 方法:`updateEmotion(FUEmotionType emotionType, int fpsNum)` 方法传入参数: `FUEmotionType`:SDK支持的内置表情的枚举。 `fpsNum`:过渡帧,当前表情切换到目标表情的帧数,默认为0,缺省参数。 **示例代码:** // 切换动作 mFUStaKit.updateEmotion(emotionType, 5); 或 mFUStaKit.updateEmotion(emotionType); 备注: `FUEmotionType`详细介绍请参考`13. FUEmotionType SDK支持的内置表情的枚举`。 该步骤可参考集成Demo中的示例代码:[AvatarDataFactory.java: Lines 392-400](../app/src/main/java/com/faceunity/fustademo/data/AvatarDataFactory.java#L392-L400)。 * **切换SDK自定义表情** 方法:`updateCustomEmotion(String path, int fpsNum)` 方法传入参数: `path`:自定义表情bs文件路径。 `fpsNum`:过渡帧,当前表情切换到目标表情的帧数,默认为0,缺省参数。 **示例代码:** // 切换动作 mFUStaKit.updateCustomEmotion(animPath, 5); 或 mFUStaKit.updateCustomEmotion(animPath); 备注: 该步骤可参考集成Demo中的示例代码:[AvatarDataFactory.java: Lines 392-400](../app/src/main/java/com/faceunity/fustademo/data/AvatarDataFactory.java#L392-L400)。 ### 10. 常用方法 * **设置3D抗锯齿配置** 设置抗锯齿,减少渲染的锯齿感。 **示例代码:** mFUStaKit.setMultiSamples(int samples);// samples默认为4 * **设置渲染帧率** 设置渲染帧率。 **示例代码:** mFUStaKit.setRenderFPS(int renderFPS);// renderFPS默认30 * **SDK onResume()** 与sdk onPause()成对出现,对应Activity onResume()减少不必要的渲染从而提高性能;非必需调用,可不设置该方法。 **示例代码:** mFUStaKit.onResume(); * **SDK onPause()** 与sdk onResume()成对出现,对应Activity onPause()减少不必要的渲染从而提高性能;非必需调用,可不设置该方法。 **示例代码:** mFUStaKit.onPause(); * **SDK onDestroy()** Activity onDestroy()调用。 **示例代码:** mFUStaKit.onDestroy(); * **Avatar更新背景** 支持背景道具更新。参数: 背景道具的绝对路径。 **示例代码:** mFUStaKit.setBackground(String background);// 背景切换,参数为背景道具路径 * **Avatar光照更新** 支持光照道具更新。参数: 灯光道具的绝对路径。 **示例代码:** mFUStaKit.setLight(String light);// 更新光照,参数为灯光道具路径 * **Avatar相机位更新** 支持相机位道具更新。参数: 相机位道具的绝对路径。 **示例代码:** mFUStaKit.setCamera(String camera);// 更新相机位,参数为相机道具路径 * **Avatar开启阴影** 支持开启阴影。 **示例代码:** mFUStaKit.enableShadow(String enableShadow);// 是否开启阴影 * **Avatar开启阴影抗锯齿** 开启阴影抗锯齿。 **示例代码:** mFUStaKit.setShadowPCFLevel(int level);// 阴影锯齿算法级别 * **设置Avatar位置** 设置角色在三维空间的位置,参数: x:double X轴坐标 一般调整范围 -200.0~200.0 y:double Y轴坐标 一般调整范围 -600.0~800.0 z:double Z轴坐标 一般调整范围 -3000.0~600.0 **示例代码:** mFUStaKit.setPosition(x,y, z); * **旋转形象** 旋转角色,参数:double 表示旋转增量,一般调整范围 -1.0~1.0,效果为旋转 **示例代码:** mFUStaKit.setRotDelta(val) // 旋转角色 * **缩放形象** 缩放角色,参数:double 表示缩放增量,一般调整范围 -1.0~1.0,效果为缩放 **示例代码:** mFUStaKit.setScaleDelta(val) // 缩放角色 * **上下移动形象** 上下移动角色,参数:double 表示上下增量,一般调整范围 -1.0~1.0,效果为上下移动 **示例代码:** mFUStaKit.setTranslateDelta(val) // 上下移动角色 * **设置旋转角度** 设置旋转角度,参数:float 表示旋转角度,效果为旋转 **示例代码:** mFUStaKit.setRotate(val) // 设置旋转角度 * **Avatar动画恢复播放** 恢复播放当前动画 **示例代码:** // 继续播放当前动画 mFUStaKit.startCurrentAnimation(); * **Avatar动画暂停播放** 暂停播放当前动画 **示例代码:** // 暂停播放当前动画 mFUStaKit.pauseCurrentAnimation(); * **设置Avatar动画的过渡时间** 动画切换有一个过渡,此方法设置动作切换时,原动作和切换动作的过渡时间。 参数:过渡时间,单位为秒,默认为0.5 **示例代码:** // 设置动作的过渡时间 mFUStaKit.setAnimationTransitionTime(time); * **设置Avatar动画的播放速度** 动画切换有一个过渡,此方法设置动作播放的速度。 参数:播放速度挡位,范围0.2-5.0,默认为1.0 **示例代码:** // 设置动作的播放速度 mFUStaKit.setAnimationSpeed(speed); * **设置日志是否输出到文件** 设置SDK输出到指定本地目录。 **示例代码:** // 设置日志输出的目录路径 mFUStaKit.setLogOutputDir(logcatDir) // 设置日志是否输出到文件 mFUStaKit.enableLogOutput(enable); ### 11. 销毁 FUStaKit 实例 在需要销毁FUStaKit时,销毁SDK,释放内存资源。 **示例代码:** ``` mFUStaKit().release(); ``` ### 12. FUStaParams 参数配置类 `streamMode` : 1 流式模式, 0 非流式模式 默认:0 `splitSegmentSeconds` : 流式分割每段间隔,单位秒 默认:2 `audioData` : 音频数据 类型:字节数组 `audioType` : 音频类型 `FUAudioType.WAV`,`FUAudioType.PCM`,默认:`FUAudioType.PCM` `audioSampleRate` : 采样率 默认:16K,必须与内部播放器设置的采样率一致 `audioEncoding` : 采样精度 默认:16 `audioChannel` : 声道数 默认: 1 `alignText` : 音频对应的文本 `timestamp` : 时间戳 类型:String 用于存在时间戳的情况 ``` 时间戳格式示例: 0.000000 0.070000 sil 0.070000 0.105000 n 0.105000 0.165000 i 0.165000 0.275000 h 0.275000 0.452000 ao 0.452000 0.802000 sil ``` 每个音素以换行符\n分行,每行内容分别为:开始时间,结束时间,音素 `timestampType` : 时间戳类型 音频数据类型对应着音节时间戳,有两种类型:音素、文字时间戳格式,分别对应 `FUTimestampType.PHONE` 、 `FUTimestampType.CHARACTER` 默认:FUTimestampType.CHARACTER ``` 音素类型,比如: 0.0 0.075 SIL 0.075 0.185 c 0.185 0.315 ong 0.315 0.41 m 0.41 0.509 ing 0.509 0.594 t 0.594 0.779 ian 0.779 0.98399997 q 0.98399997 1.189 i 1.189 1.511 SIL 文本类型,比如: 0.000000 1.871000 SIL 1.872000 2.056000 我 2.057000 2.248000 的 2.249000 2.444000 眼 2.445000 2.993000 睛 2.994000 3.313000 望 3.314000 3.680000 向 3.681000 4.130000 窗 4.131000 4.767000 外 4.767000 4.967000 SIL ``` ### 13. FUEmotionType SDK支持的内置表情的枚举 ``` NONE : 无表情,眨眼,表情跟随动画表情 DYNAMIC_NORMAL : 正常表情 DYNAMIC_JOY : 动态开心 DYNAMIC_ANGER : 动态生气 DYNAMIC_DISGUST : 动态厌恶 DYNAMIC_SURPRISE : 动态惊讶 DYNAMIC_FEAR : 动态恐惧 DYNAMIC_TRUST : 动态信任 DYNAMIC_SADNESS : 动态悲伤 DYNAMIC_DOUBT : 动态疑问 ``` ### 14. 常见问题 * **如何让获取日志** 解答: 1. 在初始化前设置`FUStaKit.setStaKitDebug(FUStaLogger.LogLevel.TRACE)`。 2. 口型问题可以用`FUSTA_LOG`过滤,渲染问题可以用`KIT_SDKController`过滤。 * **形象加载失败\加载不出来** 解答: 1. 检查证书是否过期。 2. 检查资源文件(bundle)路径(绝对路径)是否设置正确。 3. 检查并调整形象位置大小。 4. 检查FUStaSDK版本与要加载的资源文件版本是否对应。 * **形象无法驱动口型** 解答: 1. 检查证书是否过期。 2. 检查SDK初始化设置。 3. 检查形象驱动口型方法参数配置。 4. 外部播放器是否设置了开始播放方法或者有没有设置播放进度。 5. 如果使用了asr或者align方式,检查是否加载了扩展数据包。 6. 检查目标架构和SDK所支持架构是否一致。 7. 检查是否有第三方库与SDK冲突且开发做了以下操作:打包时移除项目中的库文件或设置匹配到第一个库文件。 * **口型不准确** 解答: 1. 检查形象驱动口型方法参数配置。 2. 外部播放器同步到SDK的播放进度不准确。 * **背景透明** 有些需求场景希望可以实现背景透明的需求,可参考以下方式。 解答: 1. GLTextureView.setOpaque(false); 2. mFUStaKit.setBackground(null); * **横竖屏切换** 解答: 1. 在AndroidManifest.xml中配置Activity android:configChanges="orientation|screenSize"。