WebRTC與音頻音量

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)了。

參考:https://developer.mozilla.org/en-US/docs/Web/API/ScriptProcessorNode/onaudioprocess#browser_compatibility

完整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的功能,得到音頻音量的數值。

本文鏈接:WebRTC音頻音量

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章