如何使用 AudioWorklet 获取麦克风音量

我有兴趣在 Javscript 中连续读取麦克风音量。StackOverflow 上的许多现有解决方案(请参阅此处此处此处)使用BaseAudioContext.createScriptProcessor()自 2014 年起已弃用的解决方案。

我想在我的项目中使用面向未来的代码,所以任何人都可以分享一个如何使用新的读取麦克风音量的现代最小示例AudioWorkletNode吗?


大话西游666
浏览 418回答 1
1回答

RISEBY

让我们看一下需要了解的一些要点:这一切的改变都是为了避免延迟,创建了自己的线程,也就是说,运行在音频渲染线程(AudioWorkletGlobalScope)上。这种新的实现方式有两部分:AudioWorkletProcessor 和 AudioWorkletNode。AudioWorkletNode 至少需要两件事:一个 AudioContext 对象和作为字符串的处理器名称。处理器定义可以通过新的 Audio Worklet 对象的 addModule () 调用来加载和注册。包括 AudioWorklet 在内的 Worklet API 仅在安全上下文中可用。在这种情况下,我们可以使用 localhost,但有必要知道这一点。我们需要至少从 AudioWorkletProcessor 向 AudioWorkletNode 传达当前值,或者在本例中是音量以对其进行任何操作。有必要访问您的计算机的麦克风navigator.getUserMedia/** Declare a context for AudioContext object */let audioContext// Creating a list of colors for ledconst ledColor = [&nbsp; &nbsp; "#064dac",&nbsp; &nbsp; "#064dac",&nbsp; &nbsp; "#064dac",&nbsp; &nbsp; "#06ac5b",&nbsp; &nbsp; "#15ac06",&nbsp; &nbsp; "#4bac06",&nbsp; &nbsp; "#80ac06",&nbsp; &nbsp; "#acaa06",&nbsp; &nbsp; "#ac8b06",&nbsp; &nbsp; "#ac5506",]let isFirtsClick = truelet listeing = falsefunction onMicrophoneDenied() {&nbsp; &nbsp; console.log('denied')}/**&nbsp;* This method updates leds&nbsp;* depending the volume detected&nbsp;*&nbsp;&nbsp;* @param {Float} vol value of volume detected from microphone&nbsp;*/function leds(vol) {&nbsp; &nbsp; let leds = [...document.getElementsByClassName('led')]&nbsp; &nbsp; let range = leds.slice(0, Math.round(vol))&nbsp; &nbsp; for (var i = 0; i < leds.length; i++) {&nbsp; &nbsp; &nbsp; &nbsp; leds[i].style.boxShadow = "-2px -2px 4px 0px #a7a7a73d, 2px 2px 4px 0px #0a0a0e5e";&nbsp; &nbsp; &nbsp; &nbsp; leds[i].style.height = "22px"&nbsp; &nbsp; }&nbsp; &nbsp; for (var i = 0; i < range.length; i++) {&nbsp; &nbsp; &nbsp; &nbsp; range[i].style.boxShadow = `5px 2px 5px 0px #0a0a0e5e inset, -2px -2px 1px 0px #a7a7a73d inset, -2px -2px 30px 0px ${ledColor[i]} inset`;&nbsp; &nbsp; &nbsp; &nbsp; range[i].style.height = "25px"&nbsp; &nbsp; }}/**&nbsp;* Method used to create a comunication between&nbsp;* AudioWorkletNode, Microphone and AudioWorkletProcessor&nbsp;*&nbsp;&nbsp;* @param {MediaStream} stream If user grant access to microphone, this gives you&nbsp;* a MediaStream object necessary in this implementation&nbsp;*/async function onMicrophoneGranted(stream) {&nbsp; &nbsp; // Instanciate just in the first time&nbsp; &nbsp; // when button is pressed&nbsp; &nbsp; if (isFirtsClick) {&nbsp; &nbsp; &nbsp; &nbsp; // Initialize AudioContext object&nbsp; &nbsp; &nbsp; &nbsp; audioContext = new AudioContext()&nbsp; &nbsp; &nbsp; &nbsp; // Adding an AudioWorkletProcessor&nbsp; &nbsp; &nbsp; &nbsp; // from another script with addModule method&nbsp; &nbsp; &nbsp; &nbsp; await audioContext.audioWorklet.addModule('vumeter-processor.js')&nbsp; &nbsp; &nbsp; &nbsp; // Creating a MediaStreamSource object&nbsp; &nbsp; &nbsp; &nbsp; // and sending a MediaStream object granted by&nbsp;&nbsp; &nbsp; &nbsp; &nbsp; // the user&nbsp; &nbsp; &nbsp; &nbsp; let microphone = audioContext.createMediaStreamSource(stream)&nbsp; &nbsp; &nbsp; &nbsp; // Creating AudioWorkletNode sending&nbsp; &nbsp; &nbsp; &nbsp; // context and name of processor registered&nbsp; &nbsp; &nbsp; &nbsp; // in vumeter-processor.js&nbsp; &nbsp; &nbsp; &nbsp; const node = new AudioWorkletNode(audioContext, 'vumeter')&nbsp; &nbsp; &nbsp; &nbsp; // Listing any message from AudioWorkletProcessor in its&nbsp; &nbsp; &nbsp; &nbsp; // process method here where you can know&nbsp; &nbsp; &nbsp; &nbsp; // the volume level&nbsp; &nbsp; &nbsp; &nbsp; node.port.onmessage&nbsp; = event => {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; let _volume = 0&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; let _sensibility = 5 // Just to add any sensibility to our ecuation&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; if (event.data.volume)&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; _volume = event.data.volume;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; leds((_volume * 100) / _sensibility)&nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; &nbsp; &nbsp; // Now this is the way to&nbsp; &nbsp; &nbsp; &nbsp; // connect our microphone to&nbsp; &nbsp; &nbsp; &nbsp; // the AudioWorkletNode and output from audioContext&nbsp; &nbsp; &nbsp; &nbsp; microphone.connect(node).connect(audioContext.destination)&nbsp; &nbsp; &nbsp; &nbsp; isFirtsClick = false&nbsp; &nbsp; }&nbsp; &nbsp; // Just to know if button is on or off&nbsp; &nbsp; // and stop or resume the microphone listening&nbsp; &nbsp; let audioButton = document.getElementsByClassName('audio-control')[0]&nbsp; &nbsp; if (listeing) {&nbsp; &nbsp; &nbsp; &nbsp; audioContext.suspend()&nbsp; &nbsp; &nbsp; &nbsp; audioButton.style.boxShadow = "-2px -2px 4px 0px #a7a7a73d, 2px 2px 4px 0px #0a0a0e5e"&nbsp; &nbsp; &nbsp; &nbsp; audioButton.style.fontSize = "25px"&nbsp; &nbsp; } else {&nbsp; &nbsp; &nbsp; &nbsp; audioContext.resume()&nbsp; &nbsp; &nbsp; &nbsp; audioButton.style.boxShadow = "5px 2px 5px 0px #0a0a0e5e inset, -2px -2px 1px 0px #a7a7a73d inset"&nbsp; &nbsp; &nbsp; &nbsp; audioButton.style.fontSize = "24px"&nbsp; &nbsp; }&nbsp; &nbsp; listeing = !listeing}function activeSound () {&nbsp; &nbsp; // Tell user that this&nbsp; &nbsp; // program wants to use&nbsp; &nbsp; // the microphone&nbsp; &nbsp; try {&nbsp; &nbsp; &nbsp; &nbsp; navigator.getUserMedia = navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia;&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;&nbsp; &nbsp; &nbsp; &nbsp; navigator.getUserMedia(&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; { audio: true, video: false },&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; onMicrophoneGranted,&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; onMicrophoneDenied&nbsp; &nbsp; &nbsp; &nbsp; );&nbsp; &nbsp; } catch(e) {&nbsp; &nbsp; &nbsp; &nbsp; alert(e)&nbsp; &nbsp; }}document.getElementById('audio').addEventListener('click', () => {&nbsp; &nbsp; activeSound()})在本节中,您可以知道麦克风的音量:const SMOOTHING_FACTOR = 0.8;const MINIMUM_VALUE = 0.00001;// This is the way to register an AudioWorkletProcessor// it's necessary to declare a name, in this case// the name is "vumeter"registerProcessor('vumeter', class extends AudioWorkletProcessor {&nbsp; _volume&nbsp; _updateIntervalInMS&nbsp; _nextUpdateFrame&nbsp; constructor () {&nbsp; &nbsp; super();&nbsp; &nbsp; this._volume = 0;&nbsp; &nbsp; this._updateIntervalInMS = 25;&nbsp; &nbsp; this._nextUpdateFrame = this._updateIntervalInMS;&nbsp; &nbsp; this.port.onmessage = event => {&nbsp; &nbsp; &nbsp; if (event.data.updateIntervalInMS)&nbsp; &nbsp; &nbsp; &nbsp; this._updateIntervalInMS = event.data.updateIntervalInMS;&nbsp; &nbsp; }&nbsp; }&nbsp; get intervalInFrames () {&nbsp; &nbsp; return this._updateIntervalInMS / 1000 * sampleRate;&nbsp; }&nbsp; process (inputs, outputs, parameters) {&nbsp; &nbsp; const input = inputs[0];&nbsp; &nbsp; // Note that the input will be down-mixed to mono; however, if no inputs are&nbsp; &nbsp; // connected then zero channels will be passed in.&nbsp; &nbsp; if (input.length > 0) {&nbsp; &nbsp; &nbsp; const samples = input[0];&nbsp; &nbsp; &nbsp; let sum = 0;&nbsp; &nbsp; &nbsp; let rms = 0;&nbsp; &nbsp; &nbsp; // Calculated the squared-sum.&nbsp; &nbsp; &nbsp; for (let i = 0; i < samples.length; ++i)&nbsp; &nbsp; &nbsp; &nbsp; sum += samples[i] * samples[i];&nbsp; &nbsp; &nbsp; // Calculate the RMS level and update the volume.&nbsp; &nbsp; &nbsp; rms = Math.sqrt(sum / samples.length);&nbsp; &nbsp; &nbsp; this._volume = Math.max(rms, this._volume * SMOOTHING_FACTOR);&nbsp; &nbsp; &nbsp; // Update and sync the volume property with the main thread.&nbsp; &nbsp; &nbsp; this._nextUpdateFrame -= samples.length;&nbsp; &nbsp; &nbsp; if (this._nextUpdateFrame < 0) {&nbsp; &nbsp; &nbsp; &nbsp; this._nextUpdateFrame += this.intervalInFrames;&nbsp; &nbsp; &nbsp; &nbsp; this.port.postMessage({volume: this._volume});&nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; }&nbsp; &nbsp;&nbsp;&nbsp; &nbsp; return true;&nbsp; }});最后这是 html,您可以在其中显示检测到的音量:<div class="container">&nbsp; &nbsp; <span>Microphone</span>&nbsp; &nbsp; <div class="volumen-wrapper">&nbsp; &nbsp; &nbsp; &nbsp; <div class="led"></div>&nbsp; &nbsp; &nbsp; &nbsp; <div class="led"></div>&nbsp; &nbsp; &nbsp; &nbsp; <div class="led"></div>&nbsp; &nbsp; &nbsp; &nbsp; <div class="led"></div>&nbsp; &nbsp; &nbsp; &nbsp; <div class="led"></div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;&nbsp; &nbsp; &nbsp; &nbsp; <div class="led"></div>&nbsp; &nbsp; &nbsp; &nbsp; <div class="led"></div>&nbsp; &nbsp; &nbsp; &nbsp; <div class="led"></div>&nbsp; &nbsp; &nbsp; &nbsp; <div class="led"></div>&nbsp; &nbsp; &nbsp; &nbsp; <div class="led"></div>&nbsp; &nbsp; </div>&nbsp; &nbsp; <div class="control-audio-wrapper">&nbsp; &nbsp; &nbsp; &nbsp; <div id="audio" class="audio-control">&#127908;</div>&nbsp; &nbsp; </div></div><script type="module" src="./index.js"></script>这是结果这是我在&nbsp;codepen中的实现
打开App,查看更多内容
随时随地看视频慕课网APP

相关分类

JavaScript