Skip to content

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系列

本文也发布在

知乎专栏 cnblog 华为云社区

作者: rustfisher.com | rf.cs@foxmail.com
示例: AndroidTutorial Gitee, Tutorial Github
本文链接: https://www.an.rustfisher.com/webrtc/web-samples/audio-collection/
一家之言,仅当抛砖引玉。如有错漏,还请指出。如果喜欢本站的内容,还请支持作者。也可点击1次下方的链接(链接内容与本站无关),谢谢支持服务器。 如有疑问,请与我联系:Android issues - gitee