- 小程序的 RecorderManager 可以支持设置 采样位数 sampleBits吗?
小程序的 RecorderManager 可以支持设置 采样位数 sampleBits吗? 查看文档发现 目前的 小程序 RecorderManager Api 里面支持设置采样率 sampleRate 测试,: 1.开始录音设置参数 recorderManager.start( { sampleRate: 16000, numberOfChannels: 1, encodeBitRate: 24000, format: 'wav' } ) 2. 下载录制的录音,检测文件发现采样位数 为16bits 疑问: 采样位数会根据设备不同或者设备性能不同有不同的变化吗?
2023-09-07 - 小程序录音实时波形图
首先,做这个不需要把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({ voiceLine: new Array(...uint8Array) }) }) const options = { duration: 40000, numberOfChannels: 1, format: 'mp3', frameSize: 0.01, //这里设置很小,就会只取一帧就触发onFrameRecorded事件 // sampleRate: 44100, encodeBitRate: 16000, } recorderManager.start(options) 页面wxml:引入组件,传递数据 组件voice.wxml: 组件voice.wxss: .canvas { position: fixed; top: 0; left: 10px; right: 60; bottom: 0; width: 300px; height: 100%; } 组件js: var voiceLine = []; var MaxValue = 0; Component({ /** * 组件的属性列表 */ properties: { voice: Array }, /** * 组件的初始数据 */ data: { }, lifetimes: { // 组件刚刚被创建时执行 attached() { wx.createSelectorQuery().in(this) .select('#canvas') .fields({ node: true, size: true, }) .exec(this.init.bind(this)) }, //删除该组件绑定的所有事件 detached() { } }, pageLifetimes: { show: function () { }, hide: function () { // 页面被隐藏 }, resize: function (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(0, 0, 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(0, 0, 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 - 关于录音接口PCM格式文件编码方式的文档说明需要细化补充
https://developers.weixin.qq.com/miniprogram/dev/api/media/recorder/RecorderManager.start.html 该接口以PCM格式储存的数据编码方式并不是通常按照码率采样率确定的,而是低八位在前,高八位在后的十六位编码,由于文档未进行说明,导致按照常规编码方式进行无法读取数据,耗费大量时间探索编码方式,建议在文档中添加相关说明方便开发者使用。
2021-09-13