评论

小程序录音实时波形图

小程序录音实时波形图,不需要mp3转pcm

首先,做这个不需要MP3转pcm。

结果就是,转了pcm也不知道怎么出波形。搞了我好几天。。。

但是微信现在不需要引入js-mp3库就可以转,仅仅记录一下,代码如下:

var audioCtx = wx.createWebAudioContext()
audioCtx.decodeAudioData(frameBuffer, audioBuffer => {
 let float32Array = audioBuffer.getChannelData(0)
...}

AudioContext.createAnalyser(),这是浏览器的接口,微信的WebAudioContext暂时没有这个。

页面js:

const that = this;
    var recorderManager = wx.getRecorderManager()
    //把frameBuffer转一下,再转成普通Array,传给voice组件
    recorderManager.onFrameRecorded((res) => {
      const {
        frameBuffer
      } = res
      let uint8Array = new Uint8Array(frameBuffer)
      that.setData({
        voiceLinenew Array(...uint8Array)
      })
    })


    const options = {
      duration40000,
      numberOfChannels1,
      format'mp3',
      frameSize0.01, //这里设置很小,就会只取一帧就触发onFrameRecorded事件
      // sampleRate: 44100,
      encodeBitRate16000,
    }


    recorderManager.start(options)


页面wxml:引入组件,传递数据

组件voice.wxml:

组件voice.wxss:

.canvas {
  position: fixed;
  top0;
  left10px;
  right60;
  bottom0;
  width300px;
  height100%;
}


组件js:

var voiceLine = [];
var MaxValue = 0;
Component({
  /**
   * 组件的属性列表
   */
  properties: {
    voiceArray
  },


  /**
   * 组件的初始数据
   */
  data: {


  },
  lifetimes: {
    // 组件刚刚被创建时执行
    attached() {
      wx.createSelectorQuery().in(this)
        .select('#canvas')
        .fields({
          nodetrue,
          sizetrue,
        })
        .exec(this.init.bind(this))
    },
    //删除该组件绑定的所有事件
    detached() {


    }
  },
  pageLifetimes: {
    showfunction () {


    },
    hidefunction () {
      // 页面被隐藏
    },
    resizefunction (size{
      // 页面尺寸变化
    }
  },
  observers: {
    'voice'function (voice{
      if (!voice instanceof Array || voice.length === 0) {
        return;
      }


      // 显示单一波形,调试用
//解开注释可以查看每个波形,慢慢找规律
      // this.renderVoice(voice);
      // return;


      let voiceLen = voice.length,
        plantValue = 0//常见数据
        serialValueNum = 0 //连续计数器


      for (var i = 0; i < voiceLen; i++) {
        //连续9个表示该帧空了
        if (serialValueNum > 9) {
          console.log('总长:', voiceLen, '常见数据:', plantValue, '干了:', serialValueNum)
          if (voiceLine.length > 60) {
            voiceLine.shift()
          }
          voiceLine.push(0)
          this.renderCanvas();
          return;
        }
        if (plantValue != voice[i]) {
          plantValue = voice[i]
          serialValueNum = 0
        } else {
          serialValueNum++;
        }
      }


      // 按长度显示
      let middleNum = voice.length
      if (middleNum > MaxValue) {
        MaxValue = middleNum
        console.log('MaxValue:', MaxValue);
      }
      //长度<100抛弃数据
      if (middleNum < 100) {
        console.log('长度<100抛弃数据:', middleNum, voice);
        middleNum = 100
      }
      if (voiceLine.length > 60) {
        voiceLine.shift()
      }
      voiceLine.push(middleNum)


      this.renderCanvas();
    },
  },
  /**
   * 组件的方法列表
   */
  methods: {
    init(res) {
      const width = res[0].width
      const height = res[0].height


      const canvas = res[0].node
      const ctx = canvas.getContext('2d')


      const dpr = wx.getSystemInfoSync().pixelRatio
      canvas.width = width * dpr
      canvas.height = height * dpr
      ctx.scale(dpr, dpr)


      this.ctx = ctx
      this.width = width
      this.height = height


    },


    renderVoice(voiceLine) {
      if (typeof this.ctx === 'undefined') {
        return
      }
      const width = this.width
      const height = this.height
      const ctx = this.ctx


      ctx.clearRect(00, width, height)
      var len = voiceLine.length,
        barHeight = 1,
        q = 0,
        left = 0


      let plantValue = 0//常见数据
        serialValueNum = 0 //连续计数器
      console.log(voiceLine);
      ctx.strokeStyle = 'blue'
      for (var i = 0; i < len; i++) {
        if (i == len - 1 && serialValueNum > 9) { //连续5个很罕见
          console.log('总长:', len, '常见数据:', plantValue, '干了:', serialValueNum)
        }
        if (plantValue != voiceLine[i]) {
          plantValue = voiceLine[i]
          serialValueNum = 0
        } else {
          serialValueNum++;
        }


        q = i % height
        left = parseInt(i / height) * 60 + 60
        barHeight = (voiceLine[i] / 255) * 60
        // 绘制向上的线条
        ctx.beginPath();
        ctx.moveTo(left, q);
        ctx.lineTo(left + barHeight, q);
        ctx.stroke();
      }
    },


    renderCanvas() {
      if (typeof this.ctx === 'undefined') {
        return
      }


      const width = this.width
      const height = this.height
      const ctx = this.ctx


      ctx.clearRect(00, width, height)
      var len = voiceLine.length,
        barHeight = 1,
        t_arr = []


      let now_V,
        min = 100,
        max = MaxValue - min;


      ctx.strokeStyle = 'blue'
      ctx.lineWidth = 3;
      for (var i = 0; i < len; i++) {
        now_V = voiceLine[i] == 0 ? 0 : voiceLine[i] - min
        if (now_V == 0) {
          ctx.beginPath();
          ctx.moveTo(3, i * 8 - 4);
          ctx.lineTo(3, i * 8 + 4);
          ctx.stroke();
          continue;
        }
        t_arr.push(now_V)
        barHeight = (now_V / max) * 60
        // 绘制向上的线条
        ctx.beginPath();
        ctx.moveTo(0, i * 8);
        ctx.lineTo(barHeight, i * 8);
        ctx.stroke();
      }


      t_arr.sort((a, b) => a - b);
      console.log('Min', t_arr[0], 'Max', t_arr[t_arr.length - 1])
    }
  }
})


原理:说白了,传过来的一串数据,当有大声音时,会突然变长。

当完全静音时,会连续出现85或170这个值。如果是转Int8Array,会有-86这个值。

至于为什么,上面代码解开注释可以观察单个波形。

最后成品的案例在:“艺匠人”小程序->新建作品。

感兴趣的童鞋搜一下玩玩吧~

最后一次编辑于  2023-02-07  
点赞 2
收藏
评论

5 个评论

  • Tuna
    Tuna
    2023-10-24

    这个波形图是假的吗,我没发出声音,也有波段诶

    2023-10-24
    赞同 1
    回复 6
    • 李轩任
      李轩任
      2023-10-30
      那你玩一下“艺匠人”,然后再回来仔细试试代码?
      2023-10-30
      回复
    • Tuna
      Tuna
      2023-10-31回复李轩任
      我去试玩过,确实不实时,onFrameRecorded的数据直接用是不是不太行,代码里0.01的size感觉也怪怪的😂看不懂
      2023-10-31
      回复
    • 李轩任
      李轩任
      2023-11-01回复Tuna
      怎么免费给你公开了个demo自己还添堵呢?
      2023-11-01
      回复
    • Tuna
      Tuna
      2023-11-03回复李轩任
      ???你的demo效果不对呀,不能说?
      2023-11-03
      1
      回复
    • 李轩任
      李轩任
      2023-11-20回复Tuna
      你玩了“艺匠人”小程序?新建一个作品,发语音,不实时?你保持安静,然后大喊一声,不会有个波峰出来?
      2023-11-20
      回复
    查看更多(1)
  • 糖
    2023-10-10

    请问, 录制pcm能使用吗,


    2023-10-10
    赞同
    回复 1
    • 李轩任
      李轩任
      2023-10-11
      你把代码里面的mp3换成pcm试试呗
      2023-10-11
      回复
  • Seven
    Seven
    2023-03-09
    audioCtx.decodeAudioData(frameBuffer),这里就会报错呀,decodeAudioData会报错:data error。
    我看你下边是直接使用的 voiceLine 这个数据,并没有使用decodeAudioData 解码that.setData({
     voiceLine: new Array(...uint8Array)
    })
    
    2023-03-09
    赞同
    回复 1
    • 李轩任
      李轩任
      2023-03-15
      但是微信现在不需要引入js-mp3库就可以转,仅仅记录一下,代码如下:
      仅仅记录一下。
      这玩意没用的。
      2023-03-15
      回复
  • J.H
    J.H
    2022-11-16
    请问frameBuffer每次传的数据本身就是8bit编码数据吗,frameSize如何跟时间或者采样点个数对应上,每次的数据时间上的长度是一致的吗
    
    


    2022-11-16
    赞同
    回复 1
    • 李轩任
      李轩任
      2022-11-17
      1.第一段代码,页面js:里面有个options,修改参数frameBuffer就变了。
      2.输出的是用这个“new Uint8Array”转换成可以用的数据。
      3.最好运行一下上面的代码,光看是看不出所以然的。
      2022-11-17
      回复
  • 大山
    大山
    2022-11-15
    wx都搜不到createWebAudioContextzh这个方法
    
    2022-11-15
    赞同
    回复 1
登录 后发表内容