zoukankan      html  css  js  c++  java
  • 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音频音量

    一个软件工程师的记录
  • 相关阅读:
    html5---音频视频基础一
    SQL--数据库性能优化详解
    微信--入门开发
    MVC----基础
    jquery -----简单分页
    sql优化
    集合的总结
    java并发编程与高并发解决方案
    java中boolean类型占几个字节
    正则表达式小结
  • 原文地址:https://www.cnblogs.com/rustfisher/p/15713126.html
Copyright © 2011-2022 走看看