小程序中使用onFrameRecorded实时显示波形

本文介绍了一种在小程序环境中通过MP3帧记录并计算音频分贝大小的方法。使用js-mp3解码MP3帧为PCM数据,并解决了解码过程中遇到的各种问题,如设置错误的采样率和缓冲区大小不足等。

onFrameRecorded 传过来的是MP3帧,但是,这个帧有的时候是不完整的帧,好像是小程序的性能问题或者之类的其他问题。

使用js-mp3,把MP3帧解码成PCM数据,然后再按照pcm数据,来计算分贝数。

踩坑的地方:

1.famebuffer设置的太小,造成传过来的arrayBuffer里面没有一个完整的mp3帧,结果就是Decode失败。以为是小程序的黑箱问题。

2.js-mp3声称自己不支持mpeg2,感觉心里凉了半截,后来“高坚果”给了一个表格:

原来是采样率设置问题,修改为44.1khz采样率,搞定。

3.“高坚果”提供的Recoder里面的源码是使用getChannelData来计算的,里面返回的是一个Float16Array,但是js-mp3解码出来的pcm,如果转换成Float16Array,会有很多NaN,所以没有使用Recoder里面计算分贝大小的方法。但是这个计算方法是仓促写出来的,所以问题多多,已知问题:会出现突然100的情况;并不是对所有声音都敏感。

4.效率很差。因为中间经过了mp3压缩,然后再解码,再判断分贝大小,所以,效率真的很差。

代码摘抄如下:

 
  1. recordTest: function () {

  2. var ctx = wx.createCanvasContext("firstCanvas")

  3. ctx.draw();

  4. var curr_index = 0;

  5. var reclength = 30; //录音30秒

  6. var step = reclength / app.globalData.windowWidth;

  7. var startTime = new Date();

  8. var powerArr = [];

  9. var ctx_height = 200;

  10.  
  11. var rm = wx.getRecorderManager()

  12. rm.onFrameRecorded((res) => {

  13. const { frameBuffer } = res

  14. var decoder = Mp3.newDecoder(frameBuffer)

  15. if (decoder != null) {

  16. var pcmArrayBuffer = decoder.decode()

  17. var pcmArr = new Int16Array(pcmArrayBuffer)

  18. var size = pcmArr.length

  19.  
  20. var sum = 0;

  21. for (var i = 0; i < size; i++) {

  22. sum += Math.abs(pcmArr[i]);

  23. }

  24. var powerLevel = sum * 500.0 / (size * 16383);

  25. if (powerLevel >= 100) {

  26. powerLevel = 100

  27. }

  28. if (powerLevel <= 5) {

  29. powerLevel = 2

  30. }

  31.  
  32. powerLevel = parseInt(powerLevel)

  33. if(powerArr[curr_index] * 2 < powerLevel && powerLevel == 100)

  34. {

  35. powerLevel = powerArr[curr_index]

  36. }

  37. var bufferDuration = (new Date() - startTime) / 1000; //计算开始了多少秒了

  38. while (bufferDuration > curr_index * step) //每次循环进来不止前进一次

  39. {

  40. powerArr[curr_index] = powerLevel

  41. curr_index++

  42. ctx.setFillStyle("#6E6FAC")

  43. var curr_height = powerLevel / 100 * ctx_height

  44. var curr_y = (ctx_height - curr_height) / 2

  45. ctx.fillRect(curr_index, curr_y, 1, curr_height)

  46. ctx.draw(true)

  47. }

  48. }

  49. })

  50. rm.onStart(() => {

  51. console.log('recorder start')

  52. this.setData({buttonText: "录音中"})

  53. })

  54. rm.onPause(() => {

  55. console.log('recorder pause')

  56. console.log(farms);

  57. })

  58. rm.onStop((res) => {

  59. this.setData({buttonText: "录音结束"})

  60. console.log('recorder stop', res)

  61. console.log("curr_index:" + curr_index)

  62. const { tempFilePath } = res

  63.  
  64. const fs = wx.getFileSystemManager()

  65. // // 从临时文件中读取音频

  66. fs.readFile({

  67. filePath: tempFilePath,

  68. success(res) {

  69. var decoder = Mp3.newDecoder(res.data)

  70. if (decoder == null) {

  71. console.log("decoder error")

  72. }

  73. else {

  74. console.log("decode ok")

  75. var pcmArrayBuffer = decoder.decode()

  76. console.log(pcmArrayBuffer)

  77. }

  78. },

  79. fail(e) {

  80. console.log('read fail')

  81. console.log(e)

  82. }

  83. })

  84.  
  85. })

  86. const options = {

  87. duration: 30000,

  88. sampleRate: 44100,

  89. numberOfChannels: 1,

  90. encodeBitRate: 64000,

  91. format: 'mp3',

  92. frameSize: 1

  93. }

  94. rm.start(options)

  95. }

 

pcm计算分贝大小:https://www.jianshu.com/p/d3745dd23056

js-mp3:https://github.com/soundbus-technologies/js-mp3

在微信小程序中实现录音功能并获取录音的波形图,主要涉及以下几个方面的实现: 1. **录音功能的实现** 微信小程序提供了 `RecorderManager` 接口用于录音功能的实现,开发者可以通过调用 `wx.getRecorderManager()` 获取录音管理器实例。录音的开始、暂停、停止等操作都可以通过该实例进行控制。例如,开始录音的代码如下: ```javascript const recorderManager = wx.getRecorderManager(); recorderManager.start({ format: 'mp3', // 音频格式 duration: 60000, // 录音时长,单位为毫秒 }); ``` 停止录音的代码如下: ```javascript recorderManager.stop(); ``` 录音完成后,可以通过 `onStop` 事件获取录音文件的临时路径,用于后续处理或上传[^3]。 2. **波形图的绘制** 获取录音的波形图是实现音频可视化的重要部分。微信小程序中并没有直接提供波形图的绘制接口,因此需要通过分析音频数据并使用 `Canvas` 绘制波形图。常见的做法是利用录音时的音频数据流,通过 `onFrameRecorded` 事件获取音频的 PCM 数据,然后对这些数据进行处理,提取波形信息并绘制到 `Canvas` 上。例如: ```javascript recorderManager.onFrameRecorded(res => { const { frameBuffer } = res; // 处理 frameBuffer 数据,提取波形信息 drawWaveform(frameBuffer); }); ``` 绘制波形图的具体实现需要对音频数据进行采样、归一化等处理,然后根据采样值绘制线条或矩形。以下是一个简单的波形图绘制函数示例: ```javascript function drawWaveform(data) { const ctx = wx.createCanvasContext('waveformCanvas'); const width = 300; const height = 100; const step = Math.floor(data.length / width); ctx.beginPath(); for (let i = 0; i < width; i++) { const value = data[i * step] / 256; const y = height * (1 - value); if (i === 0) { ctx.moveTo(i, y); } else { ctx.lineTo(i, y); } } ctx.setStrokeStyle('#FF0000'); ctx.stroke(); ctx.draw(); } ``` 这段代码通过采样音频数据并绘制线条来生成波形图,开发者可以根据需求调整采样率和绘制方式[^1]。 3. **音频的播放与试听** 微信小程序提供了 `InnerAudioContext` 接口用于播放音频文件。通过调用 `wx.createInnerAudioContext()` 获取音频播放实例,并设置音频文件的路径,即可实现音频的播放。例如: ```javascript const audio = wx.createInnerAudioContext(); audio.src = this.data.file.tempFilePath; audio.autoplay = true; ``` 播放音频时,还可以通过监听播放进度事件,实时更新波形图的显示效果,从而实现音频播放时的动态波形图效果[^3]。 4. **音频的上传与存储** 录音完成后,可以将录音文件上传至服务器。微信小程序提供了 `wx.uploadFile` 接口用于上传文件。上传录音文件的代码如下: ```javascript wx.uploadFile({ url: 'https://example.com/upload', filePath: this.data.file.tempFilePath, name: 'file', success(res) { console.log('上传成功'); }, fail(err) { console.error('上传失败', err); } }); ``` 上传完成后,服务器可以对录音文件进行存储或进一步处理[^2]。 5. **UI 样式的设计** 在微信小程序中,可以通过 CSS 样式对录音功能的 UI 进行美化。例如,录音按钮和波形图的样式可以通过以下代码实现: ```css .audio { width: 100%; height: 112rpx; background: rgba(230, 67, 64, 0.49); border-radius: 8rpx; margin: 10rpx 0; } ``` 这段代码定义了录音控件的基本样式,包括宽度、高度、背景颜色和边框圆角等。开发者可以根据需求进一步调整样式以提升用户体验[^4]。 --- ###
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值