首先,做这个不需要把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这个值。
至于为什么,上面代码解开注释可以观察单个波形。
最后成品的案例在:“艺匠人”小程序->新建作品。
感兴趣的童鞋搜一下玩玩吧~
这个波形图是假的吗,我没发出声音,也有波段诶
请问, 录制pcm能使用吗,
audioCtx.decodeAudioData(frameBuffer),这里就会报错呀,decodeAudioData会报错:data error。 我看你下边是直接使用的 voiceLine 这个数据,并没有使用decodeAudioData 解码that.setData({ voiceLine: new Array(...uint8Array) })
仅仅记录一下。
这玩意没用的。
2.输出的是用这个“new Uint8Array”转换成可以用的数据。
3.最好运行一下上面的代码,光看是看不出所以然的。