import { abilityAccessCtrl, bundleManager, common, Permissions } from '@kit.AbilityKit' import { AudioCapturer } from '../util/audio_recorder' import { FileCommon } from '../util/FileCommon' import { VoiceTransfer } from '../util/Speech' import { emitter } from '@kit.BasicServicesKit' import router from '@ohos.router'; import curves from '@ohos.curves'; interface KeywordAction { keyword: string; action: () => void; } @Extend(Text) function fancy() { .width(260) .height(50) .backgroundColor('#d3ba84') .fontSize(25) .fontColor('#fff') .margin({ top: 20 }) .textAlign(TextAlign.Center) .borderRadius({ topLeft: 30, topRight: 5, bottomLeft: 5, bottomRight: 30 }) } export enum VoiceRecordEnum { Cancel, // 取消录制 RecordIng, // 录制中 Transfer // 语音转化 } @Entry @Component struct IndexcPage { @State voiceState: VoiceRecordEnum = VoiceRecordEnum.RecordIng // 当前的状态 //存储临时路径的 tempAudioPath: string = "" // 临时录音变量 @State showVoiceCom: boolean = false // 是否显示发送语音组件 @State currentMessage: string = '' @State list: number[] = [] @State str: string = "" @State toggle: boolean = true @State isupWindow: boolean = false @State titleArray: string[] = ['查余额', '我要转账', '我要存款', '我要理财', '账户挂失', '人工客服'] @State VoiceText: string[] = [ '好的,您要办理账户,正在为您跳转', '好的,您要办理转账,正在为您跳转', '好的,您要办理存款,正在为您跳转', '好的,您要办理理财,正在为您跳转', '好的,您要办理账户挂失,正在为您跳转', '好的,正在为您跳转', '抱歉,暂未识别您说的业务' ] @State isImage: boolean = true @State i: number = 0 @State bool: boolean = true @State bool2: boolean = false @State greetingText: string = ''; private timer: number | null = null // 示例关键字和操作 private keywords: KeywordAction[] = [ { keyword: '余额', action: () => { VoiceTransfer.textToVoice(this.VoiceText[0]) this.currentMessage = this.VoiceText[0] } }, { keyword: '转账', action: () => { VoiceTransfer.textToVoice(this.VoiceText[1]) this.currentMessage = this.VoiceText[1] } }, { keyword: '存款', action: () => { VoiceTransfer.textToVoice(this.VoiceText[2]) this.currentMessage = this.VoiceText[2] } }, { keyword: '理财', action: () => { VoiceTransfer.textToVoice(this.VoiceText[3]) this.currentMessage = this.VoiceText[3] } }, { keyword: '挂失', action: () => { VoiceTransfer.textToVoice(this.VoiceText[4]) this.currentMessage = this.VoiceText[4] } }, { keyword: '客服', action: () => { VoiceTransfer.textToVoice(this.VoiceText[5]) this.currentMessage = this.VoiceText[5] } } ]; // 检查权限 private effect1: object = TransitionEffect.OPACITY // 创建了透明度转场效果,这里没有调用animation接口,会跟随animateTo的动画参数 // 添加平移转场效果,动画参数会跟随其之上带animation的TransitionEffect,也就是springMotion(0.6, 1.2) .combine(TransitionEffect.translate({ x: -500 }).animation({ curve: curves.springMotion(0.6, 1.2), delay: 100 })) private effect2: object = TransitionEffect.OPACITY // 创建了透明度转场效果,这里没有调用animation接口,会跟随animateTo的动画参数 // 添加平移转场效果,动画参数会跟随其之上带animation的TransitionEffect,也就是springMotion(0.6, 1.2) .combine(TransitionEffect.translate({ x: -500 }).animation({ curve: curves.springMotion(0.6, 1.2), delay: 300 })) private effect3: object = TransitionEffect.OPACITY // 创建了透明度转场效果,这里没有调用animation接口,会跟随animateTo的动画参数 // 添加平移转场效果,动画参数会跟随其之上带animation的TransitionEffect,也就是springMotion(0.6, 1.2) .combine(TransitionEffect.translate({ x: -500 }).animation({ curve: curves.springMotion(0.6, 1.2), delay: 500 })) // @Builder // VoiceState() { // Column() { // Row({ space: 2 }) { // ForEach(this.list, (v: number) => { // Row() // .height(v) // .width(2) // .borderRadius(1) // .backgroundColor(Color.Orange) // }) // } // .justifyContent(FlexAlign.Center) // .height(80) // .width(180) // .borderRadius(20) // .backgroundColor(Color.Green) // } // .backgroundColor(Color.White) // .width('100%') // .height(200) // } private debouncedCheckForKeywords = this.debounce(() => { this.checkForKeywords(this.currentMessage, this.keywords); }, 500); // 设置防抖时间为 500 毫秒 private debouncedTextToVoice = this.debounce(() => { VoiceTransfer.textToVoice(this.currentMessage) }, 1000); // 设置防抖时间为 1000 毫秒 private debouncedUnidentification = this.debounce(() => { this.currentMessage = this.VoiceText[6] VoiceTransfer.textToVoice(this.VoiceText[6]) }, 1000); // 设置防抖时间为 1000 毫秒 aboutToAppear(): void { this.time() this.requestPermissions(["ohos.permission.MICROPHONE"]) AudioCapturer.init() emitter.on("onBuffer", (res: emitter.EventData) => { this.calculateAmplitudes(res.data!["buffer"] as ArrayBuffer) }) } aboutToDisappear(): void { AudioCapturer.release() emitter.off("onBuffer") } build() { Column() { Text('X ').onClick(() => { router.back() }) .fontSize(25) .width('100%') .textAlign(TextAlign.End) Text(`${this.greetingText},您可以这样说:`) .width('90%') .fontSize(25) .margin(10) Column() { if (this.bool) { Text(this.titleArray[this.i]).transition(this.effect1).fancy() .onClick(() => { this.currentMessage = this.VoiceText[this.i] this.debouncedTextToVoice() }) Text(this.titleArray[this.i+1]).transition(this.effect2).fancy() .onClick(() => { this.currentMessage = this.VoiceText[this.i+1] this.debouncedTextToVoice() }) Text(this.titleArray[this.i+2]).transition(this.effect3).fancy() .onClick(() => { this.currentMessage = this.VoiceText[this.i+2] this.debouncedTextToVoice() }) } } .width('100%') .height(230) Text('🔄 换一换') .fontColor('#d3ba84') .fontSize(18) .margin({ top: 50, bottom: 60 }) .onClick(() => { this.bool = !this.bool if (!this.bool) { this.timer = setTimeout(() => { this.bool = true }, 200) } if (this.bool2) { this.i = 1 this.bool2 = !this.bool2 return } else { this.i = 3 this.bool2 = !this.bool2 return } }) this.getTextContent() Text('点击说话').fontSize(14) Column() { Image($r('app.media.yuyin')) .width(60) .margin({ top: 8 }) .onClick(async () => { this.isImage = true this.isupWindow = true //先检查权限 // await this.checkPermission() //开始录音 // this.showVoiceCom = true // 长按打开 this.beginCollectVoice() this.voiceState = VoiceRecordEnum.RecordIng // 5秒后自动识别 this.timer = setTimeout(() => { this.isImage = false this.stopCollectVoice() }, 5000) setTimeout(() => { this.isupWindow = false // 清除定时器 if (this.timer !== null) { clearTimeout(this.timer); this.timer = null; // 确保定时器引用被清空 } }, 9000) }) } .width(80) .height(80) .borderRadius(80) .border({ width: 1, color: '#d3ba84' }) .margin(10) if (this.isupWindow) { this.upWindow() } // Button("转文本", { type: ButtonType.Normal }) // .fontColor(Color.Green) // .margin({ top: 30 }) // .height(50) // .backgroundColor(Color.White) // .borderRadius(2) // .gesture(LongPressGesture() // .onAction(async () => { // //先检查权限 // // await this.checkPermission() // //开始录音 // this.showVoiceCom = true // 长按打开 // this.voiceState = VoiceRecordEnum.RecordIng // }) // .onActionEnd(async () => { // //停止录音收集 // this.stopCollectVoice() // this.showVoiceCom = false // 松手关闭 // //录音转文字 // this.voiceState = VoiceRecordEnum.Transfer // VoiceTransfer.VoiceToText(this.tempAudioPath, (res) => { // this.currentMessage = res.result // }) // })) } .height('100%') .width('100%') // .bindSheet($$this.showVoiceCom, this.VoiceState(), { height: 300, backgroundColor: Color.Gray }) } // 获取文本消息 @Builder getTextContent() { Text(this.currentMessage) .width('90%') .padding(10) .fontSize(22) .lineHeight(24) .textAlign(TextAlign.Center) .margin({ left: 10, right: 10, top: 100 }) .borderRadius(5) } // async checkPermission() { // try { // const manager = abilityAccessCtrl.createAtManager() // 创建程序控制管理器 // // 获取应用信息 // const buildInfo = // bundleManager.getBundleInfoForSelfSync(bundleManager.BundleFlag.GET_BUNDLE_INFO_WITH_APPLICATION) // const status = manager.checkAccessTokenSync(buildInfo.appInfo?.accessTokenId, "ohos.permission.MICROPHONE") // if (status === abilityAccessCtrl.GrantStatus.PERMISSION_DENIED) { // // 不被允许 // const context = getContext() as common.UIAbilityContext // context.startAbility({ // bundleName: 'com.huawei.hmos.settings', // abilityName: 'com.huawei.hmos.settings.MainAbility', // uri: "application_info_entry", // parameters: { // pushParams: buildInfo.name // } // }) // } else { // //收集声音 // this.beginCollectVoice() // } // } catch (error) { // // } // } @Builder upWindow() { Stack() { Column() { Column() { } .width(300) .height(250) .backgroundColor('#181818') .borderRadius(15) .onClick(() => { this.isImage = false this.stopCollectVoice() setTimeout(() => { this.isupWindow = false // 清除定时器 if (this.timer !== null) { clearTimeout(this.timer); this.timer = null; // 确保定时器引用被清空 } }, 3000) }) } .width('100%') .height('100%') .backgroundColor('#6e6e6e') .opacity(0.8) .justifyContent(FlexAlign.Center) // Row({ space: 2 }) { // ForEach(this.list, (v: number) => { // Row() // .height(v) // .width(2) // .borderRadius(1) // .backgroundColor(Color.Orange) // }) // } // .justifyContent(FlexAlign.Center) // .height(80) // .width(180) // .borderRadius(20) // .backgroundColor(Color.Green) // .offset({ top: -30 }) if (this.isImage) { Image($r(this.list[this.list.length - 1] < 11.5 ? 'app.media.img' : this.list[this.list.length - 1] >= 11.5 && this.list[this.list.length - 1] <= 13 ? 'app.media.img1' : this.list[this.list.length - 1] > 13 && this.list[this.list.length - 1] <= 14.5 ? 'app.media.img2' : this.list[this.list.length -1] > 14.5 && this.list[this.list.length - 1] <= 16 ? 'app.media.img3' : 'app.media.img4')) .width(160) .offset({ top: 10 }) } else { Text('识别中...') .fontSize(30) .fontColor('#4292ff') .offset({ top: 50 }) } Text('倾听中') .fontSize(30) .fontColor('#4292ff') .offset({ top: -90 }) // Image($r('app.media.yuyin')) // .width(60) // .offset({ top: 60 }) Text('点击屏幕关闭弹框') .fontSize(15) .fontColor('#d4ba7f') .offset({ top: 110 }) } .width('100%') .height('100%') .position({ top: 0 }) .onClick(() => { this.isImage = false this.stopCollectVoice() setTimeout(() => { this.isupWindow = false // 清除定时器 if (this.timer !== null) { clearTimeout(this.timer); this.timer = null; // 确保定时器引用被清空 } }, 3000) }) } // 开始收集声音 beginCollectVoice() { this.tempAudioPath = FileCommon.createAudioFile() AudioCapturer.start(this.tempAudioPath) // 开始录音 } //停止录音收集 stopCollectVoice() { AudioCapturer.stop() //录音转文字 this.voiceState = VoiceRecordEnum.Transfer VoiceTransfer.VoiceToText(this.tempAudioPath, (res) => { this.currentMessage = res.result this.debouncedCheckForKeywords(); }) } async requestPermissions(permissions: Permissions[]) { // 1. 创建应用权限管理器 const atManager = abilityAccessCtrl.createAtManager() // 2. 向用户申请 user_grant 权限(温馨提示:首次申请时会弹窗,后续申请则不会再出现弹窗) const requestResult = await atManager.requestPermissionsFromUser(getContext(), permissions) // 通过 every 检查权限是否都成功授权 const isAuth = requestResult.authResults.every(item => item === abilityAccessCtrl.GrantStatus.PERMISSION_GRANTED) // Promise.resolve() 返回 Promise 成功,await 后续代码,正常执行 // Promise.reject() 返回 Promise 错误,Promise.reject() 的结果可被 catch 捕获 return isAuth === true ? Promise.resolve(true) : Promise.reject(false) } // 计算波峰 calculateAmplitudes(buffer: ArrayBuffer) { try { const sampleSize = Math.floor(buffer.byteLength / 20); // 计算每个均分区间的大小 const view = new DataView(buffer); const amplitudes: number[] = []; // 遍历缓冲区,计算每个样本的振幅并转换为高度 // 遍历原始缓冲区,提取每个均分区间的振幅 for (let i = 0; i < buffer.byteLength; i += sampleSize) { let sum = 0; // 计算当前均分区间内所有振幅的平均值 for (let j = i; j < i + sampleSize; j += 2) { // 假设每个样本占据 2 字节 const sample = view.getInt16(j, true); sum += Math.abs(sample); } const averageAmplitude = 150 * sum / (sampleSize / 2) / 32767; // 假设每个样本占据 2 字节 amplitudes.push(averageAmplitude < 10 ? 10 : averageAmplitude); } animateTo({ duration: 100 }, () => { this.list = amplitudes }) } catch (error) { AlertDialog.show({ message: error.message }) } } // 防抖 private debounce(func: () => void, wait: number): (() => void) { let timeout: number | null = null; return () => { if (timeout !== null) { clearTimeout(timeout); } timeout = setTimeout(func, wait); }; } // 检查关键字并执行相应操作的函数 private checkForKeywords(text: string, keywords: KeywordAction[]): void { let foundKeyword = false; for (const keywordAction of keywords) { const keyword = keywordAction.keyword; const action = keywordAction.action; if (text.includes(keyword)) { action(); foundKeyword = true; break; // 执行第一个匹配的关键字操作后退出循环 } } if (!foundKeyword) { this.debouncedUnidentification(); } } private time() { // 获取当前时间并判断时间段 const currentTime = new Date().getHours(); if (currentTime >= 5 && currentTime < 12) { this.greetingText = '早上好'; } else if (currentTime >= 12 && currentTime < 18) { this.greetingText = '下午好'; } else if (currentTime >= 18 && currentTime < 22) { this.greetingText = '晚上好'; } else { this.greetingText = '晚上好'; } } }
鸿蒙中的录音2
最新推荐文章于 2024-10-01 14:53:57 发布