鸿蒙中的录音2

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 = '晚上好';
    }
  }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值