更新记录
1.3.2(2026-04-21)
修复 iOS 云打包时 UTS 插件编译失败问题(Optional 回调未解包)
1.3.1(2026-04-03)
修复IOS在调用stop方法的时候,概率性阻塞卡死的问题
1.3.0(2026-03-31)
- 新增声音源配置,支持设置是否隔离设备扬声器声音,仅录取外部麦克风声音
- 新增录音是否与其他App音频共存的配置
平台兼容性
uni-app(4.0)
| Vue2 | Vue3 | Chrome | Safari | app-vue | app-nvue | Android | Android插件版本 | iOS | iOS插件版本 | 鸿蒙 |
|---|---|---|---|---|---|---|---|---|---|---|
| - | - | - | - | - | - | 5.0 | 1.0.0 | 12 | 1.0.0 | - |
| 微信小程序 | 支付宝小程序 | 抖音小程序 | 百度小程序 | 快手小程序 | 京东小程序 | 鸿蒙元服务 | QQ小程序 | 飞书小程序 | 小红书小程序 | 快应用-华为 | 快应用-联盟 |
|---|---|---|---|---|---|---|---|---|---|---|---|
| - | - | - | - | - | - | - | - | - | - | - | - |
uni-app x(5.0)
| Chrome | Safari | Android | Android插件版本 | iOS | iOS插件版本 | 鸿蒙 | 微信小程序 |
|---|---|---|---|---|---|---|---|
| - | - | 5.0 | 1.0.0 | 12 | 1.0.0 | - | - |
其他
| 多语言 | 暗黑模式 | 宽屏模式 |
|---|---|---|
| √ | √ | √ |
Android iOS录音管理器 - 实时PCM音频流录音插件
支持实时PCM帧回调的uni-app录音插件,提供流式音频数据处理能力,适用于语音识别、实时转写、波形显示等场景。纯UTS实现,Android/iOS双端适配。
✨ 核心特性
- ✅ 实时PCM音频流 - 边录边获取原始PCM数据,延迟低至40ms
- ✅ 实时分贝监测 - RMS算法计算,实时返回音量分贝值
- ✅ 流式数据处理 - 支持边录边传、边录边识别等流式场景
- ✅ WAV文件保存 - 自动保存为标准WAV格式,支持file://直接上传
- ✅ Android/iOS - 双端适配,统一API,透明处理平台差异
- ✅ 暂停/恢复 - 支持录音过程中暂停和恢复
- ✅ 录音与播放共存 - 录音期间可同时播放音频,互不干扰
- ✅ 录音源可控(麦克风/混音模式) - 支持仅录制麦克风声音,或开启混音模式录制“麦克风 + 应用内播放音频”
- ✅ 中断自动检测 - 多层看门狗机制,来电/麦克风被抢占时立即回调通知
- ✅ 突破录音时长限制 - 无系统默认60秒/10分钟限制,支持长时间持续录音
- ✅ 纯UTS实现 - 无需原生代码,开箱即用
🔐 权限说明
Android
插件自动声明以下权限:
| 权限 | 用途 | 必须 |
|---|---|---|
android.permission.RECORD_AUDIO |
录音 | ✅ 必须 |
android.permission.POST_NOTIFICATIONS |
录音通知(Android 13+,showNotification=true 时使用) |
可选 |
android.permission.READ_PHONE_STATE |
来电检测,来电时自动中断录音 | 可选 |
说明:
READ_PHONE_STATE无需主动申请。已授权时插件自动启用来电检测;未授权时静默降级,录音功能不受影响,仅来电中断检测延迟约3秒。
iOS
需要在 Info.plist 中添加麦克风权限说明(插件已自动配置):
<key>NSMicrophoneUsageDescription</key>
<string>需要使用麦克风进行录音</string>
后台录音(可选):若需要应用进入后台后继续录音,需在 manifest.json 中配置:
"app-plus": {
"distribute": {
"ios": {
"UIBackgroundModes": ["audio"]
}
}
}
未配置时,进入后台约3秒后
onError会收到8010006错误回调(系统中断),录音自动停止。
🚀 快速开始
基本使用
import { getRecorderManager } from '@/uni_modules/dh-recorder';
// 获取录音管理器实例(单例)
const recorderManager = getRecorderManager();
// 监听录音开始
recorderManager.onStart(() => {
console.log('录音开始');
});
// 监听录音帧数据(核心功能)
recorderManager.onFrameRecorded((res) => {
// Android: res.base64 为 base64 字符串,需解码
// iOS: res.frameBuffer 为 ArrayBuffer,可直接使用
const arrayBuffer = res.frameBuffer ?? uni.base64ToArrayBuffer(res.base64 ?? '');
console.log('帧数据大小:', arrayBuffer.byteLength); // 如: 1280字节
console.log('当前分贝:', res.decibel); // 如: -35.2 dB
});
// 监听录音停止
recorderManager.onStop((res) => {
console.log('📁 录音文件:', res.tempFilePath); // file://格式路径
console.log('⏱️ 录音时长:', res.duration, 'ms');
console.log('📦 文件大小:', res.fileSize, 'bytes');
});
// 监听错误(含中断通知)
recorderManager.onError((err) => {
console.error('❌ 录音错误:', err.errCode, err.errMsg);
// 8010005: 麦克风被其他应用抢占
// 8010006: 来电/系统中断
});
// 开始录音
recorderManager.start({
sampleRate: 16000, // 采样率:16kHz(推荐语音场景)
numberOfChannels: 1, // 声道数:单声道
bitsPerSample: 16, // 位深:16位
frameSize: 640, // 帧大小:640样本 ≈ 40ms @ 16kHz
format: 'wav', // 格式:WAV
showNotification: true, // 仅Android:开启录音通知,增强后台/息屏存活率
notificationName: '我的App',
notificationText: '正在录音中,不要关闭应用',
androidOutputScope: 'external',
isolateSpeaker: false, // 仅Android:是否只录取麦克风声音(隔离扬声器),默认false
mixWithOthers: true, // 录音是否与其他App音频共存,默认true
});
// 停止录音
recorderManager.stop();
上传录音文件
recorderManager.onStop((res) => {
if (res.success && res.tempFilePath) {
uni.uploadFile({
url: 'https://your-server.com/upload',
filePath: res.tempFilePath, // file://格式,可直接上传
name: 'audioFile',
formData: {
duration: res.duration,
fileSize: res.fileSize,
},
success: (uploadRes) => {
console.log('✅ 上传成功:', uploadRes.data);
},
fail: (err) => {
console.error('❌ 上传失败:', err);
},
});
}
});
暂停和恢复
recorderManager.onPause(() => {
console.log('⏸️ 录音已暂停');
});
recorderManager.onResume(() => {
console.log('▶️ 录音已恢复');
});
recorderManager.pause(); // 暂停
recorderManager.resume(); // 恢复
recorderManager.cancel(); // 取消(不保存文件)
中断处理
中断发生时,onError 先回调(原因),onStop 后回调(已录文件),可根据 interrupted 字段区分:
recorderManager.onError((err) => {
switch (err.errCode) {
case 8010005:
showToast('录音被中断,请重新开始');
break;
case 8010006:
showToast('录音被电话中断');
break;
default:
showToast('录音出错:' + err.errMsg);
}
});
recorderManager.onStop((res) => {
if (res.interrupted) {
// 外部中断自动停止:保存已录片段或提示用户
console.log('中断前已录时长:', res.duration, 'ms');
console.log('已保存部分录音:', res.tempFilePath);
// 可选:上传已录片段
uploadPartialRecording(res.tempFilePath);
} else {
// 用户主动停止:正常流程
uploadRecording(res.tempFilePath);
}
updateUIToStopped();
});
📖 API文档
getRecorderManager()
获取录音管理器单例。
返回值: IRecorderManager
RecorderManager 实例方法
start(options)
开始录音。若已在录音中,会先停止当前录音再重新开始。
参数:
| 参数名 | 类型 | 必填 | 默认值 | 说明 |
|---|---|---|---|---|
| sampleRate | number | 否 | 16000 | 采样率(Hz),常用值:8000/16000/44100/48000 |
| numberOfChannels | number | 否 | 1 | 声道数:1=单声道,2=立体声 |
| bitsPerSample | number | 否 | 16 | 位深,支持:8/16/24/32 |
| frameSize | number | 否 | 640 | 帧大小(样本数)。16位时:640样本=1280字节≈40ms@16kHz |
| format | string | 否 | 'wav' | 录音格式:wav 或 pcm |
| showNotification | boolean | 否 | false | 仅Android:显示常驻通知,增强后台/息屏存活率 |
| notificationName | string | 否 | 应用名称 | 仅Android:通知标题 |
| notificationText | string | 否 | 正在录音中,不要关闭应用 | 仅Android:通知内容 |
| androidOutputScope | string | 否 | 'external' | 仅Android:文件输出目录,external(外部应用缓存)或 internal(内部私有) |
| isolateSpeaker | boolean | 否 | false | 仅Android有效:录音时隔离设备扬声器声音,只录取外部(麦克风)声音,不录入本机播放的音频 |
| mixWithOthers | boolean | 否 | true | 录音是否与其他App音频共存。true(默认):录音期间背景音乐/视频等继续播放;false:录音独占音频,其他App音频会暂停。Android需API 26+生效,iOS均支持 |
帧大小(frameSize)说明:
- 单位是样本数,不是字节数
- 16位PCM:1样本 = 2字节,640样本 = 1280字节 ≈ 40ms @ 16kHz
- 值越小回调频率越高,实时性越好,CPU占用也越高
后台录音增强(Android):
showNotification=false(默认):不显示通知,后台/息屏存活率取决于系统showNotification=true:显示常驻通知,可提升后台/息屏场景下的录音存活率
文件目录说明(Android):
external(默认):外部应用专属缓存目录,更利于本地file://直接播放internal:内部私有缓存目录,安全性更高- 外部目录不可用时自动回退到内部
stop()
停止录音,触发 onStop 回调,返回录音文件信息。
pause()
暂停录音,触发 onPause 回调。暂停期间音频帧不回调,但底层持续读取以防缓冲区积压。
resume()
恢复录音,触发 onResume 回调。
cancel()
取消录音,不保存文件,不触发 onStop 回调。
onStart(callback) / offStart(callback)
监听/取消监听录音开始事件。
onStop(callback) / offStop(callback)
监听/取消监听录音停止事件。
回调参数:
{
success: boolean; // 是否成功
tempFilePath: string; // 录音文件路径(file://格式)
duration: number; // 录音总时长(毫秒)
fileSize: number; // 文件大小(字节)
interrupted: boolean; // 是否因外部中断(来电、麦克风被抢占等)自动停止,主动调用 stop() 时为 false
}
中断时
onError先触发(说明原因),onStop后触发(交付已录文件)。若中断时尚无任何录音数据(首帧未到达),则不触发onStop。
路径格式示例:
- Android(external):
file:///storage/emulated/0/Android/data/com.xxx/cache/dh_record_1234567890.wav - iOS:
file:///var/mobile/Containers/Data/Application/.../tmp/dh_record_1234567890.wav
onFrameRecorded(callback) / offFrameRecorded(callback)
监听/取消监听录音帧数据事件,这是本插件的核心功能。
回调参数:
{
frameBuffer?: ArrayBuffer; // 音频帧数据(iOS,Int16小端序PCM)
base64?: string; // base64编码的音频帧数据(Android)
isLastFrame: boolean; // 是否为最后一帧(当前始终为 false)
decibel: number; // 当前帧分贝值(约-96dB静音 到 0dB满刻度)
}
跨平台差异:
- iOS:通过
frameBuffer(ArrayBuffer)直接传输,可直接使用DataView、Int16Array等API - Android:通过
base64传输,需用uni.base64ToArrayBuffer(res.base64)转换
// 兼容两端的写法
recorderManager.onFrameRecorded((res) => {
const buffer = res.frameBuffer ?? uni.base64ToArrayBuffer(res.base64 ?? '');
// 使用 buffer...
});
onError(callback) / offError(callback)
监听/取消监听录音错误及中断事件。
回调参数:
{
errCode: number; // 错误码(见下表)
errSubject: string; // 错误主题(固定为 'dh-recorder')
errMsg: string; // 错误详细描述
}
错误码说明:
| 错误码 | 含义 | 典型场景 |
|---|---|---|
| 8010001 | 录音权限被拒绝 | 用户拒绝麦克风授权 |
| 8010002 | 录音初始化失败 | 硬件或引擎创建失败 |
| 8010003 | 录音启动失败(麦克风被占用) | start() 时麦克风已被其他应用占用 |
| 8010004 | 录音停止失败 | stop() 过程中发生异常 |
| 8010005 | 录音被中断 | 运行中麦克风被抢占、持续无音频数据 |
| 8010006 | 录音被系统/电话中断 | 来电、其他应用抢占音频会话、进入后台(iOS无后台权限时) |
| 8010007 | 录音数据处理异常 | 帧数据读取或转换时发生内部异常 |
onPause(callback) / offPause(callback)
监听/取消监听录音暂停事件。
onResume(callback) / offResume(callback)
监听/取消监听录音恢复事件。
🎯 高级用法
实时语音识别
const recorderManager = getRecorderManager();
const ws = new WebSocket('wss://asr-api.example.com');
recorderManager.onFrameRecorded((res) => {
if (ws.readyState === WebSocket.OPEN) {
// Android 需先解码,iOS 可直接发送
const buffer = res.frameBuffer ?? uni.base64ToArrayBuffer(res.base64 ?? '');
ws.send(buffer);
}
});
recorderManager.start({
sampleRate: 16000,
numberOfChannels: 1,
format: 'pcm', // 实时传输用PCM,无WAV头,减少传输量
});
实时音量显示
const volumePercent = ref(0);
recorderManager.onFrameRecorded((res) => {
// 将 -96dB ~ 0dB 映射到 0 ~ 100
const normalized = Math.max(0, Math.min(100, ((res.decibel + 96) / 96) * 100));
volumePercent.value = normalized;
});
录音波形可视化
const waveformData = ref<number[]>([]);
recorderManager.onFrameRecorded((res) => {
const buffer = res.frameBuffer ?? uni.base64ToArrayBuffer(res.base64 ?? '');
const int16Array = new Int16Array(buffer);
// 每10个样本抽1个,归一化到[-1, 1]
const samples: number[] = [];
for (let i = 0; i < int16Array.length; i += 10) {
samples.push(int16Array[i] / 32768);
}
// 保留最近100个点
waveformData.value = [...waveformData.value, ...samples].slice(-100);
});
📝 注意事项
-
权限:首次使用会自动请求录音权限。Android 13+ 的通知权限需额外申请(通过插件提供的
requestNotificationPermission()) -
iOS 后台录音:默认进入后台会中断录音并收到
8010006错误。如需后台录音,在manifest.json中配置UIBackgroundModes: ["audio"] -
内存管理:
onFrameRecorded默认约40ms回调一次,iOS 录音数据全程在内存中积累,长时间录音(建议不超过20分钟)注意内存占用 -
采样率:
- 语音场景:推荐 16000Hz(小文件、兼容性好)
- 音乐场景:推荐 44100Hz 或 48000Hz
- iOS 实际采样率由硬件决定(通常48kHz),插件自动处理,用户无需关心
-
帧大小:
- 语音识别推荐:640样本(40ms @ 16kHz)
- 实时显示推荐:320样本(20ms @ 16kHz)
-
格式选择:
wav:带44字节文件头,可直接播放,推荐pcm:纯原始数据,适合实时传输,播放需自行添加头
🔧 兼容性
| 平台 | 支持 | 最低版本 | 核心API |
|---|---|---|---|
| Android | ✅ | API 21(Android 5.0) | AudioRecord |
| iOS | ✅ | iOS 12.0 | AVAudioEngine |
| Web | ❌ | - | - |
| 小程序 | ❌ | - | - |
💬 联系方式
如有问题或建议,欢迎通过以下方式反馈:
- 提交 Issue
- DCloud插件市场评论
- DCloud插件交流群

收藏人数:
购买源码授权版(
试用
赞赏(0)
下载 227
赞赏 5
下载 11911412
赞赏 1914
赞赏
京公网安备:11010802035340号