TIP
在一些在线会议软件、通讯软件中尤其是头一次使用麦克风设备,当电脑中存在多个麦克风设备的时候,往往初始的设备有可能并不是我们想使用的,或者有时候设备故障的时候也需要通过音量的检测来进行判断。我们可以通过音量条的显示来反馈当前设备是否正常工作。
前言
大家好,我是小鑫同学。一位从事过Android开发、混合开发,现在长期从事前端开发的编程爱好者,我觉得在编程之路上最重要的是知识的分享,所谓三人行必有我师。所以我开始在社区持续输出我所了解到、学习到、工作中遇到的各种编程知识,欢迎有想法、有同感的伙伴加我fe-xiaoxin微信交流~
写作背景:
在一些在线会议软件、通讯软件中尤其是头一次使用麦克风设备,当电脑中存在多个麦克风设备的时候,往往初始的设备有可能并不是我们想使用的,或者有时候设备故障的时候也需要通过音量的检测来进行判断。我们可以通过音量条的显示来反馈当前设备是否正常工作。
在 HTML5 中提供的 AudioContext 对象用来专门处理音频,通过上下文创建的各种 AudioNode 相互链接。
模板定义:
提供一个启动麦克风的按钮事件和一个用来显示音量的 div 元素,通过动态改变元素的宽度来实时显示。
<template>
<h2>音量检测</h2>
<a-divider />
<a-button @click="openMicrophone">打开麦克风</a-button>
<div
:style="{
width: `${audioLevel}px`,
height: '10px',
background: '#8dc63f',
marginTop: '20px',
}"
></div>
</template>
<template>
<h2>音量检测</h2>
<a-divider />
<a-button @click="openMicrophone">打开麦克风</a-button>
<div
:style="{
width: `${audioLevel}px`,
height: '10px',
background: '#8dc63f',
marginTop: '20px',
}"
></div>
</template>
启动麦克风和检测:
实例化 SoundMeter ,并注册监听来回调音量数据:
soundMeter.value = new SoundMeter(
new window.AudioContext(),
(instant: number) => {
audioLevel.value = Number(instant.toFixed(2)) * 348 + 1;
}
);
soundMeter.value = new SoundMeter(
new window.AudioContext(),
(instant: number) => {
audioLevel.value = Number(instant.toFixed(2)) * 348 + 1;
}
);
通过 getUserMedia 设置允许音频的约束来启动麦克风,并对接检测工具:
const constraints: MediaStreamConstraints = { audio: true, video: false };
navigator.mediaDevices
.getUserMedia(constraints)
.then((stream: MediaStream) => {
soundMeter.value?.connectToSource(stream);
})
.catch(handleError);
const constraints: MediaStreamConstraints = { audio: true, video: false };
navigator.mediaDevices
.getUserMedia(constraints)
.then((stream: MediaStream) => {
soundMeter.value?.connectToSource(stream);
})
.catch(handleError);
当组件卸载后我们需要停掉检测音量的工具类:
onUnmounted(() => {
soundMeter.value?.stop();
});
onUnmounted(() => {
soundMeter.value?.stop();
});
检测音量工具类:
在工具类中通过 onaudioprocess 来实时回调音量的数据,通过计算来得到一个适用于显示的数值。
export default class SoundMeter {
mic: MediaStreamAudioSourceNode | undefined;
script: ScriptProcessorNode;
context: AudioContext;
constructor(context: AudioContext, onAudioProcess: Function) {
this.context = context;
this.script = context.createScriptProcessor(2048, 1, 1);
this.script.onaudioprocess = function (event) {
let input = event.inputBuffer.getChannelData(0) || 0;
let sum = 0.0;
for (let i = 0; i < input.length; ++i) {
sum += input[i] * input[i];
}
onAudioProcess && onAudioProcess(Math.sqrt(sum / input.length));
};
}
connectToSource(stream: MediaStream) {
this.mic = this.context.createMediaStreamSource(stream);
this.mic.connect(this.script);
// necessary to make sample run, but should not be.
this.script.connect(this.context.destination);
}
stop() {
this.mic && this.mic.disconnect();
this.script && this.script.disconnect();
}
}
export default class SoundMeter {
mic: MediaStreamAudioSourceNode | undefined;
script: ScriptProcessorNode;
context: AudioContext;
constructor(context: AudioContext, onAudioProcess: Function) {
this.context = context;
this.script = context.createScriptProcessor(2048, 1, 1);
this.script.onaudioprocess = function (event) {
let input = event.inputBuffer.getChannelData(0) || 0;
let sum = 0.0;
for (let i = 0; i < input.length; ++i) {
sum += input[i] * input[i];
}
onAudioProcess && onAudioProcess(Math.sqrt(sum / input.length));
};
}
connectToSource(stream: MediaStream) {
this.mic = this.context.createMediaStreamSource(stream);
this.mic.connect(this.script);
// necessary to make sample run, but should not be.
this.script.connect(this.context.destination);
}
stop() {
this.mic && this.mic.disconnect();
this.script && this.script.disconnect();
}
}
结语:
这一篇通过一个案例完成了音量的显示和检测音量的变化,明天继续学~
如果看完觉得有收获,欢迎点赞、评论、分享支持一下。你的支持和肯定,是我坚持写作的动力~
最后可以关注我@小鑫同学。欢迎点此扫码加我微信fe-xiaoxin交流,共同进步(还可以帮你fix🐛)~