我正在尝试制作一个需要访问用户麦克风的小游戏。我需要能够检查是否连接了麦克风,然后如果连接了,就需要在游戏期间检查通过麦克风传来的声音的音量。我该如何做到这一点?
我正在尝试制作一个需要访问用户麦克风的小游戏。我需要能够检查是否连接了麦克风,然后如果连接了,就需要在游戏期间检查通过麦克风传来的声音的音量。我该如何做到这一点?
在自己摸索一番之后,我觉得提供稍微详细一些的答案可能会帮助其他人。以下代码将根据麦克风音量记录大约从0到100的数字。
navigator.mediaDevices.getUserMedia({
audio: true,
video: true
})
.then(function(stream) {
const audioContext = new AudioContext();
const analyser = audioContext.createAnalyser();
const microphone = audioContext.createMediaStreamSource(stream);
const scriptProcessor = audioContext.createScriptProcessor(2048, 1, 1);
analyser.smoothingTimeConstant = 0.8;
analyser.fftSize = 1024;
microphone.connect(analyser);
analyser.connect(scriptProcessor);
scriptProcessor.connect(audioContext.destination);
scriptProcessor.onaudioprocess = function() {
const array = new Uint8Array(analyser.frequencyBinCount);
analyser.getByteFrequencyData(array);
const arraySum = array.reduce((a, value) => a + value, 0);
const average = arraySum / array.length;
console.log(Math.round(average));
// colorPids(average);
};
})
.catch(function(err) {
/* handle the error */
console.error(err);
});
如果您拥有这个数字 jQuery 来为颜色块设置样式。以下是我提供的示例函数,但那只是简单的部分。只需取消注释 color pids 函数即可。
function colorPids(vol) {
const allPids = [...document.querySelectorAll('.pid')];
const numberOfPidsToColor = Math.round(vol / 10);
const pidsToColor = allPids.slice(0, numberOfPidsToColor);
for (const pid of allPids) {
pid.style.backgroundColor = "#e6e7e8";
}
for (const pid of pidsToColor) {
// console.log(pid[i]);
pid.style.backgroundColor = "#69ce2b";
}
}
为了尽可能详细地回答,我也在下面附上了我的html和css,这样您就可以复制js html和css,如果您希望获得一个可运行的示例。
html:
<div class="pids-wrapper">
<div class="pid"></div>
<div class="pid"></div>
<div class="pid"></div>
<div class="pid"></div>
<div class="pid"></div>
<div class="pid"></div>
<div class="pid"></div>
<div class="pid"></div>
<div class="pid"></div>
<div class="pid"></div>
</div>
CSS:
.pids-wrapper{
width: 100%;
}
.pid{
width: calc(10% - 10px);
height: 10px;
display: inline-block;
margin: 5px;
}
AudioWorklet
的答案 https://dev59.com/OFIG5IYBdhLWcg3w2VMZ - david这里是一个使用setTimeout
代替废弃的createScriptProcessor
函数的简单答案:
以下是使用setTimeout
的示例代码:
(async () => {
let volumeCallback = null;
let volumeInterval = null;
const volumeVisualizer = document.getElementById('volume-visualizer');
const startButton = document.getElementById('start');
const stopButton = document.getElementById('stop');
// Initialize
try {
const audioStream = await navigator.mediaDevices.getUserMedia({
audio: {
echoCancellation: true
}
});
const audioContext = new AudioContext();
const audioSource = audioContext.createMediaStreamSource(audioStream);
const analyser = audioContext.createAnalyser();
analyser.fftSize = 512;
analyser.minDecibels = -127;
analyser.maxDecibels = 0;
analyser.smoothingTimeConstant = 0.4;
audioSource.connect(analyser);
const volumes = new Uint8Array(analyser.frequencyBinCount);
volumeCallback = () => {
analyser.getByteFrequencyData(volumes);
let volumeSum = 0;
for(const volume of volumes)
volumeSum += volume;
const averageVolume = volumeSum / volumes.length;
// Value range: 127 = analyser.maxDecibels - analyser.minDecibels;
volumeVisualizer.style.setProperty('--volume', (averageVolume * 100 / 127) + '%');
};
} catch(e) {
console.error('Failed to initialize volume visualizer, simulating instead...', e);
// Simulation
//TODO remove in production!
let lastVolume = 50;
volumeCallback = () => {
const volume = Math.min(Math.max(Math.random() * 100, 0.8 * lastVolume), 1.2 * lastVolume);
lastVolume = volume;
volumeVisualizer.style.setProperty('--volume', volume + '%');
};
}
// Use
startButton.addEventListener('click', () => {
// Updating every 100ms (should be same as CSS transition speed)
if(volumeCallback !== null && volumeInterval === null)
volumeInterval = setInterval(volumeCallback, 100);
});
stopButton.addEventListener('click', () => {
if(volumeInterval !== null) {
clearInterval(volumeInterval);
volumeInterval = null;
}
});
})();
div {
--volume: 0%;
position: relative;
width: 200px;
height: 20px;
margin: 50px;
background-color: #DDD;
}
div::before {
content: '';
position: absolute;
top: 0;
bottom: 0;
left: 0;
width: var(--volume);
background-color: green;
transition: width 100ms linear;
}
button {
margin-left: 50px;
}
h3 {
margin: 20px;
font-family: sans-serif;
}
<h3><b>NOTE:</b> This is not accurate on stackoverflow, since microphone use is not permitted. It's a simulation instead.</h3>
<div id="volume-visualizer"></div>
<button id="start">Start</button>
<button id="stop">Stop</button>
这也意味着它可以很容易地按需启动和停止。
createScriptProcessor
的答案。但是我还没有理解为什么 minDecibels 和 maxDecibels 以及 averageVolume 的计算,即 (averageVolume * 100 / 127)。这表示什么意思?任何理解这段代码的人请帮帮我。提前感谢你们。 - Shailesh BaverageVolume
只是 volumes
的平均值。为了获取用于显示响度的百分比,需要将 averageVolume
除以最大可能值(偏移量)。要确定这一点,您需要从最大可能值(原始值)maxDecibels
中减去最小可能值(原始值)minDecibels
。请注意,所有音量都是 minDecibels
的偏移量。 - Minding127
作为响度的特定原因吗? - Peter TothAnalyserNode
文档中的getByteFrequencyData()
,minDecibels
和maxDecibels
来选择适合你的数值。 - undefinednavigator.getUserMedia(constraints, successCallback, errorCallback);
这是一个使用getUserMedia函数的示例,它将使您可以访问麦克风。
navigator.getUserMedia = navigator.getUserMedia ||
navigator.webkitGetUserMedia ||
navigator.mozGetUserMedia;
if (navigator.getUserMedia) {
navigator.getUserMedia({ audio: true, video: { width: 1280, height: 720 } },
function(stream) {
console.log("Accessed the Microphone");
},
function(err) {
console.log("The following error occured: " + err.name);
}
);
} else {
console.log("getUserMedia not supported");
}
麦克风是如何连接的
,然后如果已连接,就要检查通过麦克风传来的声音的音量
,我提供了一个演示这一点的存储库(你也提供了)。 - ioneyed简单的麦克风音量表,请参见https://codepen.io/www-0av-com/pen/jxzxEX
已经在2018年进行了检查和工作,包括由Chrome浏览器安全更新引起的错误修复。
HTML
<h3>来自麦克风输入的VU表(getUserMedia API)</h3>
<button onclick="startr();" title="点击开始,因为浏览器的安全性增加,只有在页面手势上启动语音麦克风才能启动">开始</button>
<canvas id="canvas" width="150" height="300" style='background:blue'></canvas>
<br>
点击开始
<div align=left>请查看JS以获取归属信息</div>
CSS
body {
color: #888;
background: #262626;
margin: 0;
padding: 40px;
text-align: center;
font-family: "helvetica Neue", Helvetica, Arial, sans-serif;
}
#canvas {
width: 150px;
height: 100px;
position: absolute;
top: 150px;
left: 45%;
text-align: center;
}
JS(需要 JQuery)
// Courtesy www/0AV.com, LGPL license or as set by forked host, Travis Holliday, https://codepen.io/travisholliday/pen/gyaJk
function startr(){
console.log ("starting...");
navigator.getUserMedia = navigator.getUserMedia ||
navigator.webkitGetUserMedia ||
navigator.mozGetUserMedia;
if (navigator.getUserMedia) {
navigator.getUserMedia({
audio: true
},
function(stream) {
audioContext = new AudioContext();
analyser = audioContext.createAnalyser();
microphone = audioContext.createMediaStreamSource(stream);
javascriptNode = audioContext.createScriptProcessor(2048, 1, 1);
analyser.smoothingTimeConstant = 0.8;
analyser.fftSize = 1024;
microphone.connect(analyser);
analyser.connect(javascriptNode);
javascriptNode.connect(audioContext.destination);
canvasContext = $("#canvas")[0].getContext("2d");
javascriptNode.onaudioprocess = function() {
var array = new Uint8Array(analyser.frequencyBinCount);
analyser.getByteFrequencyData(array);
var values = 0;
var length = array.length;
for (var i = 0; i < length; i++) {
values += (array[i]);
}
var average = values / length;
// console.log(Math.round(average - 40));
canvasContext.clearRect(0, 0, 150, 300);
canvasContext.fillStyle = '#BadA55';
canvasContext.fillRect(0, 300 - average, 150, 300);
canvasContext.fillStyle = '#262626';
canvasContext.font = "48px impact";
canvasContext.fillText(Math.round(average - 40), -2, 300);
// console.log (average);
} // end fn stream
},
function(err) {
console.log("The following error occured: " + err.name)
});
} else {
console.log("getUserMedia not supported");
}
}
audioContext = new (window.AudioContext || window.webkitAudioContext)();
代替audioContext = new AudioContext();
。 - Jan Misker