花瓣小跑 发表于 2025-4-9 07:05:44

HarmonyOS Next->鸿蒙修仙传之我要录音 -> AvRecorder

结果图:https://i-blog.csdnimg.cn/direct/2b7420c4c7244b96b37666f6d0aadc6e.png 

https://i-blog.csdnimg.cn/direct/368b24c35b074e02a7e6f71717520f5c.gif
要想录音,得开启录音权限副本:

       1. 在module.json5里配置麦克风权限:
"requestPermissions": [
      {
      "name": "ohos.permission.MICROPHONE",
      "usedScene": {},
      "reason": "$string:MICROPHONE_use_reason"
      }
]         2. 麦克风权限是用户级权限,必要向用户申请,可以用上篇封装的请求权限工具发起申请。
开启/关闭录音要借助音频工具:AVRecorder 

/**
* 开始录音
*/
async startRecord() {
    // 创建音频接收对象
    const avRecorder = await media.createAVRecorder()
    const ctx = getContext(this) // 获取上下文
    const filePath = ctx.filesDir + '/' + Date.now() + '.m4a' // 创建文件路径
    const file = fileIo.openSync(filePath, fileIo.OpenMode.CREATE | fileIo.OpenMode.READ_WRITE) // 打开或创建文件
    this.fd = file.fd // 存到全局
    // 创建音频配置
    const avConfig: media.AVRecorderConfig = {
      audioSourceType: media.AudioSourceType.AUDIO_SOURCE_TYPE_MIC, // 音频输入源设置为麦克风
      profile: {
      audioBitrate: 100000, // 音频比特率
      audioChannels: 2, // 音频声道数
      audioCodec: media.CodecMimeType.AUDIO_AAC, // 音频编码格式,当前支持ACC,MP3,G711MU
      audioSampleRate: 48000, // 音频采样率
      fileFormat: media.ContainerFormatType.CFT_MPEG_4A, // 封装格式,当前支持MP4,M4A,MP3,WAV
      },
      url: `fd://${file.fd}`, // 参考应用文件访问与管理中的开发示例获取创建的音频文件fd填入此处
    }
    // 准备录音
    await avRecorder.prepare(avConfig)
    // 开始录音
    await avRecorder.start()
    // 把音频对象存到全局存到全局
    this.avRecorder = avRecorder

    // 持续监听声音的振幅
    this.timerId = setInterval(async () => {
      const res = await avRecorder.getAudioCapturerMaxAmplitude() // 获取声音振幅>数字
      this.maxAmplitude = res // 赋值到全局
    }, 100)
}
/**
* 结束录音
*/
async stopRecord() {
    if (this.avRecorder) {
      await this.avRecorder.stop() // 停止录音
      await this.avRecorder.release() // 释放资源
      fileIo.closeSync(this.fd) // 关闭文件
      clearInterval(this.timerId) // 清除定时器
      this.maxAmplitude = 0 // 重置振幅
    }
} 绑定对应的变乱分别调用录音和结束录音 
 振幅拿到了,现在要给它动态展示出来:

        循环30个柱子,高度参差不齐,随着振幅变 -> 监听振幅,盘算高度
// AudioBoComp组件

@Component
export struct AudioBoComp {
@Prop @Watch('onChange') maxAmplitude: number // 振幅
@State per: number = 0 // 高
onChange() {
    // 过渡
    animateTo({ duration: 100 }, () => {
      if (this.maxAmplitude < 500) { // 500以下高度为零
      this.per = 0
      } else if (this.maxAmplitude > 30000) { // 30000以上高度为1
      this.per = 1
      } else { // 高度为振幅比最大高
      this.per = this.maxAmplitude / 30000
      }
    })
}

build() {
    Row({ space: 5 }) {
      // 循环30个柱子
      ForEach(Array.from({ length: 30 }), () => {
      Column()
          .layoutWeight(1)
          .height(this.per * 100 * Math.random()) // 高度按振幅 随机 这样才参差不齐
          .backgroundColor('#ff08a9be')
      })
    }
    .width('100%')
    .height(100)
}
} 我们把它封装成一个组件吧,以后还能直接用 
<strong>AudioBoComp({maxAmplitude: this.maxAmplitude})把振幅穿过去</strong> 整体代码:

  录音页:

import { Permissions } from '@kit.AbilityKit';
import { permission } from '../utils'; // 上篇封装的权限工具
import { promptAction, router } from '@kit.ArkUI';
import { media } from '@kit.MediaKit';
import { fileIo } from '@kit.CoreFileKit';
import { AudioBoComp } from '../components'; // 波动组件

@Entry
@Component
struct AudioPage {
// 1. 权限列表
permissions: Permissions[] = ['ohos.permission.MICROPHONE']
confirmConfig:promptAction.ShowDialogOptions = {
    title: "温馨提示",
    message: "未授权使用麦克风将无法使用该面试录音功能,是否前往设置进行授权?",
    buttons: [
      { text: '离开', color: '#ccc' },
      { text: '去授权', color: $r('app.color.black') }
    ]
} // 弹窗配置

// 2. 音频接收对象
avRecorder?: media.AVRecorder
// 3. 写入的文件fd
fd?: number
// 4. 记录声音的振幅
@State maxAmplitude: number = 0
timerId = 0 // 定时器id

async aboutToAppear() {
    // 进入改页面时,获取权限
    this.getPermission()
}

/**
   * 获取麦克风权限
   */
async getPermission(){
    try {
      // 1. 第一次拉起授权
      const isOk1 = await permission.requestPermission(this.permissions)
      if(isOk1) return
      // 2. 弹窗再次确认
      const res = await promptAction.showDialog(this.confirmConfig)
      if(res.index === 1){
      // 3. 二次授权
      const isOk2 = await permission.permissionSetting(this.permissions)
      if(isOk2) return
      }
      // 授权都不通过,退出该页面
      router.back()
    } catch (e) {
      promptAction.showToast({message: '用户授权出现问题'})
      router.back()
    }
}

/**
   * 开始录音
   */
async startRecord(){
    // 创建音频接收对象
    const avRecorder = await media.createAVRecorder()
    const ctx = getContext(this) // 获取上下文
    const filePath = ctx.filesDir + '/' + Date.now() + '.m4a' // 创建文件路径
    const file = fileIo.openSync(filePath, fileIo.OpenMode.CREATE | fileIo.OpenMode.READ_WRITE) // 打开或创建文件
    this.fd = file.fd // 存到全局
    // 创建音频配置
    const avConfig: media.AVRecorderConfig = {
      audioSourceType: media.AudioSourceType.AUDIO_SOURCE_TYPE_MIC, // 音频输入源设置为麦克风
      profile: {
      audioBitrate: 100000, // 音频比特率
      audioChannels: 2, // 音频声道数
      audioCodec: media.CodecMimeType.AUDIO_AAC, // 音频编码格式,当前支持ACC,MP3,G711MU
      audioSampleRate: 48000, // 音频采样率
      fileFormat: media.ContainerFormatType.CFT_MPEG_4A, // 封装格式,当前支持MP4,M4A,MP3,WAV
      },
      url: `fd://${file.fd}`, // 参考应用文件访问与管理中的开发示例获取创建的音频文件fd填入此处
    }
    // 准备录音
    await avRecorder.prepare(avConfig)
    // 开始录音
    await avRecorder.start()
    // 把音频对象存到全局存到全局
    this.avRecorder = avRecorder

    // 持续监听声音的振幅
    this.timerId = setInterval(async ()=>{
      const res = await avRecorder.getAudioCapturerMaxAmplitude() // 获取声音振幅
      this.maxAmplitude = res // 赋值到全局
    }, 100)
}
/**
   * 结束录音
   */
async stopRecord(){
    if (this.avRecorder) {
      await this.avRecorder.stop() // 停止录音
      await this.avRecorder.release() // 释放资源
      fileIo.closeSync(this.fd) // 关闭文件
      clearInterval(this.timerId) // 清除定时器
      this.maxAmplitude = 0 // 重置振幅
    }
}

build() {
    Column({space: 5}){
      AudioBoComp({maxAmplitude: this.maxAmplitude})
      Button('开始录音')
      .onClick(()=>{
          this.startRecord()
      })
      Button('结束录音')
      .onClick(()=>{
          this.stopRecord()
      })
    }
    .justifyContent(FlexAlign.Center)
    .height('100%')
    .width('100%')
}
} 振幅组件: 

@Component
export struct AudioBoComp {
@Prop @Watch('onChange') maxAmplitude: number // 振幅
@State per: number = 0 // 高
onChange() {
    // 过渡
    animateTo({ duration: 100 }, () => {
      if (this.maxAmplitude < 500) { // 500以下高度为零
      this.per = 0
      } else if (this.maxAmplitude > 30000) { // 30000以上高度为1
      this.per = 1
      } else { // 高度为振幅比最大高
      this.per = this.maxAmplitude / 30000
      }
    })
}

build() {
    Row({ space: 5 }) {
      // 循环30个柱子
      ForEach(Array.from({ length: 30 }), () => {
      Column()
          .layoutWeight(1)
          .height(this.per * 100 * Math.random()) // 高度按振幅 随机 这样才参差不齐
          .backgroundColor('#ff08a9be')
      })
    }
    .width('100%')
    .height(100)
}
}    权限工具:

import { abilityAccessCtrl, Permissions } from '@kit.AbilityKit'

class Permission {
// 申请权限
async requestPermission(permissions: Permissions[]) {
    // 1. 创建权限管理对象
    const atManager = abilityAccessCtrl.createAtManager()
    // 2. 弹窗授权窗口
    const ctx = AppStorage.get<Context>('context')
    if (ctx) {
      const res =await atManager.requestPermissionsFromUser(ctx, permissions)
      // 3. 判断用户是否授权
      const flag = res.authResults.every(item => item === abilityAccessCtrl.GrantStatus.PERMISSION_GRANTED )
      return flag
    } else {
      return false
    }
}

// 二次申请
async permissionSetting(permissions: Permissions[]) {
    const atManager = abilityAccessCtrl.createAtManager()
    const ctx = AppStorage.get<Context>('context')
    if (ctx) {
      const res = await atManager.requestPermissionOnSetting(ctx, permissions)
      const flag = res.every(item => item === abilityAccessCtrl.GrantStatus.PERMISSION_GRANTED)
      return flag
    }
    return false
}
}

export const permission = new Permission() 天选之人

        https://i-blog.csdnimg.cn/direct/a554fb07f8644d709a76d1b6f21ee08e.png


免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
页: [1]
查看完整版本: HarmonyOS Next->鸿蒙修仙传之我要录音 -> AvRecorder