standardizedcontext:彻底为Web Audio扫清障碍?

家好,很高兴又见面了,我是"高级前端 进阶 ",由我带着大家一起关注前端前沿、深入前端底层技术,大家一起进步,也欢迎大家关注、点赞、收藏、转发,您的支持是我不断创作的动力。

今天给大家带来的主题是 standardized-audio-context,即 Web Audio API 的跨浏览器包装器,旨在严格遵循相关标准。话不多说,直接进入正题!

1.Web Audio API

1.1 什么是 Web Audio API

Web Audio API 提供了在 Web 上控制音频的一个非常有效的通用系统,允许开发者自选音频源,对音频添加特效,使音频可视化,添加空间效果(如平移)等等。

总之,Web Audio API 使用户可以在音频上下文(AudioContext)中进行音频操作,同时具有模块化路由的特点。在音频节点上操作基础的音频,连接在一起构成音频路由图。即使在单个上下文中也支持多源,尽管这些音频源具有多种不同类型通道布局。这种模块化设计提供了灵活创建动态效果的复合音频的方法。

一个简单而典型的 Web Audio 流程如下:

1.2 使用 Web Audio API

下面的示例展示了诸多 Web Audio API 接口的使用,包括对声音的音量进行改变。

var audioCtx = new (window.AudioContext || window.webkitAudioContext)();
// 定义音频上下文
// Webkit/blink 浏览器需要前缀,Safari 没有 window 将无法工作
var voiceSelect = document.getElementById("voice");
//  用于选择语音效果选项的选择框
var visualSelect = document.getElementById("visual");
// 用于选择音频可视化选项的选择框
var mute = document.querySelector(".mute");
// 静音按钮
var drawVisual;
// requestAnimationFrame
var analyser = audioCtx.createAnalyser();
// 创建一个AnalyserNode用来获取音频时间和频率数据以实现数据可视化。
var distortion = audioCtx.createWaveShaper();
// 创建了表示非线性失真的WaveShaperNode,通常被用来给音频添加失真效果
var gainNode = audioCtx.createGain();
// 创建一个 GainNode,可用于控制音频图的整体增益(或音量)
var biquadFilter = audioCtx.createBiquadFilter();
// 创建一个 BiquadFilterNode 表示一个二阶滤波器,可配置为几种不同的常见滤波器类型
function makeDistortionCurve(amount) {
  // 为失真/波形整形器节点使用创建曲线形状的函数
  var k = typeof amount === "number" ? amount : 50,
    n_samples = 44100,
    curve = new Float32Array(n_samples),
    deg = Math.PI / 180,
    i = 0,
    x;
  for (; i < n_samples; ++i) {
    x = (i * 2) / n_samples - 1;
    curve[i] = ((3 + k) * x * 20 * deg) / (Math.PI + k * Math.abs(x));
  }
  return curve;
}
navigator.getUserMedia(
  {
    // 限制 - 此应用程序仅需要音频
    audio: true,
  },
  // 成功回调
  function (stream) {
    source = audioCtx.createMediaStreamSource(stream);
    source.connect(analyser);
    analyser.connect(distortion);
    distortion.connect(biquadFilter);
    biquadFilter.connect(gainNode);
    gainNode.connect(audioCtx.destination);
    // 将不同的音频图节点连接在一起
    visualize(stream);
    voiceChange();
  },
  // 错误回调
  function (err) {
    console.log("The following gUM error occured: " + err);
  }
);
function visualize(stream) {
  WIDTH = canvas.width;
  HEIGHT = canvas.height;
  var visualSetting = visualSelect.value;
  console.log(visualSetting);
  if (visualSetting == "sinewave") {
    analyser.fftSize = 2048;
    var bufferLength = analyser.frequencyBinCount;
    // FFT 值的一半
    var dataArray = new Uint8Array(bufferLength);
    // 创建一个数组来存储数据
    canvasCtx.clearRect(0, 0, WIDTH, HEIGHT);
    function draw() {
      drawVisual = requestAnimationFrame(draw);
      analyser.getByteTimeDomainData(dataArray);
      // 获取波形数据并将其放入上面创建的数组中
      canvasCtx.fillStyle = "rgb(200, 200, 200)";
      // 用画布画出波浪
      canvasCtx.fillRect(0, 0, WIDTH, HEIGHT);
      canvasCtx.lineWidth = 2;
      canvasCtx.strokeStyle = "rgb(0, 0, 0)";
      canvasCtx.beginPath();
      var sliceWidth = (WIDTH * 1.0) / bufferLength;
      var x = 0;
      for (var i = 0; i < bufferLength; i++) {
        var v = dataArray[i] / 128.0;
        var y = (v * HEIGHT) / 2;
        if (i === 0) {
          canvasCtx.moveTo(x, y);
        } else {
          canvasCtx.lineTo(x, y);
        }
        x += sliceWidth;
      }
      canvasCtx.lineTo(canvas.width, canvas.height / 2);
      canvasCtx.stroke();
    }
    draw();
  } else if (visualSetting == "off") {
    canvasCtx.clearRect(0, 0, WIDTH, HEIGHT);
    canvasCtx.fillStyle = "red";
    canvasCtx.fillRect(0, 0, WIDTH, HEIGHT);
  }
}
function voiceChange() {
  distortion.curve = new Float32Array();
  biquadFilter.gain.value = 0;
  // 每次使用 voiceChange 函数时都会重置效果
  var voiceSetting = voiceSelect.value;
  console.log(voiceSetting);
  if (voiceSetting == "distortion") {
    distortion.curve = makeDistortionCurve(400);
    // 使用波形成形器节点对声音应用失真
  } else if (voiceSetting == "biquad") {
    biquadFilter.type = "lowshelf";
    biquadFilter.frequency.value = 1000;
    biquadFilter.gain.value = 25;
    // 使用双二阶将 Lowshelf 滤波器应用于声音
  } else if (voiceSetting == "off") {
    console.log("Voice settings turned off");
    // 不执行任何操作,因为选择了关闭选项
  }
}
// 用于更改可视化和语音设置的事件侦听器
visualSelect.onchange = function () {
  window.cancelAnimationFrame(drawVisual);
  visualize(stream);
};
voiceSelect.onchange = function () {
  voiceChange();
};
mute.onclick = voiceMute;
function voiceMute() {
  // 切换静音和取消静音
  if (mute.id == "") {
    gainNode.gain.value = 0;
    // gain set to 0 to mute sound
    mute.id = "activated";
    mute.innerHTML = "Unmute";
  } else {
    gainNode.gain.value = 1;
    // gain set to 1 to unmute sound
    mute.id = "";
    mute.innerHTML = "Mute";
  }
}

使用 Web Audio API,时间可以非常精确地控制,几乎没有延迟,开发人员可以准确地响应事件,并且可以针对采样数据进行编程。

Web Audio API 也使开发者能够控制音频的空间化。在基于源 - 侦听器模型的系统中,它允许控制平移模型和处理距离引起的衰减或移动源(移动侦听)引起的多普勒效应。

2.什么是 standardized-audio-context

standardized-audio-context 是 Web Audio API 的跨浏览器包装器,旨在严格遵循相关标准。standardized-audio-contex 包提供了 Web Audio API 的一个子集(几乎是完整的),可以在每个受支持的浏览器中以可靠且一致的方式工作。


与其他流行的 Polyfill 相比,standardized-audio-contex 不会在全局范围内污染或修改任何内容。 换句话说,它不会引起任何副作用。 因此,可以在任何库内安全使用,即所谓的 ponyfill。standardized-audio-contex 上下文的目标之一是仅实现缺失的功能,并尽可能避免重写内置功能。

但是,有些特性是无法以某种方式伪造的并使它们在与本机实现时具有相同的性能,其中最突出的是 AudioWorklet。

Web Audio API 的 AudioWorklet 接口用于提供在单独线程中执行的自定义音频处理脚本,以提供非常低延迟的音频处理。

工作集的代码在 AudioWorkletGlobalScope 全局执行上下文中运行,使用由工作集和其他音频节点共享的单独的 Web Audio 线程。

通过 BaseAudioContext.audioWorklet 属性访问音频上下文的 AudioWorklet 实例。

值得一提的是,standardized-audio-contex 是用 TypeScript 编写,这意味着它可以在任何 TypeScript 项目中无缝使用。与 TypeScript 提供的开箱即用的 Web 音频 API 类型相比,standardized-audio-contex 导出的类型实际上与具体实现相匹配。 TypeScript 根据 Web Audio API 的 Web IDL 定义生成其类型,该定义并不总是与实际可用的实现匹配。

目前,standardized-audio-context 在 Github 上通过 MTI 协议开源,有超过 0.6k 的 star、6.3k 的项目依赖量、NPM 周平均下载量 50k,是一个值得关注的前端开源项目。

3.使用 standardized-audio-context

standardized-audio-context 可在 npm 上使用,并且可以像其他库一样安装。

npm install standardized-audio-context

然后,可以以如下方式导入 AudioContext 和 OfflineAudioContext:

import { AudioContext, OfflineAudioContext } from "standardized-audio-context";

开发者还可以使用 jspm 等服务加载标准化音频上下文。此时,上面的导入语句需要更改为指向 URL。

import {
  AudioContext,
  OfflineAudioContext,
} from "https://jspm.dev/standardized-audio-context";

一旦 AudioContext 和 OfflineAudioContext 被导入,开发者就可以原生方法一样使用。例如,以下代码片段将产生一个漂亮且干净的正弦波。

import { AudioContext } from "standardized-audio-context";
const audioContext = new AudioContext();
const oscillatorNode = audioContext.createOscillator();
// OscillatorNode 接口表示周期性波形,例如:正弦波。
// 它是一个 AudioScheduledSourceNode 音频处理模块
// 可以创建给定波的指定频率,实际上是恒定的音调
oscillatorNode.connect(audioContext.destination);
oscillatorNode.start();

另一种方法是使用 AudioNode 构造函数(在本例中为 OscillatorNode 构造函数)而不是工厂方法。

import { AudioContext, OscillatorNode } from "standardized-audio-context";
const audioContext = new AudioContext();
const oscillatorNode = new OscillatorNode(audioContext);
oscillatorNode.connect(audioContext.destination);
oscillatorNode.start();
// 指定开始播放提示音的确切时间

4.本文总结

本文主要和大家聊聊standardized-audio-context,即 Web Audio API 的跨浏览器包装器,旨在严格遵循相关标准。相信通过本文的阅读,大家对 standardized-audio-context 会有一个初步的了解,同时也会有自己的看法。

因为篇幅有限,文章并没有过多展开,如果有兴趣,可以在我的主页继续阅读,同时文末的参考资料提供了大量优秀文档以供学习。最后,欢迎大家点赞、评论、转发、收藏!

参考资料

https://github.com/chrisguttandin/standardized-audio-context

https://www.npmjs.com/package/standardized-audio-context

https://developer.mozilla.org/zh-CN/docs/Web/API/Web_Audio_API/Using_Web_Audio_API

https://developer.mozilla.org/zh-CN/docs/Web/API/Web_Audio_API

https://mdn.github.io/voice-change-o-matic/

https://developer.mozilla.org/en-US/docs/Web/API/AudioContext

https://habr.com/ru/articles/210422/

展开阅读全文

页面更新:2024-02-08

标签:侦听器   波形   上下文   滤波器   节点   开发者   函数   障碍   音频   浏览器   效果

1 2 3 4 5

上滑加载更多 ↓
推荐阅读:
友情链接:
更多:

本站资料均由网友自行发布提供,仅用于学习交流。如有版权问题,请与我联系,QQ:4156828  

© CopyRight 2008-2024 All Rights Reserved. Powered By bs178.com 闽ICP备11008920号-3
闽公网安备35020302034844号

Top