更新记录

1.0.0(2026-07-24)

自用,不保证稳定,谨慎购买


平台兼容性

uni-app(5.15)

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

uni-app x(5.15)

Chrome Safari Android Android插件版本 iOS 鸿蒙 微信小程序
12.0 1.0.0 × × ×

tki-audio-recorder

音频录制插件,支持实时 PCM 帧数据回调、PCM 流播放、通话模式(AEC + NS + AGC)。

适用于:H5(AudioWorklet)、Android(AudioRecord)、iOS(AVAudioEngine,不确定)。


目录


安装

通过 uni_modules 安装(推荐)

在 HBuilderX 中右键项目目录 → 插件市场 → 搜索 tki-audio-recorder → 点击安装。

手动安装

uni_modules/tki-audio-recorder 目录复制到项目的 uni_modules/ 目录下。

配置

Android: 插件会自动处理录音权限。Android 6.0+ 需要运行时权限,插件内部已处理。

iOS: 项目 manifest.jsonApp模块配置 → 勾选 AVFoundation(音频录制和播放)。

如果需要配置隐私权限描述,可在 manifest.jsoniOS 节点下添加:

{
  "plist": {
    "NSMicrophoneUsageDescription": "需要麦克风权限以录制音频"
  }
}

快速开始

import {
  // 录音控制
  recordStart, recordStop, recordPause, recordResume, recordMute, recordUnmute,
  // 录音事件
  onRecordStop, onRecordError, onRecordTimer, onRecordFrame, onRecordDecibel, onRecordWaveform,
  // PCM 播放
  pcmInit, pcmPlay, pcmPlayWav, pcmAppend, pcmStop, pcmReplay, pcmClear, pcmStatus,
  // 播放事件
  onPcmStatus, onPcmError, onPcmDecibel, onPcmWaveform, onPcmTimer,
  // 资源销毁
  recordDestroy, pcmDestroy,
} from '@/uni_modules/tki-audio-recorder'

// 1. 开始录音
recordStart({
  sampleRate: 16000,
  enableCommunicationMode: true,
  format: 'wav',
})

// 2. 监听录音结束
onRecordStop((info) => {
  console.log('录音结束,时长:', info.duration, '秒')
  console.log('WAV 文件:', info.tempFilePath)
  // 回放:通过 PCM 播放器(含 FFT 频谱/分贝/计时)
  if (info.tempFilePath) {
    pcmPlayWav(info.tempFilePath)
    // 或用系统播放器: uni.createInnerAudioContext()
  }
})

// 3. 停止录音
setTimeout(() => {
  recordStop()
}, 5000) // 5 秒后停止

API 文档

录音控制

函数 说明 参数
recordStart(options?) 开始录音 RecordOptions
recordStop() 停止录音
recordPause() 暂停录音(麦克风仍占用,计时暂停)
recordResume() 恢复录音(计时从中断处继续)
recordMute() 静音:帧回调推送零数据,分贝/波形回调停止
recordUnmute() 取消静音,恢复所有回调

recordStart(options?)

recordStart({
  sampleRate: 16000,             // 采样率,默认 16000
  numberOfChannels: 1,           // 声道数,默认 1(单声道)
  format: 'wav',                 // 录音格式:'wav' 或 'pcm'
  duration: 0,                   // 录音时长限制(ms),0=不限时
  bufferSize: 4096,              // onRecordFrame 回调触发阈值
  enableCommunicationMode: true, // 通话模式(含 AEC+NS+AGC)
})
  • format='wav':录音结束后生成 WAV 文件并通过 onRecordStop 返回文件路径
  • format='pcm':仅通过 onRecordFrame 回调原始 PCM 数据,不生成文件
  • 如果已处于录音状态,重复调用会被忽略

录音事件监听

函数 回调参数 触发时机 静音时
onRecordStop(cb) RecordEndInfo 录音停止/中断
onRecordError(cb) { errSubject, errCode, errMsg } 录音出错(UniError 对象)
onRecordFrame(cb) string(base64) 帧缓冲达到 bufferSize ✅ 推送零数据
onRecordDecibel(cb) number(dBFS) 约 16ms 间隔 ❌ 不触发
onRecordWaveform(cb) number[](64点) 约 16ms 间隔 ❌ 不触发
onRecordTimer(cb) number(秒) 每秒一次 ✅ 继续触发

错误码说明

错误码 说明
9010001 录音初始化失败
9010002 录音启动失败(权限被拒/麦克风被占用)
9010003 录音停止失败
9010004 录音被系统中断(如来电)
9010005 播放器初始化失败
9010006 播放数据无效

onRecordFrame(callback)

回调固定返回 base64 编码的字符串(16-bit 小端序 Int16 PCM),三端一致。 每帧样本数等于 bufferSize(默认 4096,约 256ms@16kHz)。

如需标准 ArrayBuffer,使用 uni.base64ToArrayBuffer() 转换:

onRecordFrame((base64) => {
  const pcm = uni.base64ToArrayBuffer(base64)  // ArrayBuffer
  const samples = new Int16Array(pcm)           // Int16 PCM 样本
  console.log('收到帧,样本数:', samples.length)
})

onRecordDecibel(callback)

onRecordDecibel((db: number) => {
  // db: 范围约 -96 ~ 0(-96 ≈ 静音,0 ≈ 最大声)
})

onRecordWaveform(callback)

64 段 FFT 频谱归一化值 [0, 1],对应 0~8000Hz(@16kHz 采样率)。

onRecordWaveform((arr: number[]) => {
  const bass   = arr.slice(0, 2).reduce((a, b) => a + b, 0) / 2     // ~0~250Hz
  const mid    = arr.slice(2, 32).reduce((a, b) => a + b, 0) / 30   // ~250~4000Hz
  const treble = arr.slice(32).reduce((a, b) => a + b, 0) / 32      // ~4000~8000Hz
})

录音状态查询

函数 返回 说明
isRecording() boolean 是否正在录音
isRecordPaused() boolean 是否暂停中
isRecordMuted() boolean 是否静音

PCM 播放控制

函数 说明
pcmInit(config?) 初始化 PCM 播放器。重复调用自动销毁旧实例
pcmPlay(data) 播放 PCM 数据(base64 字符串 / 字符串数组)
pcmPlayWav(filePath) 播放 WAV 文件(自动解析 → PCM 播放,支持实时频谱)
pcmAppend(data, immediatePlay?) 追加 base64 数据到播放队列
pcmPause() 暂停播放
pcmResume() 恢复播放
pcmStop() 停止播放,清空队列
pcmReplay() 从历史存档重新播放
pcmClear() 清空队列和历史数据
pcmStatus() 获取播放器状态 → PlaybackStatus

pcmPlay data 支持的类型

类型 说明
string base64 编码的 PCM 数据
string[] 多个 base64 字符串批量播放

pcmPlay 只接受 base64 PCM 数据,不支持 ArrayBuffer 或 WAV 文件路径。如需播放 WAV 文件,请使用 pcmPlayWavuni.createInnerAudioContext()

pcmInit({ inputSampleRate: 16000 })   // 页面加载时初始化一次

// 使用 onRecordFrame 配合 pcmPlay(推荐方式)
onRecordFrame((base64) => {
  pcmPlay(base64)                      // 实时回放
  queue.push(base64)                   // 存入队列
})
// 批量播放
pcmPlay([base64Frame1, base64Frame2])
// 播放 WAV 文件(自动解析 → PCM 播放,FFT/分贝/计时均有效)
pcmPlayWav('file:///storage/emulated/0/.../recording.wav')
pcmPlayWav('blob:http://localhost:5173/xxxxx')
// 追加到队列
pcmAppend(base64Frame)
pcmAppend(base64Frame, true)           // 追加并立即播放

pcmInit(config?) 配置项 PlayerConfig

PlayerConfig 参数表:

参数 类型 默认值 说明
inputSampleRate number 16000 输入 PCM 数据的采样率(Hz)
numChannels number 1 声道数:1=单声道,2=立体声
bitDepth number 16 位深度:16 或 32
littleEndian boolean true 是否小端字节序
pcmType 'int' \| 'float' 'int' PCM 数据类型
bufferSize number 4096 帧缓冲区大小(采样点数),应与 recordStartbufferSize 一致,用于计算播放完成超时

bufferSize 应与录音时的 recordStart({ bufferSize }) 保持一致。例如录音配 bufferSize: 1024,则 pcmInit({ bufferSize: 1024 })。Android 端使用此值动态计算 AudioTrack 缓冲区大小和无数据超时,防止流式回放时误判播放结束。

pcmStatus() 返回值 PlaybackStatus

字段 类型 说明
isPlaying boolean 是否正在播放
isPaused boolean 是否暂停
isEnded boolean 是否播放结束(队列自然耗尽)
isStopped boolean 是否被主动停止
queueLength number 队列中待播数据块数
contextState string 上下文状态:running | suspended | closed

播放事件监听

函数 回调参数 说明
onPcmStatus(cb) PlaybackStatus 播放器状态变化(播放/暂停/结束/停止)
onPcmError(cb) { errSubject, errCode, errMsg } 播放出错(UniError 对象)
onPcmDecibel(cb) number(dBFS) 实时分贝值(≈60fps),与 onRecordDecibel 算法一致
onPcmWaveform(cb) number[](64点) 实时 FFT 频谱(≈60fps),与 onRecordWaveform 算法一致
onPcmTimer(cb) number(秒) 播放时长(每秒触发)

资源销毁

函数 说明
recordDestroy() 销毁录音资源(不触发任何回调)。推荐在 onUnload 中调用
pcmDestroy() 销毁 PCM 播放器。推荐在 onUnload 中调用
onUnload(() => {
  recordDestroy()   // 先销毁录音
  pcmDestroy()      // 再销毁播放器
})

recordDestroy()pcmDestroy() 已分离,可根据业务场景单独调用。 recordDestroy() 不会触发任何回调(先清回调再调 stop,防止最后一帧触发 pcmPlay 等副作用)。


RecordOptions 配置项

参数 类型 默认值 说明
sampleRate number 16000 采样率(Hz),常用:8000 / 16000 / 44100
numberOfChannels number 1 声道数:1=单声道,2=立体声
format wav pcm wav wav=生成 WAV 文件;pcm=仅帧回调
duration number 0 录音时长限制(ms),0=不限时,>=1000 生效
bufferSize number 4096 onRecordFrame 触发阈值(采样点数),默认约 256ms@16kHz
enableCommunicationMode boolean true true=开启 AEC+NS+AGC;false=全部关闭(原始录音)

enableCommunicationMode 各平台实现

平台 true false
H5 getUserMedia 约束全部开启 getUserMedia 约束全部关闭
Android AudioSource.VOICE_COMMUNICATION AudioSource.UNPROCESSED(API 26+)或 MIC
iOS AVAudioSessionModeVoiceChat AVAudioSessionModeDefault

Android 回声消除说明

Android 端启用 enableCommunicationMode: true 只是选择了 VOICE_COMMUNICATION 音频源,硬件 AEC 要真正生效,还需要满足以下条件:

  1. 使用 pcmPlay 播放音频(不能只用 uni.createInnerAudioContext
  2. recordStart() 之前调用 pcmInit(),确保 PCMPlayer 已创建
  3. 在录音进行中播放(停止录音后 sessionId 失效)
// ✅ AEC 生效的正确顺序
pcmInit({ inputSampleRate: 16000 })   // 1. 先创建 PCMPlayer
recordStart({                         // 2. AudioRecord 启动,sessionId=123
  enableCommunicationMode: true,      //    自动传到 PCMPlayer
})
// 录音中...
pcmPlay(data)                         // 3. AudioTrack 用 sessionId=123 创建 ✅ 硬件 AEC
// ❌ AEC 无效的典型错误
recordStart({})                       // 1. 录音启动,但 player==null,sessionId 无法传递
pcmInit({})                           // 2. 新建 player,sessionId=0
pcmPlay(data)                         // 3. AudioTrack 以 sessionId=0 创建 ❌ 无 AEC

原理:Android 硬件 AEC 要求 AudioRecordAudioTrack 使用同一个 audioSessionIdrecordStart 获取真实 sessionId,传给 PCMPlayerpcmPlay 第一次创建 AudioTrack 时使用该 sessionId,系统此时才能挂载硬件 AEC。


RecordEndInfo 回调信息

onRecordStop 回调的参数:

字段 类型 说明
tempFilePath string WAV 文件路径(format='wav' 时有效),可直接用于 uni.createInnerAudioContext
duration number 录音时长(秒),已扣除暂停时间
fileSize number 文件大小(字节)
interrupted boolean 是否被外部中断(如来电)

各平台文件路径格式

平台 示例路径
H5 blob:http://localhost:5173/xxxx-xxxx
Android file:///storage/emulated/0/Android/data/.../recording_2026_07_15_14_30_00_1234.wav
iOS /private/var/mobile/Containers/.../tmp/recording_2026_07_15_14_30_00_1234.wav

完整示例

录音 + 文件回放

import {
  recordStart, recordStop, recordPause, recordResume,
  recordMute, recordUnmute,
  onRecordStop, onRecordError, onRecordTimer,
  recordDestroy, pcmDestroy,
} from '@/uni_modules/tki-audio-recorder'

const state = reactive({
  isRec: false,
  isPaused: false,
  isMuted: false,
  duration: 0,
  filePath: '',
})

// 录音控制
function startRec() {
  state.filePath = ''
  state.duration = 0
  recordStart({
    sampleRate: 16000,
    enableCommunicationMode: true,
    format: 'wav',
    bufferSize: 4096,
  })
  state.isRec = true
}

function stopRec() { recordStop() }
function pauseRec() { recordPause(); state.isPaused = true }
function resumeRec() { recordResume(); state.isPaused = false }
function muteRec() { recordMute(); state.isMuted = true }
function unmuteRec() { recordUnmute(); state.isMuted = false }

// 事件监听
onRecordStop((info) => {
  state.isRec = false
  state.isPaused = false
  state.filePath = info.tempFilePath
  console.log(`录制 ${info.duration}秒, 文件: ${info.tempFilePath}`)
})

onRecordTimer((sec) => { state.duration = sec })

// 回放
function playWav() {
  if (!state.filePath) return
  const audio = uni.createInnerAudioContext()
  audio.src = state.filePath
  audio.onEnded(() => audio.destroy())
  audio.onError(() => audio.destroy())
  audio.play()
}

// 页面卸载
onUnload(() => {
  recordDestroy()
  pcmDestroy()
})

实时 PCM 回放(对讲机模式)

import {
  recordStart, recordStop, pcmInit, pcmPlay,
  onRecordFrame, onRecordStop, onRecordError,
  recordDestroy, pcmDestroy,
} from '@/uni_modules/tki-audio-recorder'

// 页面加载时初始化播放器
pcmInit({ inputSampleRate: 16000 })

// 监听录音错误(如权限被拒)
onRecordError((err) => {
  console.error('录音出错:', err.errCode, err.errMsg)
  uni.showToast({ title: '录音失败', icon: 'none' })
})

// 录音时实时回放(base64 直传,无需转换)
onRecordFrame((data) => {
  pcmPlay(data)
})

function start() {
  recordStart({
    sampleRate: 16000,
    enableCommunicationMode: true,
    format: 'pcm',
    bufferSize: 4096,
  })
}

onUnload(() => {
  recordDestroy()
  pcmDestroy()
})

format: 'pcm' 避免生成无用的 WAV 文件,减少开销。


平台差异说明

功能差异

功能 H5 Android iOS
录音引擎 AudioWorklet AudioRecord AVAudioEngine
播放引擎 AudioContext + BufferSource AudioTrack AVAudioPlayerNode
WAV 文件 ✅ Blob URL ✅ 文件路径 不确定(文件路径)
帧回调(bufferSize) ✅ 每 4096 样本 ✅ 每 4096 样本 不确定(每 4096 样本)
分贝/波形 不确定
pause/resume 不确定
mute/unmute ✅(写入零数据) ✅(写入零数据) 不确定(写入零数据)
AEC (enableCommunicationMode) getUserMedia 约束 VOICE_COMMUNICATION 不确定(.voiceChat 模式)
onRecordDecibel 频域能量 时域 RMS 不确定(时域 RMS)
onRecordWaveform 频域 FFT 128 点 → 64 段 FFT 128 点 → 64 段 不确定(FFT 128 点 → 64 段)

onRecordFrame 返回数据格式

三端统一返回 base64 编码的字符串(16-bit 小端序 Int16 PCM),无平台差异。

onRecordFrame((base64) => {
  // 三端一致,无需任何平台判断
  const pcm = uni.base64ToArrayBuffer(base64)  // 可选:转 ArrayBuffer
  const samples = new Int16Array(pcm)
})

常见问题

Q: 录音后无法播放 WAV 文件?

Android 端 WAV 文件保存在 externalCacheDir/sdcard/Android/data/.../cache/),uni.createInnerAudioContext 可以直接访问。 如果使用文件管理器找不到文件,请确认路径包含 Android/data/(外部存储)。

Q: 为什么静音后 WAV 文件还是原来的大小?

静音不改变文件时长和大小——只是 PCM 数据替换为零。这样保证 tempFilePath 指向的 WAV 文件时长总是正确的。

Q: pcmPlay 只接受 base64,为什么?

Android 端 UTS 桥接对 ArrayBuffer/Uint8Array 等二进制类型存在类型丢失问题,base64 字符串可完全规避。 如需从 ArrayBuffer 转换为 base64,使用 btoauni.arrayBufferToBase64

Q: H5 端录制时间长后页面卡顿?

H5 端的 PCM 数据采用分块存储(pcmChunks: Float32Array[]),避免每帧拷贝整个缓冲区。即使是长时间录音内存占用也有限。

Q: 录音时长不准?

Web 端使用 AudioContext.currentTime(暂停时自动冻结)。 Android/iOS 使用 elapsedPaused 累计暂停时间扣除。 如果发现时长偏差,请确认是否误调了 pause()/resume()

Q: 录音文件保存在哪里?

平台 目录 清理时机
H5 内存 Blob 页面刷新后失效
Android externalCacheDir App 缓存清理时
iOS NSTemporaryDirectory 系统定期清理

如需持久保存,请将文件复制到应用的其他目录。

Q: 支持后台录音吗?

  • H5: 不支持(浏览器限制,页面不可见时 getUserMedia 会暂停)
  • Android: 支持(Service 保活需额外配置)
  • iOS: 不确定(需配置 Background Modes → Audio, AirPlay, and Picture in Picture)

隐私、权限声明

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

麦克风权限

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

插件会访问麦克风进行录音,PCM音频数据仅在本地处理,不会上传

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

暂无用户评论。