WebRTC開啟麥克風,獲取音訊,在網頁上顯示音量。
播放示例音訊
先從播放音訊入手。準備一個現成的音訊檔案。
介面上放一個audio元素,提前準備好一個音訊檔案,路徑填入src
<audio id="sample-audio" src="God_knows_01.mp3" controls autoplay></audio>
audio有預設的樣式。開啟網頁就可以利用這個來播放示例音訊了。
WebRTC開啟麥克風
準備
html頁面上放audio,meter,button等等
<audio id="play-audio" controls autoplay></audio>
<div id="meters">
<div id="instant">
<div class="label">實時:</div>
<meter high="0.25" max="1" value="0"></meter>
<div class="value"></div>
</div>
<div id="slow">
<div class="label">秒級:</div>
<meter high="0.25" max="1" value="0"></meter>
<div class="value"></div>
</div>
<div id="clip">
<div class="label">Clip:</div>
<meter max="1" value="0"></meter>
<div class="value"></div>
</div>
</div>
<div>
<button id="startBtn">啟動</button>
<button id="stopBtn" disabled>停止</button>
</div>
<div id="msg"></div>
引入js指令碼
<!-- 使用本地的介面卡 -->
<script src="../js/adapter-latest.js" async></script>
<script src="js/soundmeter.js"></script>
<script src="js/main.js"></script>
- 使用了本地的介面卡
- main.js 主要的控制邏輯
- soundmeter.js 計算音訊音量的方法
soundmeter.js
在使用WebRTC之前,先來看音訊相關的方法和類。
要使用web的音訊API,需要用到AudioContext
try {
window.AudioContext = window.AudioContext || window.webkitAudioContext;
window.audioContext = new AudioContext();
} catch (e) {
alert('Web Audio API 不支援');
}
AudioContext.createScriptProcessor(int, int, int)
建立js處理音訊的工具,接受3個數字引數
- bufferSize
- 緩衝區大小,以樣本幀為單位。具體來講,數值必須是以下這些值當中的某一個: 256, 512, 1024, 2048, 4096, 8192, 16384. 如果不傳,或者引數為0,則取當前環境最合適的緩衝區大小, 取值為2的冪次方的一個常數,在該node的整個生命週期中都不變.
- 該取值控制著audioprocess事件被分派的頻率,以及每一次呼叫多少樣本幀被處理. 較低bufferSzie將導致一定的延遲。較高的bufferSzie就要注意避免音訊的崩潰和故障。推薦作者不要給定具體的緩衝區大小,讓系統自己選一個好的值來平衡延遲和音訊質量。
- 重要: Webkit (version 31)要求呼叫這個方法的時候必須傳入一個有效的bufferSize .
- numberOfInputChannels
- 值為整數,用於指定輸入node的聲道的數量,預設值是2,最高能取32.
- numberOfOutputChannels
- 值為整數,用於指定輸出node的聲道的數量,預設值是2,最高能取32.
參考:https://developer.mozilla.org/zh-CN/docs/Web/API/BaseAudioContext/createScriptProcessor
AudioContext.onaudioprocess
監聽音訊資料
基本用法
var audioCtx = new AudioContext();
var scriptNode = audioCtx.createScriptProcessor(4096, 1, 1);
scriptNode.onaudioprocess = function(event) { /* ... */ }
本文示例使用這個監聽。但它目前已經不推薦使用(deprecated)了。
完整soundmeter.js程式碼如下
'use strict';
// 這個類生成音訊音量相關的數值
function SoundMeter(context) {
this.context = context;
this.instant = 0.0; // 實時
this.slow = 0.0; // 秒級
this.clip = 0.0;
this.script = context.createScriptProcessor(2048, 1, 1);
const that = this;
this.script.onaudioprocess = function (event) {
const input = event.inputBuffer.getChannelData(0); // 得到一個長度為2048的陣列
let i;
let sum = 0.0;
let clipcount = 0;
for (i = 0; i < input.length; ++i) {
sum += input[i] * input[i];
if (Math.abs(input[i]) > 0.99) {
clipcount += 1;
}
}
console.log('clip count', clipcount);
that.instant = Math.sqrt(sum / input.length);
that.slow = 0.95 * that.slow + 0.05 * that.instant;
that.clip = clipcount / input.length;
};
}
SoundMeter.prototype.connectToSource = function (stream, callback) {
console.log('SoundMeter connecting');
try {
this.mic = this.context.createMediaStreamSource(stream);
this.mic.connect(this.script);
this.script.connect(this.context.destination);
if (typeof callback !== 'undefined') {
callback(null);
}
} catch (e) {
console.error(e);
if (typeof callback !== 'undefined') {
callback(e);
}
}
};
SoundMeter.prototype.stop = function () {
console.log('SoundMeter 正在停止');
this.mic.disconnect();
this.script.disconnect();
};
js
獲取頁面元素
'use strict';
const instantMeter = document.querySelector('#instant meter');
const slowMeter = document.querySelector('#slow meter');
const clipMeter = document.querySelector('#clip meter');
const instantValueDisplay = document.querySelector('#instant .value');
const slowValueDisplay = document.querySelector('#slow .value');
const clipValueDisplay = document.querySelector('#clip .value');
const playAudio = document.querySelector('#play-audio');
const msgEle2 = document.querySelector("#msg");
const startBtn = document.getElementById("startBtn");
const stopBtn = document.getElementById('stopBtn');
let meterRefresh = null;
const constraints = window.constraints = {
audio: true,
video: false
};
開啟麥克風
開啟麥克風,和之前開啟攝像頭類似,用的也是getUserMedia
方法。
startBtn.onclick = function (e) {
startBtn.disabled = true;
stopBtn.disabled = false;
try {
window.AudioContext = window.AudioContext || window.webkitAudioContext;
window.audioContext = new AudioContext();
} catch (e) {
alert('Web Audio API 不支援.');
}
navigator.mediaDevices.getUserMedia(constraints).then(gotAudioStream).catch(onErr);
};
處理音訊
成功開啟麥克風後,處理音訊流
function gotAudioStream(stream) {
stream.oninactive = function () {
console.log('音訊停止');
};
window.stream = stream;
playAudio.srcObject = stream;
console.log('對接麥克風的音訊');
const soundMeter = window.soundMeter = new SoundMeter(window.audioContext);
soundMeter.connectToSource(stream, function (e) {
if (e) {
alert(e);
return;
}
meterRefresh = setInterval(() => {
instantMeter.value = instantValueDisplay.innerText =
soundMeter.instant.toFixed(2);
slowMeter.value = slowValueDisplay.innerText =
soundMeter.slow.toFixed(2);
clipMeter.value = clipValueDisplay.innerText =
soundMeter.clip;
}, 100);
});
}
function onErr(error) {
const errorMessage = '報錯 navigator.MediaDevices.getUserMedia : ' + error.message + ' ' + error.name;
msgEle2.innerText = errorMessage;
console.error(errorMessage);
}
建立soundMeter
,對接到音訊流soundMeter.connectToSource(stream, function(e){});
設定一個定時器setInterval
,定時重新整理音量資料
停止
把音訊流停下
stopBtn.onclick = function (e) {
console.log('停止');
startBtn.disabled = false;
stopBtn.disabled = true;
window.stream.getTracks().forEach(track => track.stop());
window.soundMeter.stop();
clearInterval(meterRefresh);
instantMeter.value = instantValueDisplay.innerText = '';
slowMeter.value = slowValueDisplay.innerText = '';
clipMeter.value = clipValueDisplay.innerText = '';
}
執行效果
執行效果連結
小結
用getUserMedia
開啟麥克風,獲取音訊流。使用AudioContext的功能,得到音訊音量的數值。