wangliang_wd

Merge branch 'main' of http://192.168.1.42/developOne/harmonyPool into main

* 'main' of http://192.168.1.42/developOne/harmonyPool:
  ref |> 搜索集成语音输入
  ref |> 评论输入面板集成语音识别
  ref |> 初步写语音输入UI
  过时代码注销
Showing 28 changed files with 1287 additions and 28 deletions
... ... @@ -888,4 +888,11 @@ export class HttpUrlUtils {
let url = HttpUrlUtils.getHost() + HttpUrlUtils.VISITOR_COMMENT_LIST_DATA_PATH
return url
}
///获取语音识别授权token
static getVoiceRecognizerSDKAuthToken() {
let url = HttpUrlUtils.getHost() + "/api/rmrb-search-api/zh/c/display/search/token"
return url
}
}
... ...
... ... @@ -181,4 +181,6 @@ export { TopicDetailData,GroupItem } from './src/main/ets/bean/content/TopicDeta
export { FeedbackTypeBean } from './src/main/ets/bean/detail/FeedbackTypeBean';
export { FeedBackParams } from './src/main/ets/bean/content/FeedBackParams';
export { NetLayerVoiceRecoginizerToken } from './src/main/ets/bean/voicerecoginizer/NetLayerVoiceRecoginizerToken';
... ...
export interface NetLayerVoiceRecoginizerToken {
accessToken: string
///过期时间,到毫秒
expireTime: number
}
\ No newline at end of file
... ...
... ... @@ -105,4 +105,8 @@ export { CustomPullToRefresh } from './src/main/ets/components/reusable/CustomPu
export { MorningEveningViewModel } from './src/main/ets/viewmodel/MorningEveningViewModel'
export { GrayManageModel } from './src/main/ets/viewmodel/GrayManageModel'
\ No newline at end of file
export { GrayManageModel } from './src/main/ets/viewmodel/GrayManageModel'
export { VoiceInputView } from './src/main/ets/components/comment/view/VoiceInputView'
// export {voicese}
\ No newline at end of file
... ...
... ... @@ -5,11 +5,11 @@ import { promptAction } from '@kit.ArkUI';
import { image } from '@kit.ImageKit';
import { photoAccessHelper } from '@kit.MediaLibraryKit';
import fs from '@ohos.file.fs';
import { DateTimeUtils, NetworkUtil, PermissionUtil } from 'wdKit';
import { DateTimeUtils, NetworkUtil, PermissionUtil, PermissionUtils } from 'wdKit';
import { PrivacySettingModel } from '../model/PrivacySettingModel';
import { TrackConstants, TrackingButton } from 'wdTracking';
import { faceDetector } from '@kit.CoreVisionKit';
import mediaLibrary from '@ohos.multimedia.mediaLibrary';
// import mediaLibrary from '@ohos.multimedia.mediaLibrary';
import fileIo from '@ohos.file.fs';
import { httpRequest } from '../utils/httpRequest';
import { taskpool } from '@kit.ArkTS';
... ... @@ -61,6 +61,15 @@ export struct ImageDownloadComponent {
.iconColor(Color.White)
.onClick(async () => {
//console.info(`cj2024 onClick ${this.imageBuffer}`)
// let readGrant = await PermissionUtils.checkPermissions(PERMISSIONS[0])
// let writeGrant = await PermissionUtils.checkPermissions(PERMISSIONS[1])
// console.info(`cj2024 onClick readGrant ${readGrant} writeGrant ${writeGrant}`)
// if (!readGrant && !writeGrant) {
// const context = getContext(this) as common.UIAbilityContext;
// let requestGrant = await PermissionUtils.reqPermissionsFromUser(PERMISSIONS, context);
// } else {
//
// }
if (this.imageBuffer !== undefined) {
try {
await this.saveImage(this.imageBuffer);
... ... @@ -134,7 +143,7 @@ export struct ImageDownloadComponent {
* @returns
*/
async saveImage(buffer: ArrayBuffer | string): Promise<void> {
//console.info(`cj2024 saveImage buffer ${buffer} isGranted = ${this.isGranted}`)
console.info(`cj2024 saveImage buffer ${buffer} isGranted = ${this.isGranted}`)
if (!this.isGranted) {
//跳转权限设置
// const permissionUtil = new PermissionUtil();
... ... @@ -154,23 +163,23 @@ export struct ImageDownloadComponent {
// const file = await fs.open(uri, fs.OpenMode.READ_WRITE | fs.OpenMode.CREATE);
// await fs.write(file.fd, buffer);
// await fs.close(file.fd);
let media = mediaLibrary.getMediaLibrary(context);
let publicPath = await media.getPublicDirectory(mediaLibrary.DirectoryType.DIR_IMAGE);
let picName = DateTimeUtils.getCurDate(DateTimeUtils.PATTERN_DATE_TIME_DEFAULT) + '.jpg'
// //console.info(`cj2024 saveImage time ${picName}`)
let asset = await media.createAsset(mediaLibrary.MediaType.IMAGE, picName, publicPath);
if (asset) {
let fd = await asset.open('rw');
await fileIo.write(fd, buffer);
await asset.close(fd);
promptAction.showToast({ message: '已保存至相册!' });
} else {
if (this.isGranted) {
promptAction.showToast({ message: '保存图片失败!' });
}
throw new Error('Failed to create asset');
}
// let media = mediaLibrary.getMediaLibrary(context);
// let publicPath = await media.getPublicDirectory(mediaLibrary.DirectoryType.DIR_IMAGE);
// let picName = DateTimeUtils.getCurDate(DateTimeUtils.PATTERN_DATE_TIME_DEFAULT) + '.jpg'
// // //console.info(`cj2024 saveImage time ${picName}`)
// let asset = await media.createAsset(mediaLibrary.MediaType.IMAGE, picName, publicPath);
//
// if (asset) {
// let fd = await asset.open('rw');
// await fileIo.write(fd, buffer);
// await asset.close(fd);
// promptAction.showToast({ message: '已保存至相册!' });
// } else {
// if (this.isGranted) {
// promptAction.showToast({ message: '保存图片失败!' });
// }
//
// throw new Error('Failed to create asset');
// }
}
}
\ No newline at end of file
... ...
import { inputMethodEngine } from '@kit.IMEKit'
import { commentInfo } from 'wdBean/Index'
import { VoiceRecoginizer } from 'wdHwAbility'
import { ToastUtils } from 'wdKit/Index'
import { HttpUtils } from 'wdNetwork/Index'
import { commentItemModel } from '../model/CommentModel'
import { publishCommentModel } from '../model/PublishCommentModel'
import commentViewModel from '../viewmodel/CommentViewModel'
import { VoiceInputView } from './VoiceInputView'
import { common, UIAbility } from '@kit.AbilityKit'
export interface CommentDialogInputContent {
comment?: string
... ... @@ -27,6 +30,7 @@ export struct CommentCustomDialog {
onPublishBtnClickTrack?: () => void // 和confirm配套使用。当设置了onPublishBtnClick 则无效
@State private emojiSwitch: boolean = false
@State private voiceSwitch: boolean = false
textInputController: TextAreaController = new TextAreaController()
@State positionInfo: CaretOffset = { index: 0, x: 0, y: 0 }
@State publishButtonOpacity: number = 0.5
... ... @@ -105,6 +109,9 @@ export struct CommentCustomDialog {
if (this.emojiSwitch) {
this.emojiSwitch = false
}
if (this.voiceSwitch) {
this.voiceSwitch = false
}
})
}
.backgroundColor('#F9F9F9')
... ... @@ -117,8 +124,21 @@ export struct CommentCustomDialog {
Row() {
Row({ space: 12 }) {
//语音暂时不做,隐藏
// Image($r('app.media.WDInput_voice')).width(30).height(30)
Image($r(this.voiceSwitch ? 'app.media.WDInput_keyboardImage' : 'app.media.WDInput_voice'))
.width(30)
.height(30)
.onClick(() => {
VoiceRecoginizer.checkPemmission(getContext(this) as common.UIAbilityContext)
this.positionInfo = this.textInputController.getCaretOffset()
this.voiceSwitch = !this.voiceSwitch
this.emojiSwitch = false
if (this.voiceSwitch) {
this.textInputController.stopEditing()
} else {
focusControl.requestFocus("textAreaId") // 弹起键盘
// this.textInputController.caretPosition(this.textInputController.getTextContentRect().x)
}
})
Image($r(this.emojiSwitch ? 'app.media.WDInput_keyboardImage' : 'app.media.WDInput_emojiImage'))
.width(30)
.height(30)
... ... @@ -127,6 +147,7 @@ export struct CommentCustomDialog {
this.positionInfo = this.textInputController.getCaretOffset()
this.emojiSwitch = !this.emojiSwitch
this.voiceSwitch = false
if (this.emojiSwitch) {
this.textInputController.stopEditing()
} else {
... ... @@ -137,6 +158,7 @@ export struct CommentCustomDialog {
})
}
// Blank()
Row() {
... ... @@ -172,6 +194,12 @@ export struct CommentCustomDialog {
if (this.emojiSwitch) {
emojiView({ publishCommentModel: this.publishCommentModel }).height(150)
}
if (this.voiceSwitch) {
VoiceInputView({voiceRecoginizerResult:(result: string) => {
this.publishCommentModel.commentContent = result;
}}).height(150)
}
}.backgroundColor(Color.White)
.width('100%')
... ...
import Lottie from '@ohos/lottie'
import { LottieView } from '../../lottie/LottieView'
import lottie from '@ohos/lottie';
import { CommonConstants } from 'wdConstant';
import { common} from '@kit.AbilityKit';
import { VoiceRecoginizer } from 'wdHwAbility/src/main/ets/voiceRecognizer/VoiceRecoginizer';
@Component
export struct VoiceInputView {
@State private playAnimationOne: boolean = false
// @State private playAnimationTwo: boolean = false
@State private tips:string = "长按开始语音输入"
voiceRe: VoiceRecoginizer = VoiceRecoginizer.sharedManager()
voiceRecoginizerResult?:(result: string) => void
aboutToAppear(): void {
this.initVoiceRecoigzer()
}
aboutToDisappear(): void {
this.voiceRe.stop()
this.voiceRe.voiceRecoginizerResult = undefined
}
build() {
Column() {
Stack() {
Row() {
if (this.playAnimationOne) {
LottieView({
name: 'voice_input_one',
path: "lottie/search_animation_two.json",
lottieWidth: 100,
lottieHeight: 100,
autoplay: true,
loop: true,
// onComplete:() => {
// this.playAnimationOne = false
// }
})
}
}.height(100)
Button()
.type(ButtonType.Circle)
.backgroundImage($r('app.media.WDInput_voice_longPress_icon'))
.backgroundColor(Color.White)
.width(60)
.height(60)
.gesture(
LongPressGesture()
.onAction((event: GestureEvent) => {
// this
this.playAnimationOne = true
this.tips = ""
this.voiceRe.startDialog()
})
.onActionEnd((event: GestureEvent) => {
this.tips = "长按开始语音输入"
this.playAnimationOne = false
this.voiceRe.stop()
// this.voiceRe.voiceRecoginizerResult = undefined
})
)
}
Blank()
Text(this.tips)
.fontSize(13)
.fontColor("#222222")
}
}
initVoiceRecoigzer() {
let context = getContext(this) as common.UIAbilityContext
let resoureDir = context.resourceDir
this.voiceRe.AsrInit(context.resourceDir, context.filesDir)
this.voiceRe.voiceRecoginizerResult = (result: string) => {
if (this.voiceRecoginizerResult) {
this.voiceRecoginizerResult(result)
}
}
}
}
\ No newline at end of file
... ...
import router from '@ohos.router'
import { VoiceRecoginizer } from 'wdHwAbility'
import { BreakpointSystem, DateTimeUtils, NetworkUtil, StringUtils, ToastUtils } from 'wdKit'
import { ParamType, TrackConstants, Tracking } from 'wdTracking/Index'
import SearcherAboutDataModel from '../../model/SearcherAboutDataModel'
... ... @@ -8,6 +9,8 @@ import { SearchHistoryComponent } from './SearchHistoryComponent'
import { SearchHotsComponent } from './SearchHotsComponent'
import { SearchRelatedComponent } from './SearchRelatedComponent'
import { SearchResultComponent } from './SearchResultComponent'
import { VoiceSearchCustomDialog } from './VoiceSearchCustomDialog'
import { common } from '@kit.AbilityKit'
const TAG = "SearchComponent"
... ... @@ -37,6 +40,7 @@ export struct SearchComponent {
@StorageProp('currentBreakpoint') @Watch("currentChanged") currentBreakpoint: string = 'sm';
private breakpointSystem = new BreakpointSystem();
@State percent: number = 1
private commentInputDialogController?: CustomDialogController
currentChanged() {
if (this.currentBreakpoint == "md" || this.currentBreakpoint == "lg") {
... ... @@ -366,17 +370,22 @@ export struct SearchComponent {
}
})
Image($r('app.media.search_input_del_icon'))
Image($r(StringUtils.isEmpty(this.searchText) ? 'app.media.search_voice_icon' : 'app.media.search_input_del_icon'))
.width(`${this.calcHeight(31)}lpx`)
.height(`${this.calcHeight(31)}lpx`)
.objectFit(ImageFit.Auto)
.interpolation(ImageInterpolation.Medium)
.onClick(() => {
this.searchText = ""
if (StringUtils.isEmpty(this.searchText)) { ///唤起语音搜索
this.stopInput()
this.voiceSearchDialog()
}else { ///清除输入内容
this.searchText = ""
}
})
.offset({ x: 10 })
.enabled(true)
.visibility(StringUtils.isEmpty(this.searchText) ? Visibility.Hidden : Visibility.Visible)
// .visibility(StringUtils.isEmpty(this.searchText) ? Visibility.Hidden : Visibility.Visible)
}.padding({ right: `${this.calcHeight(70)}lpx` })
.layoutWeight(1)
... ... @@ -406,6 +415,31 @@ export struct SearchComponent {
.alignItems(VerticalAlign.Center)
}
private voiceSearchDialog() {
VoiceRecoginizer.checkPemmission(getContext(this) as common.UIAbilityContext)
this.commentInputDialogController = new CustomDialogController({
builder: VoiceSearchCustomDialog (
{ onSearchBtnClick:((content:string) => {
this.commentInputDialogController?.close()
this.searchText = content
if (StringUtils.isNotEmpty(this.searchText)) {
SearcherAboutDataModel.putSearchHistoryData(this.searchText)
this.getSearchHistoryData()
this.getSearchInputResData(this.searchText)
}
})
}
),
autoCancel: true,
alignment: DialogAlignment.Bottom,
customStyle: true,
backgroundColor: "#50000000",
}).open()
}
getSearchResultCountData() {
SearcherAboutDataModel.getSearchResultCountData(encodeURI(this.searchText), getContext(this)).then((value) => {
if (value != null) {
... ...
import { VoiceRecoginizer } from 'wdHwAbility'
import { common, UIAbility } from '@kit.AbilityKit'
import { VoiceInputView } from '../comment/view/VoiceInputView'
@Preview
@CustomDialog
export struct VoiceSearchCustomDialog {
controller?: CustomDialogController
onSearchBtnClick?: (content: string) => void
@State voiceContent: string = ""
build() {
Column() {
Row() {
TextArea({
placeholder: '我来说两句',
text: this.voiceContent
})
.placeholderColor("#999999")
.id("textAreaId")
.defaultFocus(false) // 获取默认焦点
.enableKeyboardOnFocus(true)
.height('100%')
.width('100%')
.backgroundColor($r('app.color.color_transparent'))
.caretColor("#ED2800")
.onFocus(() => {
this.controller?.close()
})
}
.backgroundColor('#F9F9F9')
.margin({ top: 12, right: 12, left: 12, bottom:10})
.height(80)
.borderRadius(4)
Row() {
Image($r('app.media.WDInput_keyboardImage'))
.width(30)
.height(30)
.onClick(() => {
this.controller?.close()
})
Text('搜索')
.backgroundColor("#ED2800")
.width(80)
.height(30)
.fontSize(15)
.fontColor(Color.White)
.textAlign(TextAlign.Center)
.borderRadius(4)
.opacity(this.voiceContent.length > 0 ? 1.0 : 0.5)
.enabled(this.voiceContent.length > 0)
.onClick(() => {
if (this.onSearchBtnClick) {
this.onSearchBtnClick(this.voiceContent)
}
})
}
.padding({ left: 12, right: 12 })
.width('100%')
.justifyContent(FlexAlign.SpaceBetween)
VoiceInputView({voiceRecoginizerResult:(result: string) => {
this.voiceContent = result
}}).height(150)
}.backgroundColor(Color.White)
.width('100%')
}
}
\ No newline at end of file
... ...
... ... @@ -3,4 +3,6 @@ export { add } from "./src/main/ets/utils/Calc"
export { HWLocationUtils } from './src/main/ets/location/HWLocationUtils'
// export { WDPushNotificationManager } from "./src/main/ets/notification/WDPushNotificationManager"
export { GetuiPush } from "./src/main/ets/getuiPush/GetuiPush"
\ No newline at end of file
export { GetuiPush } from "./src/main/ets/getuiPush/GetuiPush"
export {VoiceRecoginizer} from './src/main/ets/voiceRecognizer/VoiceRecoginizer'
\ No newline at end of file
... ...
... ... @@ -8,6 +8,7 @@
"packageType": "InterfaceHar",
"dependencies": {
"library": "file:./src/main/ets/getuiPush/GT-HM-1.0.4.har",
"neonui":"file:./src/main/ets/voiceRecognizer/neonui.har",
"wdConstant": "file:../../commons/wdConstant",
"wdLogin": "file:../../features/wdLogin",
"wdKit": "file:../../commons/wdKit",
... ...
import { audio } from '@kit.AudioKit'
import { ArkTSUtils } from '@kit.ArkTS'
import {getVoicebufferDataLengthByte} from 'neonui'
import process from '@ohos.process'
import { NativeNui } from 'neonui'
let TAG:string = "AudioCapturer"
//音频采集器类,主要用于采集音频,写入文件
export default class AudioCapturer{
static lock_:ArkTSUtils.locks.AsyncLock = new ArkTSUtils.locks.AsyncLock()
//static voiceBuffer2:Int16Array=new Int16Array(0);
static voiceBuffer1:ArrayBuffer[] = [];
//static voiceBuffer:ArrayBuffer[] =[];
static async clearVoiceArrayBuffer(){
await AudioCapturer.lock_.lockAsync( ()=>{
if (AudioCapturer.voiceBuffer1!==undefined){
AudioCapturer.voiceBuffer1= []
}
console.info(`AudioCapturer clearVoiceArrayBuffer, then voiceBuffer1 size ${AudioCapturer.voiceBuffer1.length}`);
})
}
static async setVoiceArrayBuffer(voice:ArrayBuffer){
console.info(`AudioCapturer setVoiceArrayBuffer, 1 voiceBuffer1 size ${AudioCapturer.voiceBuffer1.length}`);
AudioCapturer.lock_.lockAsync( ()=>{
if (AudioCapturer.voiceBuffer1==undefined){
AudioCapturer.voiceBuffer1= []
}
AudioCapturer.voiceBuffer1.push(voice)
console.info(`AudioCapturer setVoiceArrayBuffer, 3 voiceBuffer1 size ${AudioCapturer.voiceBuffer1.length}`);
})
console.info(`AudioCapturer setVoiceArrayBuffer, 2 voiceBuffer1 size ${AudioCapturer.voiceBuffer1.length}`);
}
static async getVoiceArrayBuffer(buffer:ArrayBuffer):Promise<number>{
let ret:number;
let outbuferleng=buffer.byteLength;
let buffer_out_typedarray = new Int8Array(buffer)
let offset = 0
console.log("AudioCapturer enter getVoiceArrayBuffer");
return await AudioCapturer.lock_.lockAsync( ()=>{
console.info(`AudioCapturer womx getVoiceArrayBuffer, outbuferleng ${buffer.byteLength}.with length ${AudioCapturer.voiceBuffer1.length}`);
// while(AudioRenderer.voiceBuffer1.length <= 0){
// if(AudioRenderer.flagVoiceEnd==true) {
// }
// }
if (AudioCapturer.voiceBuffer1.length > 0) {
let voice_length = getVoicebufferDataLengthByte(AudioCapturer.voiceBuffer1)
if (voice_length>=outbuferleng) {
let bytes_need = outbuferleng
while(bytes_need>0){
let voice1st = AudioCapturer.voiceBuffer1.shift()
if (voice1st==undefined ){
} else {
let out_typedbuffer:Int8Array;
console.info(`AudioCapturer womx voice1st.byteLength=${voice1st.byteLength} vs bytes_need=${bytes_need}`);
if (voice1st.byteLength > bytes_need) {
let out_buffer = voice1st.slice(0,bytes_need)
out_typedbuffer = new Int8Array(out_buffer)
let save_buffer = voice1st.slice(bytes_need)
AudioCapturer.voiceBuffer1.unshift(save_buffer)
} else {
out_typedbuffer = new Int8Array(voice1st)
}
for (let i = 0; i < out_typedbuffer.byteLength; i++) {
buffer_out_typedarray[offset + i] = out_typedbuffer[i]
}
bytes_need -= out_typedbuffer.byteLength
offset += out_typedbuffer.byteLength
console.info(`AudioCapturer womx bytes_need=${bytes_need}`);
}
}
} else {
ret = 0
console.error(`AudioCapturer error getVoiceArrayBuffer, outbuferleng 0.with ringbuffer voicebytes length ${voice_length}`);
}
} else {
ret = 0;
console.error(`AudioCapturer error getVoiceArrayBuffer, outbuferleng 0.with ringbuffer.length ${AudioCapturer.voiceBuffer1.length}<=0`);
}
return ret
})
}
static getVoiceArrayBuffer1(buffer:ArrayBuffer):number{
let ret:number=0;
let outbuferleng=buffer.byteLength;
let buffer_out_typedarray = new Int8Array(buffer)
let offset = 0
console.log("AudioCapturer enter getVoiceArrayBuffer");
console.info(`AudioCapturer womx getVoiceArrayBuffer, outbuferleng ${buffer.byteLength}.with length ${AudioCapturer.voiceBuffer1.length}`);
// while(AudioRenderer.voiceBuffer1.length <= 0){
// if(AudioRenderer.flagVoiceEnd==true) {
// }
// }
if (AudioCapturer.voiceBuffer1.length > 0) {
let voice_length = getVoicebufferDataLengthByte(AudioCapturer.voiceBuffer1)
if (voice_length>=outbuferleng) {
let bytes_need = outbuferleng
while(bytes_need>0){
let voice1st = AudioCapturer.voiceBuffer1.shift()
if (voice1st==undefined ){
break
} else {
let out_typedbuffer:Int8Array;
console.info(`AudioCapturer womx voice1st.byteLength=${voice1st.byteLength} vs bytes_need=${bytes_need}`);
if (voice1st.byteLength > bytes_need) {
let out_buffer = voice1st.slice(0,bytes_need)
out_typedbuffer = new Int8Array(out_buffer)
let save_buffer = voice1st.slice(bytes_need)
AudioCapturer.voiceBuffer1.unshift(save_buffer)
} else {
out_typedbuffer = new Int8Array(voice1st)
}
for (let i = 0; i < out_typedbuffer.byteLength; i++) {
buffer_out_typedarray[offset + i] = out_typedbuffer[i]
}
bytes_need -= out_typedbuffer.byteLength
offset += out_typedbuffer.byteLength
console.info(`AudioCapturer womx bytes_need=${bytes_need}`);
}
}
ret = outbuferleng - bytes_need
} else {
ret = 0
console.error(`AudioCapturer error getVoiceArrayBuffer, outbuferleng 0.with ringbuffer voicebytes length ${voice_length}`);
}
} else {
ret = 0;
console.error(`AudioCapturer error getVoiceArrayBuffer, outbuferleng 0.with ringbuffer.length ${AudioCapturer.voiceBuffer1.length}<=0`);
}
return ret;
}
//定义音频流信息
static audioStreamInfo:audio.AudioStreamInfo = {
//彩样率
samplingRate:audio.AudioSamplingRate.SAMPLE_RATE_16000,
//通道数
channels:audio.AudioChannel.CHANNEL_1,
//采样格式
sampleFormat:audio.AudioSampleFormat.SAMPLE_FORMAT_S16LE,
//编码格式
encodingType:audio.AudioEncodingType.ENCODING_TYPE_RAW
}
//定义音频采集器信息
static audioCapturerInfo:audio.AudioCapturerInfo={
//音源类型
source:audio.SourceType.SOURCE_TYPE_MIC,
//音频采集器标志
capturerFlags:0
}
//定义音频采集器
static audioCapturer:audio.AudioCapturer
static g_asrinstance:NativeNui
//初始化音频采集器
static async init(asrinstance:NativeNui){
AudioCapturer.g_asrinstance = asrinstance
AudioCapturer.audioCapturer = await audio.createAudioCapturer({
//需要音频流信息和音频采集器信息
streamInfo:AudioCapturer.audioStreamInfo,
capturerInfo:AudioCapturer.audioCapturerInfo
})
if (AudioCapturer.audioCapturer !== undefined) {
await AudioCapturer.audioCapturer.on('readData', AudioCapturer.readDataCallback);
}
}
static readDataCallback = (buffer: ArrayBuffer) => {
console.log(`${TAG} read data bytelength is ${buffer.byteLength}. uid[${process.uid}] pid[${process.pid}] tid[${process.tid}]`);
//AudioCapturer.setVoiceArrayBuffer(buffer)
AudioCapturer.g_asrinstance.updateAudio(buffer,false)
}
//开始采集音频
static async start(){
await AudioCapturer.clearVoiceArrayBuffer()
if (AudioCapturer.audioCapturer) {
let stateGroup = [audio.AudioState.STATE_PREPARED,
audio.AudioState.STATE_PAUSED,
audio.AudioState.STATE_STOPPED];
if (stateGroup.indexOf(AudioCapturer.audioCapturer.state.valueOf()) === -1) {
// 当且仅当状态为STATE_PREPARED、STATE_PAUSED和STATE_STOPPED之一时才能启动采集
console.error(`${TAG}: start failed`);
console.error('Capturer is not STATE_PREPARED or STATE_PAUSED or STATE_STOPPED');
return;
}
//开始录音
await AudioCapturer.audioCapturer.start()
console.log(`${TAG} start done`);
} else {
console.log(`${TAG} start with AudioCapturer.audioCapturer is null`);
return
}
}
//停止采集音频
static async stop(){
if (AudioCapturer.audioCapturer) {
// 只有采集器状态为STATE_RUNNING或STATE_PAUSED的时候才可以停止
if (AudioCapturer.audioCapturer.state === audio.AudioState.STATE_RUNNING ||
AudioCapturer.audioCapturer.state === audio.AudioState.STATE_PAUSED
) {
console.error(`Capturer state is ${AudioCapturer.audioCapturer.state}`);
await AudioCapturer.audioCapturer.stop()/*.then( (value)=>{
console.log("result of Capturer stop is " + value);
}); // 停止采集*/
console.error('Capturer stop done');
} else {
console.error('Capturer is not running or paused');
return;
}
} else {
console.log(`${TAG} stop with AudioCapturer.audioCapturer is null`);
return
}
await AudioCapturer.clearVoiceArrayBuffer()
}
// 释放资源
static async release() {
if (AudioCapturer.audioCapturer) {
// 采集器状态不是STATE_RELEASED或STATE_NEW状态,才能release
if (AudioCapturer.audioCapturer.state === audio.AudioState.STATE_RELEASED ||
AudioCapturer.audioCapturer.state === audio.AudioState.STATE_NEW) {
console.info('Capturer already released');
return;
}
await AudioCapturer.audioCapturer.release()
}
}
}
... ...
import {NativeNui,Constants,MapToJson,INativeNuiCallback, AsrResult, KwsResult} from 'neonui'
import { PermissionUtils,DeviceUtil, Logger, DateTimeUtils } from 'wdKit';
import AudioCapturer from './AudioCapture';
import { common, Permissions } from '@kit.AbilityKit';
import { NetLayerVoiceRecoginizerToken } from 'wdBean';
import { HttpUrlUtils,ResponseDTO } from 'wdNetwork'
import { HttpRequest } from 'wdNetwork/src/main/ets/http/HttpRequest'
class NativeNuiCallbackHandle implements INativeNuiCallback {
asrspeechrealtimeResultOld:string=""
asrmessage:string;
message:string;
nuiCallback?:(result: string) => void
constructor() {
this.asrmessage=""
this.message=""
}
onNuiEventCallback(event:Constants.NuiEvent, resultCode:number, arg2:number, kwsResult:KwsResult,
asrResult:AsrResult):void {
let asrinfo:string = ""
// console.log("onUsrNuiEventCallback last this.asrmessage is " + this.asrmessage)
// console.log("onUsrNuiEventCallback this is " + JSON.stringify(this))
// console.log("onUsrNuiEventCallback event is " + event)
if (event === Constants.NuiEvent.EVENT_ASR_RESULT){
this.message = "EVENT_ASR_RESULT"
this.asrspeechrealtimeResultOld=""
} else if (event === Constants.NuiEvent.EVENT_ASR_PARTIAL_RESULT || event === Constants.NuiEvent.EVENT_SENTENCE_END){
if (event === Constants.NuiEvent.EVENT_ASR_PARTIAL_RESULT ) {
this.message = "EVENT_ASR_PARTIAL_RESULT"
} else if(event === Constants.NuiEvent.EVENT_SENTENCE_END){
this.message = "EVENT_SENTENCE_END"
}
} else if (event === Constants.NuiEvent.EVENT_ASR_ERROR){
this.message = "EVENT_ASR_ERROR"
} else if (event === Constants.NuiEvent.EVENT_MIC_ERROR){
this.message = "EVENT_MIC_ERROR"
} else if (event === Constants.NuiEvent.EVENT_DIALOG_EX){
this.message = "EVENT_DIALOG_EX"
}
if (asrResult) {
asrinfo = asrResult.asrResult
//console.log(`asrinfo string is ${asrinfo}`)
if (asrinfo) {
try {
let asrresult_json:object|null = JSON.parse(asrResult.asrResult)
//console.log(JSON.stringify(asrresult_json))
if (asrresult_json) {
let payload:object|null = asrresult_json["payload"];
if (payload) {
//console.log(JSON.stringify(payload))
this.asrmessage = this.asrspeechrealtimeResultOld + payload["result"];
if(event === Constants.NuiEvent.EVENT_SENTENCE_END){
// console.info("onUsrNuiEventCallback EVENT_SENTENCE_END")
this.asrspeechrealtimeResultOld = this.asrmessage
}
if (this.nuiCallback) {
this.nuiCallback(this.asrmessage)
}
}
}
} catch (e){
// console.error("got asrinfo not json, so donot fresh asrinfo." + JSON.stringify(e))
}
}
}
// console.info(`womx onUsrNuiEventCallback(${event}, ${resultCode},${arg2}, kwsResult:${kwsResult},asrResult:"${this.asrmessage}") done`)
}
onNuiNeedAudioData(buffer:ArrayBuffer):number {
// console.info(`womx onUsrNuiNeedAudioData(buffer length = ${buffer.byteLength})`)
// console.log(`womx onUsrNuiNeedAudioData uid[${process.uid}] pid[${process.pid}] tid[${process.tid}]`);
let num:number = 0;
if (true){
if (true){
//由于录音回调是异步的,所以此处获取共享数据需要加锁/并保持同步 是较难的,需要修改asr回调为用户主动推送方式
num = AudioCapturer.getVoiceArrayBuffer1(buffer)
} else {
let flag_returned:boolean = false
try {
AudioCapturer.getVoiceArrayBuffer(buffer).then( (result:number)=>{
console.log("womx onUsrNuiNeedAudioData getVoiceArrayBuffer done, result="+result)
num = result
flag_returned=true
})
} catch(e ){
flag_returned=true
}
while(flag_returned==false){
console.log("womx onUsrNuiNeedAudioData waiting ... num="+num)
}
}
}
// console.info(`womx onUsrNuiNeedAudioData(buffer length = ${buffer.byteLength}) return ${num}}`)
return num;//buffer.byteLength;
}
onNuiAudioStateChanged(state:Constants.AudioState):void {
// console.info(`womx onUsrNuiAudioStateChanged(${state})`)
if (state === Constants.AudioState.STATE_OPEN){
// console.info(`womx onUsrNuiAudioStateChanged(${state}) audio recorder start`)
//AudioCapturer.init(g_asrinstance)
AudioCapturer.start()
// console.info(`womx onUsrNuiAudioStateChanged(${state}) audio recorder start done`)
} else if (state === Constants.AudioState.STATE_CLOSE){
// console.info(`womx onUsrNuiAudioStateChanged(${state}) audio recorder close`)
AudioCapturer.stop()
//AudioCapturer.release()
// console.info(`womx onUsrNuiAudioStateChanged(${state}) audio recorder close done`)
} else if (state === Constants.AudioState.STATE_PAUSE){
// console.info(`womx onUsrNuiAudioStateChanged(${state}) audio recorder pause`)
AudioCapturer.stop()
//AudioCapturer.release()
// console.info(`womx onUsrNuiAudioStateChanged(${state}) audio recorder pause done`)
}
}
onNuiAudioRMSChanged(val:number):number{
// console.info(`womx onNuiAudioRMSChanged(${val})`)
return 0;
}
clearAsrInfo(){
this.asrmessage = ""
}
}
const TAG = 'VoiceRecoginizer'
export class VoiceRecoginizer {
private static MICROPHONEMISSION: Permissions = 'ohos.permission.MICROPHONE'
private static READMEDIA: Permissions = 'ohos.permission.READ_MEDIA'
private static INTENT: Permissions = "ohos.permission.INTERNET"
private static WRITE_MEDIA: Permissions = "ohos.permission.WRITE_MEDIA"
private static appKey = "EospGmM6mdPljjjm"
private static url = "wss://nls-gateway.cn-shanghai.aliyuncs.com:443/ws/v1"
private authToken?: NetLayerVoiceRecoginizerToken
private vadMode: boolean = false
private cbhandle: NativeNuiCallbackHandle = new NativeNuiCallbackHandle()
private g_asrinstance: NativeNui = new NativeNui(Constants.ModeType.MODE_DIALOG, "asr1")
private static manager: VoiceRecoginizer
public static sharedManager(): VoiceRecoginizer {
if (!VoiceRecoginizer.manager) {
VoiceRecoginizer.manager = new VoiceRecoginizer()
}
return VoiceRecoginizer.manager
}
voiceRecoginizerResult?:(result: string) => void
// public voiceRecoginzerResult:(result: string) => void
genParams():string {
let params:string = "";
let nls_config:Map<string, string|number|boolean|object> = new Map();
nls_config.set("enable_intermediate_result", true);
//参数可根据实际业务进行配置
//接口说明可见: https://help.aliyun.com/document_detail/173298.html
//查看 2.开始识别
//由于对外的SDK不带有本地VAD模块(仅带有唤醒功能的SDK具有VAD模块),
//若要使用VAD模式,则需要设置nls_config参数启动在线VAD模式(见genParams())
if (this.vadMode) {
nls_config.set("enable_voice_detection", true);
nls_config.set("max_start_silence", 10000);
nls_config.set("max_end_silence", 800);
} else {
nls_config.set("enable_voice_detection", false);
}
nls_config.set("enable_punctuation_prediction", true);
nls_config.set("enable_inverse_text_normalization", true);
// nls_config.set("customization_id", "test_id");
// nls_config.set("vocabulary_id", "test_id");
// nls_config.set("sample_rate", 16000);
// nls_config.set("sr_format", "opus");
let parameters:Map<string, string|number|boolean|object> = new Map();
parameters.set("nls_config", Object( JSON.parse(MapToJson(nls_config)) ) );
//一句话识别
// console.log("start asr for 一句话识别")
parameters.set("service_type", Constants.kServiceTypeASR); // 必填
//如果有HttpDns则可进行设置
//parameters.put("direct_ip", Utils.getDirectIp());
params = MapToJson(parameters);//parameters.toString();
console.log("configinfo genParams" + params)
return params;
}
genInitParams(workpath:string, debugpath:string):string {
let str:string = "";
//获取token方式:
let object:Map<string, string|number|boolean|object> = new Map();
//账号和项目创建
// ak_id ak_secret app_key如何获得,请查看https://help.aliyun.com/document_detail/72138.html
object.set("app_key",VoiceRecoginizer.appKey); // 必填
//方法1:
// 首先ak_id ak_secret app_key如何获得,请查看https://help.aliyun.com/document_detail/72138.html
// 然后请看 https://help.aliyun.com/document_detail/466615.html 使用其中方案一获取临时凭证
// 此方案简介: 远端服务器生成具有有效时限的临时凭证, 下发给移动端进行使用, 保证账号信息ak_id和ak_secret不被泄露
// 获得Token方法(运行在APP服务端): https://help.aliyun.com/document_detail/450255.html?spm=a2c4g.72153.0.0.79176297EyBj4k
if (this.authToken) {
object.set("token", this.authToken.accessToken)
}
//方法2:
// STS获取临时凭证方法暂不支持
//方法3:(强烈不推荐,存在阿里云账号泄露风险)
// 参考Auth类的实现在端上访问阿里云Token服务获取SDK进行获取。请勿将ak/sk存在本地或端侧环境。
// 此方法优点: 端侧获得Token, 无需搭建APP服务器。
// 此方法缺点: 端侧获得ak/sk账号信息, 极易泄露。
// JSONObject object = Auth.getAliYunTicket();
object.set("device_id", DeviceUtil.clientId()/*Utils.getDeviceId()*/); // 必填, 推荐填入具有唯一性的id, 方便定位问题
object.set("url", VoiceRecoginizer.url); // 默认
object.set("workspace", workpath); // 必填, 且需要有读写权限
// object.set("sample_rate", "16000");
// object.set("format", "pcm");
//当初始化SDK时的save_log参数取值为true时,该参数生效。表示是否保存音频debug,该数据保存在debug目录中,需要确保debug_path有效可写。
// object.put("save_wav", "true");
//debug目录,当初始化SDK时的save_log参数取值为true时,该目录用于保存中间音频文件。
object.set("debug_path", debugpath);
// FullMix = 0 // 选用此模式开启本地功能并需要进行鉴权注册
// FullCloud = 1
// FullLocal = 2 // 选用此模式开启本地功能并需要进行鉴权注册
// AsrMix = 3 // 选用此模式开启本地功能并需要进行鉴权注册
// AsrCloud = 4
// AsrLocal = 5 // 选用此模式开启本地功能并需要进行鉴权注册
//一句话识别
// console.log("init asr for 一句话识别")
object.set("service_mode", Constants.ModeAsrCloud); // 必填
str = MapToJson(object)
// console.info("configinfo genInitParams:" + str);
return str;
}
async AsrInit(path:string, filePath:string): Promise<number> {
//console.log("AsrInit this is " + JSON.stringify(this))
// console.info("AsrInit path: " + path);
//获取工作路径, 这里获得当前nuisdk.aar中assets路径
let asset_path:string = path+"/resources_cloud"
// fs.stat(asset_path).catch((error: BusinessError) => {
// if (error.code = 13900002) {
// fs.mkdirSync(asset_path)
// }
// })
// fs.mkdirSync(asset_path)
// try {
// await this.checkPath(asset_path)
this.cbhandle.nuiCallback = (result: string) => {
if (this.voiceRecoginizerResult) {
this.voiceRecoginizerResult(result)
}
}
let parameters: string = ""
// this.genInitParams(asset_path,filePath)
// .then(parameters) {
//
// }
await this.getToken()
let ret:number = this.g_asrinstance.initialize(this.cbhandle, this.genInitParams(asset_path,filePath), Constants.LogLevel.LOG_LEVEL_NONE, false);
// console.info("result = " + ret);
if (ret == Constants.NuiResultCode.SUCCESS) {
} else {
// Logger.debug(TAG,"语音识别初始化失败")
//final String msg_text = Utils.getMsgWithErrorCode(ret, "init");
//抛出错误异常信息。
/*
runOnUiThread(new Runnable() {
@Override
public void run() {
Toast.makeText(SpeechRecognizerActivity.this, msg_text, Toast.LENGTH_LONG).show();
}
});
*/
}
// return ret
// }
// catch {
// return -1;
// }
//初始化SDK,注意用户需要在Auth.getAliYunTicket中填入相关ID信息才可以使用。
return ret
}
// checkPath(asset_path:string):Promise<void> {
// return new Promise<void> ((success,fail) => {
// fs.stat(asset_path).catch((error: BusinessError) => {
// if (error.code = 13900002) {
// fs.mkdirSync(asset_path)
// success()
// return
// }
// success()
// })
// })
// }
startDialog():number {
// console.log(`womx startDialog uid[${process.uid}] pid[${process.pid}] tid[${process.tid}]`);
this.cbhandle.clearAsrInfo()
//由于对外的SDK不带有本地VAD模块(仅带有唤醒功能的SDK具有VAD模块),
//若要使用VAD模式,则需要设置nls_config参数启动在线VAD模式(见genParams())
let vad_mode:Constants.VadMode = Constants.VadMode.TYPE_P2T;
if (this.vadMode) {
//TYPE_VAD: SDK自动判断句尾结束识别。(此功能仅存在于<029>带唤醒功能的SDK)
//vad_mode = Constants.VadMode.TYPE_VAD;
// console.info("使用Voice Active Detection模式");
} else {
//TYPE_P2T: 有用户主动stop()以告知识别完成
//vad_mode = Constants.VadMode.TYPE_P2T;
// console.info("使用Push To Talk模式");
}
//设置相关识别参数,具体参考API文档
// initialize()之后startDialog之前调用
this.g_asrinstance.setParams(this.genParams());
AudioCapturer.init(this.g_asrinstance)
let ret:number = this.g_asrinstance.startDialog(vad_mode, "");
// console.info("start done . ret = ", ret);
if (ret != 0) {
// console.info("call startDialog return error. ", ret);
}
return ret
}
stop() {
this.cbhandle.clearAsrInfo()
AudioCapturer.stop()
}
static async checkPemmission (context: common.UIAbilityContext) {
let grant = await PermissionUtils.checkPermissions(VoiceRecoginizer.WRITE_MEDIA)
if (grant) {
///初始化SDK
// Logger.debug("已申请过权限")
return
}
// let context = getContext()
let requestGrant = await PermissionUtils.reqPermissionsFromUser([VoiceRecoginizer.MICROPHONEMISSION
,VoiceRecoginizer.READMEDIA
,VoiceRecoginizer.WRITE_MEDIA
,VoiceRecoginizer.INTENT],context)
if (requestGrant) {
///初始化SDK
}
// return false
}
private async getToken():Promise<NetLayerVoiceRecoginizerToken | undefined> {
return new Promise<NetLayerVoiceRecoginizerToken|undefined>((success,fail) => {
if (this.authToken && this.authToken.accessToken.length > 0) {
if (Date.now() < this.authToken.expireTime - 60 * 1000) {
success(this.authToken)
return
}
}
HttpRequest.get<ResponseDTO<NetLayerVoiceRecoginizerToken>>(
HttpUrlUtils.getVoiceRecognizerSDKAuthToken()
).then((res: ResponseDTO<NetLayerVoiceRecoginizerToken>) => {
if (res.code != 0) {
fail(res.message)
return
}
this.authToken = res.data
success(res.data)
})
})
}
}
\ No newline at end of file
... ...
{
"cei":{
"cei_param_version":"2.2.0",
"cei_param_device_type":"car",
"cei_param_reco_mode":0,
"cei_param_log_level":2,
"cei_param_debug_path":"asr_debug",
"cei_param_is_debug_enable":false,
"cei_param_resource_path":"",
"cei_param_resource_is_multi_language":false,
"cei_param_audio_format_str":"16k16bitmono",
"cei_param_mcs_mode":0,
"cei_param_work_mode":0,
"cei_param_max_cache_frames":1000,
"cei_param_is_aec_bf_active":false,
"cei_param_is_agc_active":false,
"cei_param_is_vad_active":true,
"cei_param_is_kws_active":true,
"cei_param_is_sr_active":true
},
"asp":{
"asp_param_is_process_parallel":false,
"asp_param_is_input_debug_enable":false,
"asp_param_is_output_debug_enable":false,
"asp_param_debug_path":"asr_debug",
"asp_param_is_callback_enable":false,
"asp_param_callback_period_frames":5
},
"vad":{
"vad_param_is_input_debug_enable":false,
"vad_param_is_output_debug_enable":false,
"vad_param_debug_path":"asr_debug",
"vad_param_asleep_speech_noise_thres":-0.8,
"vad_param_awake_speech_noise_thres":-0.5,
"vad_param_asleep_max_speech_segment_time":300000,
"vad_param_awake_max_speech_segment_time":60000,
"vad_param_asleep_block_size":3,
"vad_param_awake_block_size":3,
"vad_param_front_timeout_interval":8000,
"vad_param_tail_timeout_interval":800,
"vad_param_is_detect_start":true,
"vad_param_is_detect_end":true
},
"kws":{
"kws_param_is_input_debug_enable":false,
"kws_param_is_output_debug_enable":false,
"kws_param_debug_path":"asr_debug",
"kws_param_is_process_parallel":false,
"kws_param_front_extend_frames":10,
"kws_param_tail_extend_frames":5,
"kws_param_encoder_type_str":"opu",
"kws_param_encoder_bitrate":16000,
"kws_param_encoder_complexity":2,
"kws_param_callback_period_ms":100,
"kws_param_max_frames_per_callback":25,
"kws_param_max_bytes_per_callback":16000
},
"sr":{
"sr_param_is_input_debug_enable":false,
"sr_param_is_output_debug_enable":false,
"sr_param_debug_path":"asr_debug",
"sr_param_is_itn_enable":true,
"sr_param_is_do_conf_filter":false,
"sr_param_is_process_parallel":true,
"sr_param_is_need_result":false,
"sr_param_is_need_voice":true,
"sr_param_ngram_conf_thres":65.0,
"sr_param_jsgf_conf_thres":65.0,
"sr_param_encoder_type_str":"opu",
"sr_param_encoder_bitrate":16000,
"sr_param_encoder_complexity":2,
"sr_param_callback_period_ms":100,
"sr_param_max_frames_per_callback":25,
"sr_param_max_bytes_per_callback":16000
},
"ou":{
"oss_upload_param_is_enable":false,
"oss_upload_param_asp_in":false,
"oss_upload_param_asp_out":false,
"oss_upload_param_vad_in":false,
"oss_upload_param_vad_out":false,
"oss_upload_param_kws_in":false,
"oss_upload_param_kws_susp_in":false,
"oss_upload_param_kws_out":false,
"oss_upload_param_kws_susp_out":false,
"oss_upload_param_sr_in":false
}
}
... ...
{
"device_type":"phone",
"assets_version":"1.1.20190902",
"nui_config":{
"service_mode":"kModeFullCloud",
"log_level":"kLogLevelVerbose",
"enable_recorder_by_user":true,
"enable_callback_recording":false,
"enable_dialog":false
},
"nls_config":{
"debug_level":4,
"sr_format":"opus",
"sample_rate":16000,
"dns_timeout":5000,
"vocab_default_weight":2
},
"audio_config":{
"debug":"none",
"16k_audio":{
"name":"16kmono16bit",
"id":0,
"mic":{
"name":"16kmono16bit",
"debug_heap_pollution":false,
"read_cnt":0,
"sample_rate":16000,
"bits_per_sample":16,
"channels":1,
"recording_interval":10,
"cei_frame_time_len":20,
"channel_mask":"kAMChannalMaskNone",
"format_type":"kAMDataFormatPcmInterleaved",
"endianness":"kAMByteOrderLittleEndian"
}
},
"8k_audio":{
"name":"8kmono16bit",
"id":0,
"mic":{
"name":"8kmono16bit",
"debug_heap_pollution":false,
"read_cnt":0,
"sample_rate":8000,
"bits_per_sample":16,
"channels":1,
"recording_interval":10,
"cei_frame_time_len":20,
"channel_mask":"kAMChannalMaskNone",
"format_type":"kAMDataFormatPcmInterleaved",
"endianness":"kAMByteOrderLittleEndian"
}
}
}
}
... ...
{
"device_type":"car",
"mode_type":2,
"smart_engine":3,
"encode_type":"pcm",
"debug_info":{
"debug_level":0,
"debug_path":"/sdcard/tmp"
},
"common_info":{
"pitch_level":0,
"speed_level":1.0,
"volume":1.0,
"play_audio":0,
"callback_raw_data":1,
"sample_rate":16000,
"font_name":"xiaoyun",
"wait_time":2000
},
"cache":{
"on":true,
"max_cache_size":2000000,
"save_path":"/sdcard/mit/"
},
"font":{
"on":false,
"mode":"cmd",
"save_path":"/sdcard/mit"
}
}
... ...
{"v":"5.6.10","fr":60,"ip":0,"op":90,"w":560,"h":560,"nm":"波纹","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"语音","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[280,280,0],"ix":2},"a":{"a":0,"k":[12.98,14.416,0],"ix":1},"s":{"a":0,"k":[300,300,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-0.122,-0.519],[0,0],[-4.764,-0.045],[0,0],[-1.197,4.408],[0,0],[0,0],[-0.519,-0.123],[0,0],[0.123,-0.519],[0,0],[5.457,-0.527],[0,0],[0.008,-0.042],[0.016,-0.039],[0.023,-0.035],[0.03,-0.03],[0.035,-0.023],[0.039,-0.016],[0.041,-0.009],[0.042,0],[0,0],[0.041,0.008],[0.039,0.016],[0.036,0.024],[0.03,0.03],[0.023,0.035],[0.017,0.039],[0.008,0.041],[0,0.042],[0,0],[1.37,5.161],[0,0],[0,0],[-0.52,0.122],[0,0]],"o":[[0,0],[1.08,4.582],[0,0],[4.646,-0.044],[0,0],[0,0],[0.124,-0.519],[0,0],[0.519,0.123],[0,0],[-1.272,5.357],[0,0],[0,0.042],[-0.009,0.041],[-0.016,0.039],[-0.024,0.035],[-0.029,0.03],[-0.035,0.024],[-0.039,0.016],[-0.042,0.008],[0,0],[-0.042,0],[-0.042,-0.009],[-0.039,-0.016],[-0.035,-0.023],[-0.03,-0.03],[-0.024,-0.035],[-0.016,-0.04],[-0.009,-0.042],[0,0],[-5.308,-0.547],[0,0],[0,0],[-0.122,-0.519],[0,0],[0.52,-0.122]],"v":[[-10.101,0.773],[-10.027,1.085],[-0.095,9.016],[0.098,9.016],[9.945,1.421],[10.026,1.102],[10.1,0.79],[11.263,0.072],[11.89,0.221],[12.607,1.385],[12.532,1.697],[1.248,11.531],[1.248,13.523],[1.236,13.648],[1.199,13.768],[1.14,13.88],[1.059,13.977],[0.962,14.057],[0.851,14.118],[0.73,14.154],[0.604,14.167],[-0.684,14.167],[-0.809,14.154],[-0.93,14.118],[-1.042,14.057],[-1.139,13.977],[-1.219,13.88],[-1.279,13.77],[-1.315,13.648],[-1.328,13.523],[-1.328,11.523],[-12.446,2.027],[-12.534,1.676],[-12.608,1.363],[-11.889,0.202],[-11.263,0.053]],"c":true},"ix":2},"nm":"路径 1","mn":"ADBE Vector Shape - Group","hd":false},{"ind":1,"ty":"sh","ix":2,"ks":{"a":0,"k":{"i":[[0,0],[2.845,0],[0,-2.845],[0,0],[-2.846,0],[0,2.845]],"o":[[0,-2.845],[-2.846,0],[0,0],[0,2.845],[2.845,0],[0,0]],"v":[[5.112,-6.439],[-0.039,-11.59],[-5.191,-6.439],[-5.191,-1.288],[-0.039,3.865],[5.112,-1.288]],"c":true},"ix":2},"nm":"路径 2","mn":"ADBE Vector Shape - Group","hd":false},{"ind":2,"ty":"sh","ix":3,"ks":{"a":0,"k":{"i":[[0,0],[-4.268,0],[0,-4.268],[0,0],[4.268,0],[0,4.267]],"o":[[0,-4.268],[4.268,0],[0,0],[0,4.267],[-4.268,0],[0,0]],"v":[[-7.767,-6.439],[-0.04,-14.167],[7.687,-6.439],[7.687,-1.288],[-0.04,6.439],[-7.767,-1.288]],"c":true},"ix":2},"nm":"路径 3","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"mm","mm":1,"nm":"合并路径 1","mn":"ADBE Vector Filter - Merge","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"填充 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[12.98,14.416],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"变换"}],"nm":"组 1","np":5,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":90,"st":0,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":"按钮","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[280,280,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.833,0.833],"y":[0.833,0.833]},"o":{"x":[0.167,0.167],"y":[0.167,0.167]},"t":0,"s":[210,210]},{"i":{"x":[0.833,0.833],"y":[0.833,0.833]},"o":{"x":[0.167,0.167],"y":[0.167,0.167]},"t":10,"s":[210,210]},{"i":{"x":[0.833,0.833],"y":[0.833,0.833]},"o":{"x":[0.167,0.167],"y":[0.167,0.167]},"t":20,"s":[210,210]},{"t":30,"s":[210,210]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"椭圆路径 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.929411764706,0.156862745098,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"填充 1","mn":"ADBE Vector Graphic - Fill","hd":false}],"ip":0,"op":90,"st":0,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":"按钮 2","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[280,280,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.833,0.833],"y":[0.833,0.833]},"o":{"x":[0.167,0.167],"y":[0.167,0.167]},"t":0,"s":[210,210]},{"i":{"x":[0.833,0.833],"y":[0.833,0.833]},"o":{"x":[0.167,0.167],"y":[0.167,0.167]},"t":10,"s":[210,210]},{"i":{"x":[0.833,0.833],"y":[0.833,0.833]},"o":{"x":[0.167,0.167],"y":[0.167,0.167]},"t":20,"s":[210,210]},{"i":{"x":[0.667,0.667],"y":[1,1]},"o":{"x":[0.167,0.167],"y":[0,0]},"t":30,"s":[210,210]},{"i":{"x":[0.667,0.667],"y":[1,1]},"o":{"x":[0.333,0.333],"y":[0,0]},"t":90,"s":[210,210]},{"t":130,"s":[300,300]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"椭圆路径 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":0,"s":[0.929411764706,0.156862745098,0,1]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":90,"s":[1,0.683882236481,0.597104787827,1]},{"t":130,"s":[1,0.800000011921,0.745098054409,1]}],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"填充 1","mn":"ADBE Vector Graphic - Fill","hd":false}],"ip":0,"op":90,"st":0,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":"扩散圈","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.908],"y":[0]},"t":234,"s":[100]},{"t":240,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[280,280,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.59,0.59],"y":[1.003,1.003]},"o":{"x":[0.543,0.543],"y":[0,0]},"t":15,"s":[210,210]},{"i":{"x":[0.833,0.833],"y":[1,1]},"o":{"x":[0.167,0.167],"y":[0,0]},"t":90,"s":[300,300]},{"t":130,"s":[390,390]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"椭圆路径 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":90,"s":[1,0.798773705959,0.745113372803,1]},{"t":130,"s":[0.992156863213,0.882352948189,0.858823537827,1]}],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"填充 1","mn":"ADBE Vector Graphic - Fill","hd":false}],"ip":0,"op":90,"st":0,"bm":0},{"ddd":0,"ind":5,"ty":4,"nm":"扩散圈 2","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.908],"y":[0]},"t":234,"s":[100]},{"t":240,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[280,280,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.566,0.566],"y":[0.957,0.957]},"o":{"x":[0.511,0.511],"y":[0.032,0.032]},"t":30,"s":[214.752,214.752]},{"i":{"x":[0.833,0.833],"y":[1,1]},"o":{"x":[0.167,0.167],"y":[0,0]},"t":90,"s":[390,390]},{"t":130,"s":[480,480]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"椭圆路径 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":90,"s":[0.992432598039,0.884142348346,0.860077861711,1]},{"t":130,"s":[1,0.945098042488,0.933333337307,1]}],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"填充 1","mn":"ADBE Vector Graphic - Fill","hd":false}],"ip":30,"op":90,"st":-30,"bm":0},{"ddd":0,"ind":6,"ty":4,"nm":"扩散圈 3","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.566],"y":[1]},"o":{"x":[0.198],"y":[0]},"t":90,"s":[100]},{"i":{"x":[0.847],"y":[1]},"o":{"x":[0.362],"y":[0]},"t":130,"s":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.908],"y":[0]},"t":235,"s":[100]},{"t":241,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[280,280,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.587,0.587],"y":[0.981,0.981]},"o":{"x":[0.526,0.526],"y":[0.103,0.103]},"t":46,"s":[233.126,233.126]},{"i":{"x":[0.833,0.833],"y":[1,1]},"o":{"x":[0.167,0.167],"y":[0,0]},"t":90,"s":[480,480]},{"t":130,"s":[570,570]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"椭圆路径 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0.946208160999,0.934681372549,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"填充 1","mn":"ADBE Vector Graphic - Fill","hd":false}],"ip":46,"op":90,"st":-14,"bm":0}],"markers":[]}
\ No newline at end of file
... ...
{"v":"5.6.10","fr":60,"ip":0,"op":42,"w":560,"h":560,"nm":"波纹","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"语音","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[280,280,0],"ix":2},"a":{"a":0,"k":[12.98,14.416,0],"ix":1},"s":{"a":0,"k":[300,300,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-0.122,-0.519],[0,0],[-4.764,-0.045],[0,0],[-1.197,4.408],[0,0],[0,0],[-0.519,-0.123],[0,0],[0.123,-0.519],[0,0],[5.457,-0.527],[0,0],[0.008,-0.042],[0.016,-0.039],[0.023,-0.035],[0.03,-0.03],[0.035,-0.023],[0.039,-0.016],[0.041,-0.009],[0.042,0],[0,0],[0.041,0.008],[0.039,0.016],[0.036,0.024],[0.03,0.03],[0.023,0.035],[0.017,0.039],[0.008,0.041],[0,0.042],[0,0],[1.37,5.161],[0,0],[0,0],[-0.52,0.122],[0,0]],"o":[[0,0],[1.08,4.582],[0,0],[4.646,-0.044],[0,0],[0,0],[0.124,-0.519],[0,0],[0.519,0.123],[0,0],[-1.272,5.357],[0,0],[0,0.042],[-0.009,0.041],[-0.016,0.039],[-0.024,0.035],[-0.029,0.03],[-0.035,0.024],[-0.039,0.016],[-0.042,0.008],[0,0],[-0.042,0],[-0.042,-0.009],[-0.039,-0.016],[-0.035,-0.023],[-0.03,-0.03],[-0.024,-0.035],[-0.016,-0.04],[-0.009,-0.042],[0,0],[-5.308,-0.547],[0,0],[0,0],[-0.122,-0.519],[0,0],[0.52,-0.122]],"v":[[-10.101,0.773],[-10.027,1.085],[-0.095,9.016],[0.098,9.016],[9.945,1.421],[10.026,1.102],[10.1,0.79],[11.263,0.072],[11.89,0.221],[12.607,1.385],[12.532,1.697],[1.248,11.531],[1.248,13.523],[1.236,13.648],[1.199,13.768],[1.14,13.88],[1.059,13.977],[0.962,14.057],[0.851,14.118],[0.73,14.154],[0.604,14.167],[-0.684,14.167],[-0.809,14.154],[-0.93,14.118],[-1.042,14.057],[-1.139,13.977],[-1.219,13.88],[-1.279,13.77],[-1.315,13.648],[-1.328,13.523],[-1.328,11.523],[-12.446,2.027],[-12.534,1.676],[-12.608,1.363],[-11.889,0.202],[-11.263,0.053]],"c":true},"ix":2},"nm":"路径 1","mn":"ADBE Vector Shape - Group","hd":false},{"ind":1,"ty":"sh","ix":2,"ks":{"a":0,"k":{"i":[[0,0],[2.845,0],[0,-2.845],[0,0],[-2.846,0],[0,2.845]],"o":[[0,-2.845],[-2.846,0],[0,0],[0,2.845],[2.845,0],[0,0]],"v":[[5.112,-6.439],[-0.039,-11.59],[-5.191,-6.439],[-5.191,-1.288],[-0.039,3.865],[5.112,-1.288]],"c":true},"ix":2},"nm":"路径 2","mn":"ADBE Vector Shape - Group","hd":false},{"ind":2,"ty":"sh","ix":3,"ks":{"a":0,"k":{"i":[[0,0],[-4.268,0],[0,-4.268],[0,0],[4.268,0],[0,4.267]],"o":[[0,-4.268],[4.268,0],[0,0],[0,4.267],[-4.268,0],[0,0]],"v":[[-7.767,-6.439],[-0.04,-14.167],[7.687,-6.439],[7.687,-1.288],[-0.04,6.439],[-7.767,-1.288]],"c":true},"ix":2},"nm":"路径 3","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"mm","mm":1,"nm":"合并路径 1","mn":"ADBE Vector Filter - Merge","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"填充 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[12.98,14.416],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"变换"}],"nm":"组 1","np":5,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":42,"st":-90,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":"按钮","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[280,280,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.833,0.833],"y":[0.833,0.833]},"o":{"x":[0.167,0.167],"y":[0.167,0.167]},"t":-90,"s":[210,210]},{"i":{"x":[0.833,0.833],"y":[0.833,0.833]},"o":{"x":[0.167,0.167],"y":[0.167,0.167]},"t":-80,"s":[210,210]},{"i":{"x":[0.833,0.833],"y":[0.833,0.833]},"o":{"x":[0.167,0.167],"y":[0.167,0.167]},"t":-70,"s":[210,210]},{"t":-60,"s":[210,210]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"椭圆路径 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.929411764706,0.156862745098,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"填充 1","mn":"ADBE Vector Graphic - Fill","hd":false}],"ip":0,"op":42,"st":-90,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":"按钮 2","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[280,280,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.833,0.833],"y":[0.833,0.833]},"o":{"x":[0.167,0.167],"y":[0.167,0.167]},"t":-90,"s":[210,210]},{"i":{"x":[0.833,0.833],"y":[0.833,0.833]},"o":{"x":[0.167,0.167],"y":[0.167,0.167]},"t":-80,"s":[210,210]},{"i":{"x":[0.833,0.833],"y":[0.833,0.833]},"o":{"x":[0.167,0.167],"y":[0.167,0.167]},"t":-70,"s":[210,210]},{"i":{"x":[0.667,0.667],"y":[1,1]},"o":{"x":[0.167,0.167],"y":[0,0]},"t":-60,"s":[210,210]},{"i":{"x":[0.667,0.667],"y":[1,1]},"o":{"x":[0.333,0.333],"y":[0,0]},"t":0,"s":[210,210]},{"t":40,"s":[300,300]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"椭圆路径 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":-90,"s":[0.929411764706,0.156862745098,0,1]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":0,"s":[1,0.683882236481,0.597104787827,1]},{"t":40,"s":[1,0.800000011921,0.745098054409,1]}],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"填充 1","mn":"ADBE Vector Graphic - Fill","hd":false}],"ip":0,"op":42,"st":-90,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":"扩散圈","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.908],"y":[0]},"t":144,"s":[100]},{"t":150,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[280,280,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.59,0.59],"y":[1.003,1.003]},"o":{"x":[0.543,0.543],"y":[0,0]},"t":-75,"s":[210,210]},{"i":{"x":[0.833,0.833],"y":[1,1]},"o":{"x":[0.167,0.167],"y":[0,0]},"t":0,"s":[300,300]},{"t":40,"s":[390,390]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"椭圆路径 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":0,"s":[1,0.798773705959,0.745113372803,1]},{"t":40,"s":[0.992156863213,0.882352948189,0.858823537827,1]}],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"填充 1","mn":"ADBE Vector Graphic - Fill","hd":false}],"ip":0,"op":42,"st":-90,"bm":0},{"ddd":0,"ind":5,"ty":4,"nm":"扩散圈 2","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.908],"y":[0]},"t":144,"s":[100]},{"t":150,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[280,280,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.566,0.566],"y":[0.957,0.957]},"o":{"x":[0.511,0.511],"y":[0.032,0.032]},"t":-60,"s":[214.752,214.752]},{"i":{"x":[0.833,0.833],"y":[1,1]},"o":{"x":[0.167,0.167],"y":[0,0]},"t":0,"s":[390,390]},{"t":40,"s":[480,480]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"椭圆路径 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":0,"s":[0.992432598039,0.884142348346,0.860077861711,1]},{"t":40,"s":[1,0.945098042488,0.933333337307,1]}],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"填充 1","mn":"ADBE Vector Graphic - Fill","hd":false}],"ip":0,"op":42,"st":-120,"bm":0},{"ddd":0,"ind":6,"ty":4,"nm":"扩散圈 3","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.566],"y":[1]},"o":{"x":[0.198],"y":[0]},"t":0,"s":[100]},{"i":{"x":[0.847],"y":[1]},"o":{"x":[0.362],"y":[0]},"t":40,"s":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.908],"y":[0]},"t":145,"s":[100]},{"t":151,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[280,280,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.587,0.587],"y":[0.981,0.981]},"o":{"x":[0.526,0.526],"y":[0.103,0.103]},"t":-44,"s":[233.126,233.126]},{"i":{"x":[0.833,0.833],"y":[1,1]},"o":{"x":[0.167,0.167],"y":[0,0]},"t":0,"s":[480,480]},{"t":40,"s":[570,570]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"椭圆路径 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0.946208160999,0.934681372549,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"填充 1","mn":"ADBE Vector Graphic - Fill","hd":false}],"ip":0,"op":42,"st":-104,"bm":0}],"markers":[]}
\ No newline at end of file
... ...
{
"cei":{
"cei_param_version":"2.2.0",
"cei_param_device_type":"car",
"cei_param_reco_mode":0,
"cei_param_log_level":2,
"cei_param_debug_path":"asr_debug",
"cei_param_is_debug_enable":false,
"cei_param_resource_path":"",
"cei_param_resource_is_multi_language":false,
"cei_param_audio_format_str":"16k16bitmono",
"cei_param_mcs_mode":0,
"cei_param_work_mode":0,
"cei_param_max_cache_frames":1000,
"cei_param_is_aec_bf_active":false,
"cei_param_is_agc_active":false,
"cei_param_is_vad_active":true,
"cei_param_is_kws_active":true,
"cei_param_is_sr_active":true
},
"asp":{
"asp_param_is_process_parallel":false,
"asp_param_is_input_debug_enable":false,
"asp_param_is_output_debug_enable":false,
"asp_param_debug_path":"asr_debug",
"asp_param_is_callback_enable":false,
"asp_param_callback_period_frames":5
},
"vad":{
"vad_param_is_input_debug_enable":false,
"vad_param_is_output_debug_enable":false,
"vad_param_debug_path":"asr_debug",
"vad_param_asleep_speech_noise_thres":-0.8,
"vad_param_awake_speech_noise_thres":-0.5,
"vad_param_asleep_max_speech_segment_time":300000,
"vad_param_awake_max_speech_segment_time":60000,
"vad_param_asleep_block_size":3,
"vad_param_awake_block_size":3,
"vad_param_front_timeout_interval":8000,
"vad_param_tail_timeout_interval":800,
"vad_param_is_detect_start":true,
"vad_param_is_detect_end":true
},
"kws":{
"kws_param_is_input_debug_enable":false,
"kws_param_is_output_debug_enable":false,
"kws_param_debug_path":"asr_debug",
"kws_param_is_process_parallel":false,
"kws_param_front_extend_frames":10,
"kws_param_tail_extend_frames":5,
"kws_param_encoder_type_str":"opu",
"kws_param_encoder_bitrate":16000,
"kws_param_encoder_complexity":2,
"kws_param_callback_period_ms":100,
"kws_param_max_frames_per_callback":25,
"kws_param_max_bytes_per_callback":16000
},
"sr":{
"sr_param_is_input_debug_enable":false,
"sr_param_is_output_debug_enable":false,
"sr_param_debug_path":"asr_debug",
"sr_param_is_itn_enable":true,
"sr_param_is_do_conf_filter":false,
"sr_param_is_process_parallel":true,
"sr_param_is_need_result":false,
"sr_param_is_need_voice":true,
"sr_param_ngram_conf_thres":65.0,
"sr_param_jsgf_conf_thres":65.0,
"sr_param_encoder_type_str":"opu",
"sr_param_encoder_bitrate":16000,
"sr_param_encoder_complexity":2,
"sr_param_callback_period_ms":100,
"sr_param_max_frames_per_callback":25,
"sr_param_max_bytes_per_callback":16000
},
"ou":{
"oss_upload_param_is_enable":false,
"oss_upload_param_asp_in":false,
"oss_upload_param_asp_out":false,
"oss_upload_param_vad_in":false,
"oss_upload_param_vad_out":false,
"oss_upload_param_kws_in":false,
"oss_upload_param_kws_susp_in":false,
"oss_upload_param_kws_out":false,
"oss_upload_param_kws_susp_out":false,
"oss_upload_param_sr_in":false
}
}
... ...
{
"device_type":"phone",
"assets_version":"1.1.20190902",
"nui_config":{
"service_mode":"kModeFullCloud",
"log_level":"kLogLevelVerbose",
"enable_recorder_by_user":true,
"enable_callback_recording":false,
"enable_dialog":false
},
"nls_config":{
"debug_level":4,
"sr_format":"opus",
"sample_rate":16000,
"dns_timeout":5000,
"vocab_default_weight":2
},
"audio_config":{
"debug":"none",
"16k_audio":{
"name":"16kmono16bit",
"id":0,
"mic":{
"name":"16kmono16bit",
"debug_heap_pollution":false,
"read_cnt":0,
"sample_rate":16000,
"bits_per_sample":16,
"channels":1,
"recording_interval":10,
"cei_frame_time_len":20,
"channel_mask":"kAMChannalMaskNone",
"format_type":"kAMDataFormatPcmInterleaved",
"endianness":"kAMByteOrderLittleEndian"
}
},
"8k_audio":{
"name":"8kmono16bit",
"id":0,
"mic":{
"name":"8kmono16bit",
"debug_heap_pollution":false,
"read_cnt":0,
"sample_rate":8000,
"bits_per_sample":16,
"channels":1,
"recording_interval":10,
"cei_frame_time_len":20,
"channel_mask":"kAMChannalMaskNone",
"format_type":"kAMDataFormatPcmInterleaved",
"endianness":"kAMByteOrderLittleEndian"
}
}
}
}
... ...
{
"device_type":"car",
"mode_type":2,
"smart_engine":3,
"encode_type":"pcm",
"debug_info":{
"debug_level":0,
"debug_path":"/sdcard/tmp"
},
"common_info":{
"pitch_level":0,
"speed_level":1.0,
"volume":1.0,
"play_audio":0,
"callback_raw_data":1,
"sample_rate":16000,
"font_name":"xiaoyun",
"wait_time":2000
},
"cache":{
"on":true,
"max_cache_size":2000000,
"save_path":"/sdcard/mit/"
},
"font":{
"on":false,
"mode":"cmd",
"save_path":"/sdcard/mit"
}
}
... ...