更新记录
1.0.0(2026-07-16)
自用,自用,自用,不做任何保障,慎重购买
仅支持安卓和H5 仅支持安卓和H5 仅支持安卓和H5
平台兼容性
uni-app(5.15)
| Vue2 | Vue3 | Chrome | Safari | app-vue | app-nvue | Android | iOS | 鸿蒙 |
|---|---|---|---|---|---|---|---|---|
| √ | √ | √ | × | √ | × | 12.0 | × | × |
| 微信小程序 | 支付宝小程序 | 抖音小程序 | 百度小程序 | 快手小程序 | 京东小程序 | 鸿蒙元服务 | QQ小程序 | 飞书小程序 | 小红书小程序 | 快应用-华为 | 快应用-联盟 |
|---|---|---|---|---|---|---|---|---|---|---|---|
| × | × | × | × | × | × | × | × | × | × | × | × |
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.json → App模块配置 → 勾选 AVFoundation(音频录制和播放)。
如果需要配置隐私权限描述,可在 manifest.json 的 iOS 节点下添加:
{
"plist": {
"NSMicrophoneUsageDescription": "需要麦克风权限以录制音频"
}
}
快速开始
import {
recordStart,
recordStop,
onRecordStop,
onRecordError,
onRecordTimer,
} 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) // 可用于播放或上传
})
// 3. 停止录音
setTimeout(() => {
recordStop()
}, 5000) // 5 秒后停止
// 4. 回放录音
onRecordStop((info) => {
if (info.tempFilePath) {
const audio = uni.createInnerAudioContext()
audio.src = info.tempFilePath
audio.play()
}
})
API 文档
录音控制
recordStart(options?)
开始录音。需要提前获取麦克风权限(H5 浏览器会自动弹权限请求,App 端插件内部处理)。
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 数据,不生成文件- 如果已处于录音状态,重复调用会被忽略
recordStop()
停止录音。
format='wav'时,触发onRecordStop,tempFilePath为 WAV 文件的完整路径format='pcm'时,tempFilePath为空字符串
recordPause()
暂停录音。麦克风仍然占用,暂停数据采集和计时。
recordResume()
恢复录音。计时从中断处继续。
recordMute()
静音。停止 onRecordFrame / onRecordDecibel / onRecordWaveform 回调,但 PCM 数据继续写入文件(写入静音数据,时长正确)。
适用于:需要停止 UI 刷新但不想中断录音的场景(如暂时不想暴露频谱)。
recordUnmute()
取消静音,恢复所有回调。
录音事件监听
onRecordStop(callback)
监听录音结束。回调参数:
onRecordStop((info: RecordEndInfo) => {
console.log(info.tempFilePath) // WAV 文件路径(format='wav' 时)
console.log(info.duration) // 录音时长(秒)
console.log(info.fileSize) // 文件大小(字节)
console.log(info.interrupted) // 是否被外部中断
})
onRecordError(callback)
监听录音错误。
onRecordError((err: ErrorInfo) => {
console.log(err.errCode) // 错误码
console.log(err.errMsg) // 错误描述
})
错误码说明:
| 错误码 | 说明 |
|---|---|
| 9010001 | 录音初始化失败 |
| 9010002 | 录音启动失败(权限被拒/麦克风被占用) |
| 9010003 | 录音停止失败 |
| 9010004 | 录音被系统中断(如来电) |
| 9010005 | 播放器初始化失败 |
| 9010006 | 播放数据无效 |
onRecordFrame(callback)
监听原始 PCM 帧数据。
回调时机:每次累积的样本数达到 bufferSize 阈值时触发一次。
onRecordFrame((data: ArrayBuffer) => {
// data: Int16Array.buffer,16-bit 小端序 PCM 数据
const pcmData = new Int16Array(data)
console.log('收到帧,样本数:', pcmData.length)
})
注意:每帧样本数等于
bufferSize(默认 4096),约 256ms@16kHz。静音时不触发回调。
onRecordDecibel(callback)
监听实时分贝值(dBFS)。
onRecordDecibel((db: number) => {
// db: 范围约 -96 ~ 0(-96 ≈ 静音,0 ≈ 最大声)
})
静音时不触发回调。
onRecordWaveform(callback)
监听实时波形数据(FFT 频域)。
onRecordWaveform((arr: number[]) => {
// arr: 64 个归一化值,范围 [0, 1]
// 索引 0…63 对应频率 0Hz…8000Hz(@16kHz 采样率)
// 可用于绘制音频频谱可视化(类似均衡器)
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
})
三端均为 FFT 频域算法,输出一致的 64 段频谱值。仅
enableCommunicationMode=false时因采样率不同频率范围会有差异。
onRecordTimer(callback)
监听录音时长(每秒触发一次)。
onRecordTimer((sec: number) => {
// sec: 已录制秒数(排除暂停时间)
})
录音状态查询
const isRec = isRecording() // 是否正在录音
const isPau = isRecordPaused() // 是否暂停中
const isMut = isRecordMuted() // 是否静音
PCM 播放控制
pcmInit(config?)
初始化 PCM 播放器。重复调用会自动销毁旧实例。
pcmInit({
inputSampleRate: 16000, // PCM 数据的采样率
numChannels: 1, // 声道数
bitDepth: 16, // 位深度(16 或 32)
})
推荐用法:在页面加载时初始化一次,后续复用。
pcmPlay(data)
播放 PCM 音频数据。
// 播放单个 PCM 帧
pcmPlay(arrayBuffer)
// 批量播放
pcmPlay([arrayBuffer1, arrayBuffer2])
data 支持的类型:
| 类型 | 说明 |
|---|---|
ArrayBuffer |
Int16Array.buffer,16-bit 小端序 PCM |
string |
base64 编码的 PCM 数据 |
ArrayBuffer[] |
多个 ArrayBuffer 批量播放 |
string[] |
多个 base64 字符串批量播放 |
不支持 WAV 文件路径。如需播放 WAV 文件,请使用
uni.createInnerAudioContext()。
pcmAppend(data, immediatePlay?)
向播放队列追加 PCM 数据。
pcmAppend(arrayBuffer) // 入队但不播放
pcmAppend(arrayBuffer, true) // 入队并立即播放
pcmPause() / pcmResume()
暂停/恢复 PCM 播放。
pcmStop()
停止 PCM 播放,清空播放队列。
pcmReplay()
使用历史数据重新播放(重播从首次 pcmPlay 到最近一次 pcmClear 之间的所有数据)。
pcmClear()
清空播放队列和所有历史数据。
pcmStatus()
获取播放器当前状态。
const status = pcmStatus()
if (status) {
console.log(status.isPlaying) // 是否正在播放
console.log(status.isPaused) // 是否暂停
console.log(status.queueLength) // 队列中待播数据块数
console.log(status.contextState) // 上下文状态:'running' | 'suspended' | 'closed'
}
播放事件监听
onPCMStatus((status) => {
// status 同 pcmStatus() 返回值
})
onPCMError((err) => {
console.log(err.errCode, err.errMsg)
})
资源销毁
import { recordDestroy, pcmDestroy } from '@/uni_modules/tki-audio-recorder'
// 销毁录音资源(不触发 onRecordStop 回调)
recordDestroy()
// 销毁 PCM 播放器
pcmDestroy()
// 推荐在页面卸载时调用
onUnload(() => {
recordDestroy()
pcmDestroy()
})
recordDestroy()和pcmDestroy()已分离,可根据业务场景单独调用。recordDestroy()不会触发任何回调(onRecordStop/onRecordError/onRecordFrame等均被清空)。
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 要真正生效,还需要满足以下条件:
- 使用
pcmPlay播放音频(不能只用uni.createInnerAudioContext) - 在
recordStart()之前调用pcmInit(),确保 PCMPlayer 已创建 - 在录音进行中播放(停止录音后 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 要求
AudioRecord和AudioTrack使用同一个 audioSessionId。recordStart获取真实 sessionId,传给PCMPlayer,pcmPlay第一次创建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,
recordDestroy, pcmDestroy,
} from '@/uni_modules/tki-audio-recorder'
// 页面加载时初始化播放器
pcmInit({ inputSampleRate: 16000 })
// 录音时实时回放
onRecordFrame((data: ArrayBuffer) => {
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 返回数据格式
| 平台 | 数据类型 | 字节序 | 格式 |
|---|---|---|---|
| 三端一致 | ArrayBuffer |
小端 | Int16 PCM |
onRecordFrame 返回的数据可以在 H5 和 Android 端互通消费,iOS 不确定。
常见问题
Q: 录音后无法播放 WAV 文件?
Android 端 WAV 文件保存在 externalCacheDir(/sdcard/Android/data/.../cache/),uni.createInnerAudioContext 可以直接访问。
如果使用文件管理器找不到文件,请确认路径包含 Android/data/(外部存储)。
Q: 为什么静音后 WAV 文件还是原来的大小?
静音不改变文件时长和大小——只是 PCM 数据替换为零。这样保证 tempFilePath 指向的 WAV 文件时长总是正确的。
Q: pcmPlay 报 ArrayBuffer 被忽略?
Android 端 pcmPlay([arrayBuf1, arrayBuf2]) 数组中的 ArrayBuffer 元素也会正确处理。如果使用的是字符串数组,字符串按 base64 解码。
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)

收藏人数:
购买普通授权版(
试用
赞赏(0)
下载 32981
赞赏 11
下载 12431697
赞赏 1934
赞赏
京公网安备:11010802035340号