更新记录

1.0.1(2026-07-15)

优化

  • 完善 uni-app x 与 uni-app Vue3 使用说明。
  • 补充完整 API 签名、公共类型、事件结果、错误类型和后台录音配置说明。

1.0.0(2026-07-15)

新增

  • 支持 uni-app x · HarmonyOS 和 uni-app Vue3 · HarmonyOS。
  • 提供 createHarmonyRecorder(options) 录音实例 API。
  • 支持开始、暂停、恢复、停止、销毁和状态查询。
  • 支持 PCM、WAV 文件输出和实际生效配置查询。
  • 支持实时 PCM ArrayBuffer 分片回调及丢弃统计。
  • 支持 peak、RMS、dBFS 振幅回调。
  • 支持录音状态、输入设备变化、音频中断和错误事件。
  • 支持麦克风权限申请和可选后台录音长时任务。
  • 提供统一错误码和可控插件日志。

平台兼容性

uni-app(5.15)

Vue2 Vue3 Chrome Safari app-vue app-nvue Android iOS 鸿蒙
- - - - -
微信小程序 支付宝小程序 抖音小程序 百度小程序 快手小程序 京东小程序 鸿蒙元服务 QQ小程序 飞书小程序 小红书小程序 快应用-华为 快应用-联盟
- - - - - - - - - - - -

uni-app x(5.15)

Chrome Safari Android iOS 鸿蒙 微信小程序
- - - - - -

hans-hm-recorder

HarmonyOS 原生录音 UTS 插件,支持 WAV / PCM 文件、实时 PCM、振幅、输入设备变化、录音中断和后台录音。

支持范围

项目类型 平台 支持情况
uni-app Vue3 · App HarmonyOS
uni-app x App HarmonyOS
uni-app / uni-app x Android、iOS、Web

环境要求:

  • HBuilderX 4.61+
  • HarmonyOS 5.0+

安装

将插件放到项目的 uni_modules 目录:

uni_modules/hans-hm-recorder/

页面必须从插件根目录导入,不要直接导入 utssdk 内部文件。

导入

uni-app x

.uvue<script setup lang="uts"> 中可以同时导入函数和类型:

import {
  createHarmonyRecorder,
  setHarmonyRecorderLogEnabled,
  HarmonyRecorder,
  HarmonyRecorderOptions,
  HarmonyRecorderActualConfig,
  HarmonyRecorderStopResult,
  HarmonyRecorderState,
  HarmonyRecorderOutputFormat,
  HarmonyRecorderSourceType,
  HarmonyRecorderSampleFormat,
  HarmonyRecorderStateResult,
  HarmonyRecorderDataResult,
  HarmonyRecorderAmplitudeResult,
  HarmonyRecorderDeviceResult,
  HarmonyRecorderInterruptionResult,
  HarmonyRecorderErrorCode,
  HarmonyRecorderFail,
  HarmonyRecorderStateCallback,
  HarmonyRecorderDataCallback,
  HarmonyRecorderAmplitudeCallback,
  HarmonyRecorderDeviceCallback,
  HarmonyRecorderInterruptionCallback,
  HarmonyRecorderErrorCallback,
  CreateHarmonyRecorder,
  SetHarmonyRecorderLogEnabled,
} from '@/uni_modules/hans-hm-recorder'

uni-app Vue3

普通 JavaScript 页面只需导入运行时 API:

import {
  createHarmonyRecorder,
  setHarmonyRecorderLogEnabled
} from '@/uni_modules/hans-hm-recorder'

快速开始

uni-app x 完整示例

<script setup lang="uts">
import {
  createHarmonyRecorder,
  setHarmonyRecorderLogEnabled,
  HarmonyRecorder,
  HarmonyRecorderOptions,
  HarmonyRecorderStateResult,
  HarmonyRecorderDataResult,
  HarmonyRecorderAmplitudeResult,
  HarmonyRecorderDeviceResult,
  HarmonyRecorderInterruptionResult,
  HarmonyRecorderStopResult,
  HarmonyRecorderFail,
} from '@/uni_modules/hans-hm-recorder'

// 原生录音对象不要放入 reactive 或 ref。
let recorder: HarmonyRecorder | null = null

const startRecording = async () => {
  const options: HarmonyRecorderOptions = {
    sampleRate: 48000,
    channels: 1,
    sampleFormat: 's16le',
    sourceType: 'mic',
    outputFormat: 'wav',
    emitData: false,
    enableBackground: false,
  }

  const current = createHarmonyRecorder(options)
  recorder = current

  current.onStateChange((res: HarmonyRecorderStateResult) => {
    console.log('state', res.previousState, '->', res.state)
  })

  current.onData((res: HarmonyRecorderDataResult) => {
    console.log('pcm', res.sequence, res.buffer.byteLength, res.isLast)
  })

  current.onAmplitude((res: HarmonyRecorderAmplitudeResult) => {
    console.log('amplitude', res.peak, res.rms, res.dbfs)
  })

  current.onDeviceChange((res: HarmonyRecorderDeviceResult) => {
    console.log('device', res.deviceId, res.deviceType, res.deviceName)
  })

  current.onInterruption((res: HarmonyRecorderInterruptionResult) => {
    console.log('interruption', res.eventType, res.forceType, res.hintType)
  })

  current.onError((err: HarmonyRecorderFail) => {
    console.error('runtime error', err.errCode, err.errMsg, err.data)
  })

  try {
    await current.start()
  } catch (e) {
    const err = e as HarmonyRecorderFail
    console.error('start failed', err.errCode, err.errMsg, err.data)
  }
}

const pauseRecording = async () => {
  if (recorder != null) await recorder!.pause()
}

const resumeRecording = async () => {
  if (recorder != null) await recorder!.resume()
}

const stopRecording = async () => {
  if (recorder == null) return

  try {
    const result: HarmonyRecorderStopResult = await recorder!.stop()
    console.log('file', result.tempFilePath)
    console.log('duration', result.durationMs)
    console.log('bytes', result.byteLength)
    console.log('dropped', result.droppedChunkCount)
  } catch (e) {
    const err = e as HarmonyRecorderFail
    console.error('stop failed', err.errCode, err.errMsg)
  }
}

const releaseRecorder = async () => {
  if (recorder == null) return
  await recorder!.destroy()
  recorder = null
}

setHarmonyRecorderLogEnabled(false)

onUnload(() => {
  if (recorder != null) {
    recorder!.destroy()
    recorder = null
  }
})
</script>

uni-app Vue3 完整示例

<script>
import {
  createHarmonyRecorder,
  setHarmonyRecorderLogEnabled
} from '@/uni_modules/hans-hm-recorder'

// UTS 原生对象不要放入 data、reactive 或 ref。
let recorder = null

export default {
  onUnload() {
    if (recorder) {
      recorder.destroy()
      recorder = null
    }
  },
  methods: {
    async startRecording() {
      if (recorder) await recorder.destroy()

      recorder = createHarmonyRecorder({
        sampleRate: 48000,
        channels: 1,
        sampleFormat: 's16le',
        sourceType: 'mic',
        outputFormat: 'wav',
        emitData: false,
        enableBackground: false
      })

      recorder.onStateChange((res) => {
        console.log('state', res.previousState, '->', res.state)
      })

      recorder.onAmplitude((res) => {
        console.log('dbfs', res.dbfs)
      })

      recorder.onDeviceChange((res) => {
        console.log('device', res.deviceId, res.deviceType, res.deviceName)
      })

      recorder.onInterruption((res) => {
        console.log('interruption', res.eventType, res.forceType, res.hintType)
      })

      recorder.onError((err) => {
        console.error('runtime error', err.errCode, err.errMsg, err.data)
      })

      try {
        await recorder.start()
      } catch (err) {
        console.error('start failed', err.errCode, err.errMsg)
      }
    },

    async stopRecording() {
      if (!recorder) return

      try {
        const result = await recorder.stop()
        console.log(result.tempFilePath, result.durationMs, result.byteLength)
      } catch (err) {
        console.error('stop failed', err.errCode, err.errMsg)
      }
    }
  }
}
</script>

API 概览

createHarmonyRecorder(
  options?: HarmonyRecorderOptions | null
): HarmonyRecorder

setHarmonyRecorderLogEnabled(enabled: boolean): void

录音实例提供以下方法:

interface HarmonyRecorder {
  start(): Promise<void>
  pause(): Promise<void>
  resume(): Promise<void>
  stop(): Promise<HarmonyRecorderStopResult>
  destroy(): Promise<void>

  getState(): HarmonyRecorderState
  getActualConfig(): HarmonyRecorderActualConfig | null

  onStateChange(callback: HarmonyRecorderStateCallback): void
  offStateChange(callback?: HarmonyRecorderStateCallback | null): void

  onData(callback: HarmonyRecorderDataCallback): void
  offData(callback?: HarmonyRecorderDataCallback | null): void

  onAmplitude(callback: HarmonyRecorderAmplitudeCallback): void
  offAmplitude(callback?: HarmonyRecorderAmplitudeCallback | null): void

  onDeviceChange(callback: HarmonyRecorderDeviceCallback): void
  offDeviceChange(callback?: HarmonyRecorderDeviceCallback | null): void

  onInterruption(callback: HarmonyRecorderInterruptionCallback): void
  offInterruption(callback?: HarmonyRecorderInterruptionCallback | null): void

  onError(callback: HarmonyRecorderErrorCallback): void
  offError(callback?: HarmonyRecorderErrorCallback | null): void
}

类型定义

以下类型均可从 @/uni_modules/hans-hm-recorder 导入,以插件内的 utssdk/interface.uts 为准。

基础类型

为兼容 UTS 与 HarmonyOS ArkTS,以下公共类型实际定义为 string,业务传值仍必须使用表格列出的值。

type HarmonyRecorderState = string
type HarmonyRecorderOutputFormat = string
type HarmonyRecorderSourceType = string
type HarmonyRecorderSampleFormat = string

HarmonyRecorderState 可能值:

说明
idle 空闲,尚未开始录音
preparing 正在创建和启动原生录音器
recording 正在录音
pausing 正在暂停
paused 已暂停
resuming 正在恢复
stopping 正在停止并完成文件
stopped 已停止,可再次调用 start()
error 录音发生不可恢复错误
released 原生资源已释放

HarmonyRecorderOutputFormat 可选值:

说明
wav 写入带标准 44 字节文件头的 WAV 文件
pcm 写入原始 S16LE PCM 文件

HarmonyRecorderSourceType 可选值:

说明
mic 普通麦克风录音
voice_recognition 语音识别场景
voice_message 语音消息场景
voice_communication 语音通信场景
unprocessed 未处理音频

HarmonyRecorderSampleFormat 当前仅支持 s16le

HarmonyRecorderOptions

type HarmonyRecorderOptions = {
  sampleRate?: number
  channels?: number
  sampleFormat?: HarmonyRecorderSampleFormat
  sourceType?: HarmonyRecorderSourceType
  outputFormat?: HarmonyRecorderOutputFormat
  fileName?: string
  frameDurationMs?: number
  amplitudeIntervalMs?: number
  emitData?: boolean
  enableBackground?: boolean
}
字段 类型 默认值 可选值或范围 说明
sampleRate number 48000 160004410048000 采样率,单位 Hz
channels number 1 12 声道数
sampleFormat HarmonyRecorderSampleFormat s16le s16le 有符号 16 位小端 PCM
sourceType HarmonyRecorderSourceType mic 见基础类型 音源场景
outputFormat HarmonyRecorderOutputFormat wav wavpcm 输出文件格式
fileName string 自动生成 纯文件名 不能包含 /\..,建议包含正确扩展名
frameDurationMs number 40 20..1000 实时 PCM 分片目标时长,单位 ms
amplitudeIntervalMs number 100 50..1000 振幅回调间隔,单位 ms
emitData boolean false truefalse 是否触发实时 PCM 数据回调
enableBackground boolean false truefalse 是否申请后台录音长时任务

配置只在 createHarmonyRecorder(options) 时读取。需要修改配置时,应停止并销毁旧实例,再创建新实例。

HarmonyRecorderActualConfig

type HarmonyRecorderActualConfig = {
  sampleRate: number
  channels: number
  sampleFormat: string
  sourceType: string
  outputFormat: string
}

这是系统实际创建成功的配置,可能与请求值不同。录音器尚未准备成功时,getActualConfig() 返回 null

HarmonyRecorderStopResult

type HarmonyRecorderStopResult = {
  tempFilePath: string
  durationMs: number
  byteLength: number
  droppedChunkCount: number
  config: HarmonyRecorderActualConfig
}
字段 说明
tempFilePath 录音文件的应用缓存路径
durationMs 录音时长,单位 ms
byteLength 文件总字节数;WAV 包含文件头
droppedChunkCount 页面实时 PCM 回调因背压被丢弃的累计分片数
config 实际生效的录音配置

droppedChunkCount 只表示页面 onData 分片是否被丢弃,不表示写入录音文件的数据丢失。

事件结果类型

type HarmonyRecorderStateResult = {
  state: HarmonyRecorderState
  previousState: HarmonyRecorderState
}

type HarmonyRecorderDataResult = {
  buffer: ArrayBuffer
  sequence: number
  timestampMs: number
  durationMs: number
  droppedChunkCount: number
  isLast: boolean
}

type HarmonyRecorderAmplitudeResult = {
  peak: number
  rms: number
  dbfs: number
  timestampMs: number
}

type HarmonyRecorderDeviceResult = {
  deviceId: number
  deviceName: string
  deviceType: string
}

type HarmonyRecorderInterruptionResult = {
  eventType: string
  forceType: string
  hintType: string
}

HarmonyRecorderDataResult 字段:

字段 说明
buffer 当前 S16LE PCM 分片
sequence 0 开始递增的分片序号
timestampMs 分片起始时间,基于已处理采样帧计算
durationMs 当前分片时长
droppedChunkCount 页面回调累计丢弃分片数
isLast 是否为停止录音时发送的最后一个分片

HarmonyRecorderAmplitudeResult 字段:

字段 范围或说明
peak 峰值,范围 0..1
rms 均方根,范围 0..1
dbfs 分贝满量程,最大 0,最小截断为 -120
timestampMs 基于已处理采样帧计算的时间戳

中断结果的三个字段来自 HarmonyOS 音频中断事件并转换为字符串。业务应按实际设备返回值记录和处理,不要假设所有设备都返回同一组枚举文本。

错误类型

type HarmonyRecorderErrorCode =
  | 9011001
  | 9011002
  | 9011003
  | 9011004
  | 9011005
  | 9011006
  | 9011007
  | 9011008
  | 9011009
  | 9011010

interface HarmonyRecorderFail extends IUniError {
  errCode: HarmonyRecorderErrorCode
}

常用错误字段:

字段 类型 说明
errSubject string 错误主题,固定为 hans-hm-recorder
errCode HarmonyRecorderErrorCode 稳定插件错误码
errMsg string 错误说明
data any \| null 调试数据,可能包含操作名、原生错误和状态

回调类型

type HarmonyRecorderStateCallback =
  (result: HarmonyRecorderStateResult) => void

type HarmonyRecorderDataCallback =
  (result: HarmonyRecorderDataResult) => void

type HarmonyRecorderAmplitudeCallback =
  (result: HarmonyRecorderAmplitudeResult) => void

type HarmonyRecorderDeviceCallback =
  (result: HarmonyRecorderDeviceResult) => void

type HarmonyRecorderInterruptionCallback =
  (result: HarmonyRecorderInterruptionResult) => void

type HarmonyRecorderErrorCallback =
  (result: HarmonyRecorderFail) => void

type CreateHarmonyRecorder =
  (options?: HarmonyRecorderOptions | null) => HarmonyRecorder

type SetHarmonyRecorderLogEnabled =
  (enabled: boolean) => void

API 说明

createHarmonyRecorder(options)

createHarmonyRecorder(
  options?: HarmonyRecorderOptions | null
): HarmonyRecorder

创建一个录音实例。省略 options 或传入 null 时使用默认配置。

非法配置不会在创建对象时静默修正,调用 start() 时会拒绝并返回错误码 9011002。同一时间只允许一个实例实际占用麦克风。

setHarmonyRecorderLogEnabled(enabled)

setHarmonyRecorderLogEnabled(enabled: boolean): void

全局开启或关闭插件内部日志,默认关闭。生产环境建议保持关闭。

实例方法

方法 允许状态 返回值 说明
start() idlestopped Promise<void> 请求麦克风权限并开始录音
pause() recording Promise<void> 暂停采集
resume() paused Promise<void> 恢复采集
stop() recordingpaused Promise<HarmonyRecorderStopResult> 停止并完成输出文件
destroy() 任意 Promise<void> 释放资源并清空监听,可重复调用
getState() 任意 HarmonyRecorderState 获取当前状态
getActualConfig() 任意 HarmonyRecorderActualConfig \| null 获取系统实际生效配置

典型状态变化:

idle
  -> preparing
  -> recording
  -> pausing -> paused
  -> resuming -> recording
  -> stopping -> stopped

发生异常时可能进入 error;资源释放过程中可能收到 released 状态。

所有 Promise 方法都应使用 try/catchonError 用于接收录音过程中异步发生的运行时错误,不能替代 start()pause()resume()stop() 的 Promise 错误处理。

事件说明

状态变化

const callback: HarmonyRecorderStateCallback =
  (res: HarmonyRecorderStateResult): void => {
    console.log(res.previousState, res.state)
  }

recorder.onStateChange(callback)
recorder.offStateChange(callback)

实时 PCM

创建录音器时设置 emitData: true

recorder.onData((res: HarmonyRecorderDataResult) => {
  console.log(
    res.buffer.byteLength,
    res.sequence,
    res.timestampMs,
    res.durationMs,
    res.droppedChunkCount,
    res.isLast
  )
})

buffer 为 S16LE PCM ArrayBuffer。不要在高频回调中执行网络请求、编码、Base64 转换、同步大文件写入或频繁页面更新。

振幅

recorder.onAmplitude((res: HarmonyRecorderAmplitudeResult) => {
  console.log(res.peak, res.rms, res.dbfs, res.timestampMs)
})

振幅始终按 S16LE 的全部声道合并计算,不要求开启 emitData

输入设备变化

recorder.onDeviceChange((res: HarmonyRecorderDeviceResult) => {
  console.log(res.deviceId, res.deviceType, res.deviceName)
})

系统报告输入设备变化时触发,例如麦克风路由发生变化。

录音中断

recorder.onInterruption((res: HarmonyRecorderInterruptionResult) => {
  console.log(res.eventType, res.forceType, res.hintType)
})

电话、其他音频会话或系统策略可能触发录音中断。业务应同时监听 onStateChangeonError 完成状态恢复或页面提示。

运行时错误

recorder.onError((err: HarmonyRecorderFail) => {
  console.error(err.errCode, err.errMsg, err.data)
})

取消监听

每个 offX 方法都支持两种用法:

// 只移除指定回调,必须传入注册时的同一个函数引用。
recorder.offAmplitude(amplitudeCallback)

// 不传回调,移除该事件的全部业务监听。
recorder.offAmplitude()

destroy() 会清空该实例的全部监听。

后台录音

插件已声明以下权限:

  • ohos.permission.MICROPHONE
  • ohos.permission.KEEP_BACKGROUND_RUNNING

使用 enableBackground: true 前,还必须给宿主 EntryAbility 声明后台录音模式。

  1. 用 HBuilderX 运行一次 HarmonyOS 项目。
  2. 将生成的 unpackage/dist/dev/app-harmony/entry/src/main/module.json5 复制到项目的 harmony-configs/entry/src/main/module.json5
  3. EntryAbility 中加入 backgroundModes
{
  "name": "EntryAbility",
  "backgroundModes": [
    "audioRecording"
  ]
}
  1. 创建录音器时启用后台录音:
const options: HarmonyRecorderOptions = {
  enableBackground: true
}

const recorder = createHarmonyRecorder(options)
await recorder.start()

录音必须由用户在前台主动触发。后台长时任务申请失败时,start() 返回 9011009

如果不需要后台录音,保持 enableBackground: false,无需添加 audioRecording

文件保存与播放

stop() 返回的 tempFilePath 位于应用缓存目录,可能被系统清理。需要长期保留时,应在停止录音后复制到业务目录。

outputFormat: 'wav' 的文件可以直接尝试播放:

const result: HarmonyRecorderStopResult = await recorder.stop()

const audio = uni.createInnerAudioContext()
audio.src = result.tempFilePath
audio.play()

// 页面卸载或播放完成后释放。
audio.destroy()

outputFormat: 'pcm' 返回原始 S16LE PCM,不能直接作为普通音频文件播放,需要业务自行解码或封装。

错误码

错误码 含义 常见处理
9011001 麦克风权限被拒绝 引导用户授权或打开系统设置
9011002 参数无效或设备不支持 检查采样率、声道、格式和文件名
9011003 当前状态不允许调用 根据 getState() 调整调用顺序
9011004 已有活动录音实例 停止并销毁其他实例
9011005 创建原生录音器失败 检查设备支持情况并记录 data
9011006 启动或停止采集失败 记录错误并重新创建实例
9011007 文件打开、写入或完成失败 检查存储状态和错误数据
9011008 录音被中断 提示用户并按业务决定是否重录
9011009 后台任务启动失败 检查宿主 backgroundModes 配置
9011010 未分类内部错误 开启日志并保留 err.data 排查

使用注意事项

  • 同一时间只允许一个录音实例采集麦克风。
  • uni-app x 和 uni-app Vue3 中都不要把录音实例放入 reactiveref 或页面 data
  • 先注册事件,再调用 start(),避免遗漏启动阶段的状态和设备事件。
  • 每次 start() 都会重新检查麦克风权限。
  • 页面卸载、业务结束或异常退出录音流程时调用 destroy()
  • destroy() 后不要继续使用旧实例,需要录音时重新创建。
  • getActualConfig() 以系统实际结果为准。
  • 需要永久保存文件时,不要长期依赖缓存路径。
  • 开启 emitData 后应保持回调轻量,并关注 droppedChunkCount

隐私、权限声明

1. 本插件需要申请的系统权限列表:

需要麦克风权限(ohos.permission.MICROPHONE);启用后台录音时需要长时任务权限(ohos.permission.KEEP_BACKGROUND_RUNNING)。

2. 本插件采集的数据、发送的服务器地址、以及数据用途说明:

插件仅在用户主动录音时采集麦克风音频,写入应用缓存目录的本地临时文件,不上传任何音频数据。

3. 本插件是否包含广告,如包含需详细说明广告表达方式、展示频率:

暂无用户评论。