一、需求
不采用任何额外的插件服务,通过浏览器采集电脑麦克风声音,并实时播放
二、技术思路
1、通过浏览器采集电脑麦克风的声音,recorder.js
①完整示例参考:https://www.jb51.net/article/159849.htm
npm 安装js-audio-recorder插件版本
(1.x版本目前不支持边录边转,0.x版本支持)
1.x版本:https://github.com/2fps/recorder
0.x版本:https://gitee.com/davylw/recorder
1.x版本-在线测试:https://recorder.zhuyuntao.cn/
②如果上面的插件在参考本文做法后还是不能满足你的需求,可以尝试看下这个:https://github.com/xiangyuecn/Recorder
亲测可用,具体demo示例(uniapp)见:后期更新
在线demo测试:https://xiangyuecn.gitee.io/recorder/
③其他录音功能参考(其实我没用到,就当入门学习吧)
https://blog.csdn.net/qiao_1017/article/details/102609243
https://www.cnblogs.com/shihuc/p/9703508.html
2、采集的声音是WAV格式的,通过lamejs转成MP3 格式
参考:https://github.com/zhuker/lamejs
3、通过MQTT,把音频发布到后台
let payload ='';
await mp3Blob.arrayBuffer().then((arraybuffer) => {
payload=new Uint8Array(arraybuffer)//8位无符号整型数组
});
//发送到后台
MQTT.publish('audiostream/'+streamid,payload,{qos:0}, () => {//消息发布
console.log("音频消息发布")
});
三、实现步骤
1、npm安装录音插件npm i js-audio-recorder
//开始录音
startRecord = () => {
this.clearPlay();
const config = this.collectData();
console.log(config)
if (!recorder) {
recorder = new Recorder(config);
recorder.onprocess =(duration)=>{
// this.setState({
// duration: duration.toFixed(5),
// });
// 推荐使用 onprogress
}
recorder.onprogress = (params) => {
// console.log(recorder.duration);
// console.log(recorder.fileSize);
this.setState({
duration: params.duration.toFixed(5),//录音时长
fileSize: params.fileSize,//已录音文件大小(字节)
vol: params.vol.toFixed(2)//录音音量百分比
});
// // 此处控制数据的收集频率
// if (config.compiling) {
// // console.log('音频总数据:', params.data);
// }
}
recorder.onplay = () => {
console.log('%c回调监听,开始播放音频', 'color: #2196f3')
}
recorder.onpauseplay = () => {
console.log('%c回调监听,暂停播放音频', 'color: #2196f3')
}
recorder.onresumeplay = () => {
console.log('%c回调监听,恢复播放音频', 'color: #2196f3')
}
recorder.onstopplay = () => {
console.log('%c回调监听,停止播放音频', 'color: #2196f3')
}
recorder.onplayend = () => {
console.log('%c回调监听,音频已经完成播放', 'color: #2196f3')
// 播放结束后,停止绘制canavs
// this.stopDrawPlay();
}
// 定时获取录音的数据并播放
config.compiling && (playTimer = setInterval(async() => {
if (!recorder) {
return;
}
let newData = recorder.getNextData();
if (!newData.length) {
return;
}
let byteLength = newData[0].byteLength
let buffer = new ArrayBuffer(newData.length * byteLength)
let dataView = new DataView(buffer)
// 数据合并
for (let i = 0, iLen = newData.length; i < iLen; ++i) {
for (let j = 0, jLen = newData[i].byteLength; j < jLen; ++j) {
dataView.setInt8(i * byteLength + j, newData[i].getInt8(j))
}
}
// 将录音数据转成WAV格式
let a = encodeWAV(dataView, config.sampleRate, config.sampleRate, config.numChannels, config.sampleBits)
let blob=new Blob([ a ], { type: 'audio/wav' });//一个专门用于支持文件操作的二进制对象
let payload ='',payloadMp3='';
await blob.arrayBuffer().then((arraybuffer) => {//一个通用的二进制缓冲区,类似数组,但在API和特性上有诸多不同
payload=new Uint8Array(arraybuffer)//不懂为啥
});
// 将录音数据转成mp3格式(使用lamejs转成MP3)
let mp3Blob = this.convertWavToMp30(a);//转成MP3格式
await mp3Blob.arrayBuffer().then((arraybuffer) => {
//这里不清楚为啥要转成8位无符号整型数组
payloadMp3=new Uint8Array(arraybuffer)
});
//0.x版本实现边转边播
// Recorder.playAudio(blob);
//1.x版本实现边转边播-暂未实现
// blob.arrayBuffer().then((arraybuffer) => {
// Player.play(arraybuffer);
// });
//将录音转成MP3,并发送到后台
MQTT.publish('wavstream/1',payloadMp3,{qos:0}, () => {//消息发布
console.log("音频消息发布")
});
//end发送到后台
}, 3000))
} else {
recorder.stop();
}
recorder.start().then(() => {
console.log('开始录音');
}, (error) => {
console.log(`异常了,${error.name}:${error.message}`);
});
// 开始绘制canvas
// this.drawRecord();
}
2、npm安装lamejsnpm install lamejs
//将录音数据转成mp3格式(使用lamejs转成MP3)
convertWavToMp30=(wavDataView)=> {
const wav = lamejs.WavHeader.readHeader(wavDataView);
const samples = new Int16Array(wavDataView.buffer, wav.dataOffset, wav.dataLen / 2);
const { channels, sampleRate } = wav;
const buffer = [];
const mp3enc = new lamejs.Mp3Encoder(channels, sampleRate, 128);
let remaining = samples.length;
const maxSamples = 1152;
for (let i = 0; remaining >= maxSamples; i += maxSamples) {
const mono = samples.subarray(i, i + maxSamples);
const mp3buf = mp3enc.encodeBuffer(mono);
if (mp3buf.length > 0) {
buffer.push(new Int8Array(mp3buf));
}
remaining -= maxSamples;
}
const d = mp3enc.flush();
if (d.length > 0) {
buffer.push(new Int8Array(d));
}
return new Blob(buffer, { type: 'audio/mp3' });
}
四、注意点
根据上述步骤完成后,发布到正式环境,会发现有几个问题:
1、录音功能只能localhost或者127.0.0.1时可用,输入地址时提示浏览器不支持
解决方案:
参考:新版chrome中非https无法打开摄像头、麦克风
https://www.jianshu.com/p/751a9cb93a43?tdsourcetag=s_pctim_aiomsg
步骤:
1、在浏览器地址栏中输入chrome://flags,
2、搜索栏中输入unsafely
3、如下图,将该选项置为Enabled,在输入框中输入需要访问的地址,多个地址使用“,”隔开;如果输入地址后,你测试依然无效,记得加上端口号试试
4、然后点击右下角弹出的Relaunch按钮,自动重启浏览器之后就可以在添加的http地址下调用摄像头和麦克风了。
注意:如果输入地址后,你测试依然无效,记得加上端口号试试
网上其他解决方案(我测试下来好像没啥用,如果上面的方法不能解决,可以试试)
//不知道这几种行不行-没试http转成https
//有人用这个方法解决了,用了代理加上nginx,配置https,就可以了
https://blog.csdn.net/baiyunshi/article/details/98207952?utm_medium=distribute.pc_relevant_bbs_down.none-task--2~all~sobaiduend~default-2.nonecase&depth_1-utm_source=distribute.pc_relevant_bbs_down.none-task--2~all~sobaiduend~default-2.nonecase
https://blog.csdn.net/cswhl/article/details/110183132?utm_medium=distribute.pc_relevant_bbs_down.none-task-blog-baidujs-1.nonecase&depth_1-utm_source=distribute.pc_relevant_bbs_down.none-task-blog-baidujs-1.nonecase
2、录音语速变快。录制完语音后,播放倍率明显加快,导致录音时长变短
解决方案:
我发现录制歌曲时,明明时间相同,但是打印录制的时间总是变短。
然后找到node_modules/js-audio-recorder/dist/recorder.js中的initRecorder函数,把duration延长(这里是通过把缓存变大,使得时间变长,注意:缓冲区大小(12288)必须为0或256到16384之间的2的幂。)
这是我的解决方法,如果大家遇到同样的现象,可以试一下。
网上比较靠谱的其他方法(我没进行测试):
//原因--录制完语音后,播放倍率明显加快,导致录音时长变短
https://github.com/xiangyuecn/Recorder/issues/51
//解决方案--解决设备卡顿时接收到PCM缺失导致音频变短
https://github.com/xiangyuecn/Recorder/commit/cbd272bc725923c915c825983277f8766bdbc0a0