更新记录

1.2.0(2026-03-16)

  • 新增录音中断自动检测:麦克风被抢占或来电时通过 onError 回调通知,不再静默停止
  • Android 新增来电检测(READ_PHONE_STATE 权限可选,无权限自动降级为看门狗)
  • iOS 新增系统中断监听(来电、其他应用抢占时立即回调)
  • 录音与播放共存:Android 移除 AudioFocus 管理,iOS 切换为 playAndRecord 模式
  • 错误码扩展为 7 个,语义更准确,Android/iOS 两端统一
  • 修复 iOS onStart 回调时机(引擎启动成功后才触发)
  • 修复 iOS tempFilePath 补全 file:// 前缀,与 Android 一致

1.1.0(2026-03-10)

1、Android 新增录音输出目录配置:

  • RecorderOptions.androidOutputScopeexternal | internal
  • 默认值改为 external(外部应用专属缓存目录)
  • 外部目录不可用时自动回退到内部缓存目录

2、Android 新增录音通知增强能力(可选) RecorderOptions 新增字段:

  • showNotification(默认 false)
  • notificationName
  • notificationTextshowNotification=true 时,录音期间显示常驻通知,可增强后台/息屏存活率 当 showNotification=false 时,保持原有录音逻辑不变 Android 清单新增 android.permission.POST_NOTIFICATIONS

3、新增事件取消监听 API,Android/iOS 统一支持:

  • offStart
  • offStop
  • offFrameRecorded
  • offError
  • offPause
  • offResume

1.0.0(2026-02-12)

首个正式完善发布版本 实现实时PCM音频流回调,支持边录边获取原始音频数据 支持实时分贝监测,帧回调中直接返回当前音量值 提供完整的录音控制:开始、停止、暂停、恢复、取消 Android/iOS双端适配,统一API,纯UTS实现无需原生代码

查看更多

平台兼容性

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音频流录音插件

uni-app Platform

支持实时PCM帧回调的uni-app录音插件,提供流式音频数据处理能力,适用于语音识别、实时转写、波形显示等场景。纯UTS实现,Android/iOS双端适配。

✨ 核心特性

  • 实时PCM音频流 - 边录边获取原始PCM数据,延迟低至40ms
  • 实时分贝监测 - RMS算法计算,实时返回音量分贝值
  • 流式数据处理 - 支持边录边传、边录边识别等流式场景
  • WAV文件保存 - 自动保存为标准WAV格式,支持file://直接上传
  • Android/iOS - 双端适配,统一API,透明处理平台差异
  • 暂停/恢复 - 支持录音过程中暂停和恢复
  • 录音与播放共存 - 录音期间可同时播放音频,互不干扰
  • 中断自动检测 - 多层看门狗机制,来电/麦克风被抢占时立即回调通知
  • 突破录音时长限制 - 无系统默认60秒/10分钟限制,支持长时间持续录音
  • 纯UTS实现 - 无需原生代码,开箱即用

📦 安装

将插件目录复制到项目的 uni_modules 目录下即可。

your-project/
  └── uni_modules/
      └── dh-recorder/

🔐 权限说明

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',
});

// 停止录音
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' 录音格式:wavpcm
showNotification boolean false 仅Android:显示常驻通知,增强后台/息屏存活率
notificationName string 应用名称 仅Android:通知标题
notificationText string 正在录音中,不要关闭应用 仅Android:通知内容
androidOutputScope string 'external' 仅Android:文件输出目录,external(外部应用缓存)或 internal(内部私有)

帧大小(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)直接传输,可直接使用 DataViewInt16Array 等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端:

  • 使用 AudioRecord API 进行PCM录音,独立线程持续读取音频数据
  • ArrayBuffer 无法直接跨 JS 边界传输,采用 base64 编码传输
  • 数据流:AudioRecord → ByteBuffer → ByteArray → Base64 → JS
  • 暂停时继续 read() 并丢弃数据,防止内部缓冲区积压导致恢复后数据异常

iOS端:

  • 使用 AVAudioEngine + installTap 实时获取音频,系统推送回调
  • Float32 → Int16 转换,兼容标准PCM格式
  • AVAudioSession.CategoryplayAndRecord,支持录音与播放同时进行
  • 采样率自动适配:硬件实际采样率通常48kHz,插件自动降采样到配置值(如16kHz)用于回调,文件保存使用实际采样率保证音质
  • ArrayBuffer 可直接跨 JS 边界传输,无需编码

中断检测机制

插件采用多层机制,确保各种场景下都能及时通知:

层次 Android iOS 响应速度
直接API检测 AudioRecord.read() 返回错误码 AVAudioEngine.start() 抛出异常 立即
电话监听 TelephonyManager.PhoneStateListener AVAudioSession.interruptionNotification 立即
首帧超时 1.5秒内无首帧 → 报错 1.5秒内无首帧 → 报错 1.5秒
帧超时看门狗 3秒无音频帧 → 报错 3秒无音频帧 → 报错 ≤3秒

分贝计算算法

基于RMS(均方根)算法:

  1. 将Int16样本归一化到 [-1, 1]
  2. 计算RMS:√(Σsample² / N)
  3. 转换为分贝:dB = 20 × log₁₀(RMS)

范围约 -96dB(静音)到 0dB(满刻度)。

数据格式

统一输出 16位PCM(小端序,Int16):

  • 采样值范围:-32768 到 32767
  • 字节序:Little Endian
  • 数据类型:ArrayBuffer(可用 new Int16Array(buffer) 访问)

📝 注意事项

  1. 权限:首次使用会自动请求录音权限。Android 13+ 的通知权限需额外申请(通过插件提供的 requestNotificationPermission()

  2. iOS 后台录音:默认进入后台会中断录音并收到 8010006 错误。如需后台录音,在 manifest.json 中配置 UIBackgroundModes: ["audio"]

  3. 内存管理onFrameRecorded 默认约40ms回调一次,iOS 录音数据全程在内存中积累,长时间录音(建议不超过20分钟)注意内存占用

  4. 采样率

    • 语音场景:推荐 16000Hz(小文件、兼容性好)
    • 音乐场景:推荐 44100Hz 或 48000Hz
    • iOS 实际采样率由硬件决定(通常48kHz),插件自动处理,用户无需关心
  5. 帧大小

    • 语音识别推荐:640样本(40ms @ 16kHz)
    • 实时显示推荐:320样本(20ms @ 16kHz)
  6. 格式选择

    • wav:带44字节文件头,可直接播放,推荐
    • pcm:纯原始数据,适合实时传输,播放需自行添加头

🔧 兼容性

平台 支持 最低版本 核心API
Android API 21(Android 5.0) AudioRecord
iOS iOS 12.0 AVAudioEngine
Web - -
小程序 - -

💬 联系方式

如有问题或建议,欢迎通过以下方式反馈:

  • 提交 Issue
  • DCloud插件市场评论
  • DCloud插件交流群

隐私、权限声明

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

麦克风权限

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

插件会访问麦克风进行录音

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