音频可视化:原生音频API为前端带来的全新可能!

音频API是一组提供给网页开发者的接口,允许他们直接在浏览器中处理音频内容。这些API使得在不依赖任何外部插件的情况下操作和控制音频成为可能。
Web Audio API 可以进行音频的播放、处理、合成以及分析等操作。借助于这些工具,开发者可以实现自定义的音效处理,创建互动的音乐体验,甚至开发复杂的音频应用程序,如实时音频频谱分析或音频可视化效果。

在这里插入图片描述

本文就通过Web Audio API来实现对音乐可视化的案例

基础结构

整个界面结构比较简单,需要一个播放音频的audio和用于可视化的canvas。

<canvas id="canvas"></canvas>
<audio
  src="https://resource.dengzhanyong.com/audio/海底.mp3"
  controls
></audio>

初始化工作

对 audio 注册播放事件,在首次播放时做一些音频处理相关的初始化工作

const audio = document.querySelector("audio");
const canvas = document.querySelector("canvas");
const ctx = canvas.getContext("2d");
// 事件初始化状态
let isInit = false;
// 绑定播放事件
audio.onplay = () => {
    // 初始化内容只需要做一次,每次暂停后再播放都会调用此事件,因此这里做个判断
    if (isInit) return;
    /* 处理内容 */
    isInit = true;
};

音频处理流程

要想把音频可视化,必须要拿到播放的音频数据,这就需要使用到AudioContext API,可以理解为是一个音频上下文,音频所有的事情都在上下文中发生,整个流程见下图。
在这里插入图片描述

源节点:可以通过AudioContext来创建源节点,本案例中源节点就是audio标签
处理节点:在这里对音频进行处理,比如:处理音色、音调、音频等
输出节点:一般为当前使用的扬声器,可以在AudioContext中获取到
每个节点之间通过连接的方式串联起来,形成一个完整的链路,在处理节点可以加入多个处理节点,也可以有多个源节点。

在这里插入图片描述

对于本案例来说,需要一个分析器节点,从分析器节点中获取到音频波形数据,然后处理数据,最后交给canvas绘制。
在这里插入图片描述

audio.onplay = () => {
    if (isInit) return;
    // 创建音频上下文
    const audioCtx = new AudioContext();
    // 创建音频源
    const source = audioCtx.createMediaElementSource(audio);
    // 连接到输出设备
    source.connect(audioCtx.destination);
    isInit = true;
};

audioCtx.destination为当前的输出设备

获取处理音频数据

使用audioCtx.createAnalyser 创建一个分析器节点,然后将源节点连接到分析其节点。

分析器节点获取到的是时域图的数据,需要通过快速傅立叶变换把时域图转为频域图数据,转换过程不需要我们自己去做,AudioContext 提供了相关的API,只需要简单设置一些参数即可。

// 设置初始化状态
let isInit = false;
let analyser, data;
// 绑定播放事件
audio.onplay = () => {
  // console.log("开始播放");
  if (isInit) return;
  // 创建音频上下文
  const audioCtx = new AudioContext();
  // 创建音频源
  const source = audioCtx.createMediaElementSource(audio);
  // 创建分析器节点
      analyser = audioCtx.createAnalyser();
  // 设置窗口大小,窗口越大,分析结果越详细
      analyser.fftSize = 512;
      data = new Uint8Array(analyser.frequencyBinCount);
  // 将源连接到分析器节点
      source.connect(analyser);
  // 将分析器节点连接到输出设备
    analyser.connect(audioCtx.destination);
    isInit = true;
};
  • fftSize:设置傅立叶变换的窗口大小,窗口越大,分析的结果越详细。数值必须是2的n次幂
  • 分析结果放到数组中,数组的每一项都是一个8位无符号的整数,因此这里创建的不是一个普通数组Array,而是Uint8Array
  • frequencyBinCount:这个属性的值为fftSize的一半,因为傅立叶变换后的频域图是对称的结构,所以这里只需要拿到一半的数据即可
    最后将分析器节点连接到输出设备,否则无法听到音频声音

绘制数据

随着音频的不断播放,需要把分析器的数据不断的更新到data数组中。
绘制过程就是一些简单的计算逻辑:
每个矩形的宽度 = 画布宽度/数组长度
每个条形的总宽度 = 数据/255 * 画布高度

// 绘制内容
function draw() {
    requestAnimationFrame(draw);
// 清空画布
const { width, height } = canvas;
    ctx.clearRect(0, 0, width, height);
    if (!isInit) return;
    // 把分析器节点的数据更新到data中
    analyser.getByteFrequencyData(data);
    const len = data.length;
    const barWidth = width / len;
    // 每一个方块的高度
    const blockHeight = 8;
    for (let index = 0; index < data.length; index++) {
      // 拿到本列的数值
      const _data = data[index];
      const barHeight = (_data / 255) * height;
      // 每列的横坐标
      const x = index * barWidth;
      // 每列的方块数量
      const blockCount = Math.round(barHeight / 10);
      // 循环绘制每列的小方块
      for (let number = 0; number < blockCount; number++) {
            // 设置颜色
            ctx.fillStyle = gradient[number];
            // 每个小方块的纵坐标
            const y = height - blockHeight * number;
            // 绘制圆角矩形
            drawRoundedRect(x, y, barWidth - 1,  blockHeight - 1, 2);
        }
    }   
}

绘制圆角矩形

canvas没有直接绘制圆角矩形的方法,我们通过lineTo方法来设置四边,再通过quadraticCurveTo(二次贝塞尔曲线路径)方法来设置圆角的路径,最后再进行填充。

function drawRoundedRect(x, y, width, height, radius) {
if (height === 0) return;
    ctx.beginPath();
    ctx.moveTo(x + radius, y);
    ctx.lineTo(x + width - radius, y);
    ctx.quadraticCurveTo(x + width, y, x + width, y + radius);
    ctx.lineTo(x + width, y + height - radius);
    ctx.quadraticCurveTo(x + width, y + height, x + width - radius, y + height);
    ctx.lineTo(x + radius, y + height);
    ctx.quadraticCurveTo(x, y + height, x, y + height - radius);
    ctx.lineTo(x, y + radius);
    ctx.quadraticCurveTo(x, y, x + radius, y);
    ctx.fill();
}

设置渐变色

HSLA表示一种颜色模式,它是由四个部分组成:色相(Hue)、饱和度(Saturation)、亮度(Lightness)和透明度(Alpha)

  • hue(色相):0到360之间的整数,表示颜色的基本属性,如红色、绿色或蓝色。
  • saturation(饱和度):0%到100%之间的百分比,表示颜色的纯度。0%表示灰色,100%表示最鲜艳的颜色。
  • lightness(亮度):0%到100%之间的百分比,表示颜色的明暗程度。0%表示黑色,50%表示原始颜色,100%表示白色。
  • alpha(透明度):0到1之间的小数,表示颜色的透明度。0表示完全透明,1表示完全不透明。
    在这里插入图片描述

封装一个获取渐变色的方法 generateGradient,接收两个参数:baseColor(起始颜色)、count(渐变色的数量)。

function generateGradient(baseColor, count) {
    let hsl = baseColor.match(
    /hsla?\((\d+),\s*(\d+%),\s*(\d+%),\s*([\d.]+)\)/
        );
    let h = parseInt(hsl[1], 10); // Hue
    let s = parseInt(hsl[2], 10); // Saturation
    let l = parseInt(hsl[3], 10); // Lightness// 在色盘上按照数量均分,获取每个均分点的颜色
    let stepH = 360 / count;
    // 提高每个等级的亮度
    let stepL = 100 / (count + 1);
    
    let gradientColors = [];
    for (let i = 0; i < count; i++) {
          gradientColors.push(
              `hsl(${h + i * stepH}, ${s}%, ${l + i * stepL}%)`
          );
    }
  
  return gradientColors;
}let baseColor = "hsla(240, 100%, 50%, 1)"; // 蓝色
let gradient = generateGradient(baseColor, 200); // 200种颜色

到这里就已经完成了本次案例的全部内容,对于音频的处理这还只是冰山一角,发挥你的想象力可以做出更多可玩性较强的内容。

最后,可以访问 https://resource.dengzhanyong.com/audio/音频可视化.html 查看本次案例的效果。回复 “音频可视化” 获取本案例的全部源码。

往期推荐

10分钟掌握HTML拖放API!让你的网页元素瞬间拥有拖拽功能,轻松提升用户体验!

不要只会用conosle.log了,这几个console命令,让你的调试效率翻倍

前端手写Promise.all,你不知道的多个知识点,比想象中更精彩!

写在最后

欢迎访问我的个人网站:www.dengzhanyong.com
关注我的公众号【前端筱园】,不错过每一篇推送
在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/595377.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

MySQL使用GROUP BY使用技巧和注意事项总结

⛰️个人主页: 蒾酒 &#x1f525;系列专栏&#xff1a;《mysql经验总结》 目录 写在前面 GROUP BY简介 基本用法 单列分组 多列分组 使用聚合函数 过滤分组结果 按表达式分组 使用 GROUP BY 的排序 注意事项 遵循原则 使用能够唯一标识每个分组的字段或字…

PCB 阻抗设置

凡亿电路有详细的阻抗设计 https://baijiahao.baidu.com/s?id1773006310888936808&wfrspider&forpc 差分基本上是100ohm, 单端是50ohm 布线阻抗通常是&#xff0c; -设置叠层关系 层的定义设计原则&#xff1a; 1&#xff09;主芯片相临层为地平面&#xff0c;提供器…

Whisper、Voice Engine推出后,训练语音大模型的高质量数据去哪里找?

近期&#xff0c;OpenAI 在语音领域又带给我们惊喜&#xff0c;通过文本输入以及一段 15 秒的音频示例&#xff0c;可以生成既自然又与原声极为接近的语音。值得注意的是&#xff0c;即使是小模型&#xff0c;只需一个 15 秒的样本&#xff0c;也能创造出富有情感且逼真的声音。…

图像处理-图像平滑

图像平滑 前言一、概念介绍1.1 图像的平滑1.2 图像中噪声的分类1.3 MATLAB的添加噪音代码 二、空间域平滑滤波2.1 均值滤波2.2 原理计算 总结 前言 在图像的获取、传输和存储过程常常收到各种噪声的干扰和影响&#xff0c;使得图像的质量下降&#xff0c;为了获得高质量的数字…

CPU炼丹——YOLOv5s

1.Anaconda安装与配置 1.1安装与配置 Anaconda3的安装看下面的教程&#xff1a; 最新Anaconda3的安装配置及使用教程&#xff08;详细过程&#xff09;http://t.csdnimg.cn/yygXD&#xff0c;接上面文章下载后&#xff0c;配置环境变量的时候记得在原来你装的Python更下面添…

如何快速找出文件夹里的全部带有英文纯英文的文件

参考此文章&#xff1a;如何快速找出文件夹里的全部带有中文&纯中文的文件 只需要根据自己的需求&#xff0c;把下面相关的设置调整好即可

【Hadoop】MapReduce (六)

MapReduce 组件 输入格式 - InputFormat InputFormat发生在Mapper之前&#xff0c;用于对数据进行切分和读取&#xff0c;会将读取到的数据传递给MapTask处理。所以InputFormat读取到的数据是什么格式&#xff0c;Mapper接收到的数据就是什么格式 作用 getSplits&#xff1a…

YOLO系列改进,自研模块助力涨点

目录 一、原理 二、代码 三、添加到YOLOv5中 一、原理 论文地址:

“先锋”西凤

执笔 | 文 清 编辑 | 古利特 制曲是酿酒的第一道工序&#xff0c;也是中国酿酒史上的一大创新&#xff0c;对白酒风味的影响至关重要。西凤酿酒人坚信“曲是酒之骨”&#xff0c;“曲”的品质决定酒的“骨气”&#xff0c;“酒曲”是酒体形成主题风味的基本定型元素和催化剂…

OpenNJet如何做到让用户永远在线

前言 最近看到了国内开源的一个名为OpenNJet的项目&#xff0c;有一个响亮的口号&#xff1a;“下一代云原生应用引擎”。 一下子就被吸引到了。 先看下官方对OpenNJet的介绍&#xff1a; OpenNJet 应用引擎是基于 NGINX 的面向互联网和云原生应用提供的运行时组态服务程序&…

如何根据配置动态生成Spring的Bean?

一、问题解析 在 Spring 应用中&#xff0c;根据运行时的配置&#xff08;比如数据库配置、配置文件、配置中心等&#xff09;动态生成 Spring Bean 是一种常见需求&#xff0c;特别是在面对多环境配置或者需要根据不同条件创建不同实例时。 Spring 提供了几种方式来实现这一需…

spice common模块

库分为三部分libspice-common.a&#xff0c;libspice-common-client.a,libspice-common-server.a。 1、libspice-common.a工程编译代码 # # libspice-common # spice_common_sources [ agent.c, agent.h, backtrace.c, backtrace.h, canvas_utils.c, canvas_utils.h, demarsha…

sql编写规范(word原件)

编写本文档的目的是保证在开发过程中产出高效、格式统一、易阅读、易维护的SQL代码。 1 编写目的 2 SQL书写规范 3 SQL编写原则 软件全套资料获取进主页或者本文末个人名片直接获取。

高德地图在vue3项目中使用:实现画矢量图、编辑矢量图

使用高德地图实现画多边形、矩形、圆&#xff0c;并进行编辑保存和回显。 1、准备工作 参考高德地图官网&#xff0c;进行项目key申请&#xff0c;链接: 准备 2、项目安装依赖 npm i amap/amap-jsapi-loader --save3、地图容器 html <template><!-- 绘制地图区域…

GNSS 地球自转改正算例分析

文章目录 Part.I IntroductionPart.II 由地球自转引起的误差的概念和改正方法Chap.I 误差概念Chap.II 改正方法 Part.II 算例分析Chap.I 基础数据Chap.II 计算过程 AppendixReference Part.I Introduction 为了更好地理解 地球自转改正&#xff0c;本文将介绍一个算例。 Part.…

手动交互式选点提取三维点云轮廓边界线 附python代码

一种新的三维点云轮廓边界提取方案: 1 手动选择一个边界或者其附近的点 2 自动搜索临近区域,并找到附近的平面和进行平面分割 3 提取平面的交点 4 该交点就是点云的轮廓边界点,把它往两边延展,就是完整的点云轮廓边界 import open3d as o3d import numpy as np import …

Java模块化系统:引领代码革命与性能飞跃

JDK工程结构的问题 在说Java模块化系统之前&#xff0c;先来说说Java9之前的JDK在工程结构上的问题&#xff0c;从JDK本身的问题说起&#xff0c;Java从1996年发布第一版到2017年发布Java9&#xff0c;中间经历了近20年的时间&#xff0c;在这期间发布了无数个大大小小的版本用…

RESTFul风格设计和实战

四、RESTFul风格设计和实战 4.1 RESTFul风格概述 4.1.1 RESTFul风格简介 RESTful&#xff08;Representational State Transfer&#xff09;是一种软件架构风格&#xff0c;用于设计网络应用程序和服务之间的通信。它是一种基于标准 HTTP 方法的简单和轻量级的通信协议&#x…

YAML如何操作Kubernetes核心对象

Pod Kubernetes 最核心对象Pod Pod 是对容器的“打包”&#xff0c;里面的容器&#xff08;多个容器&#xff09;是一个整体&#xff0c;总是能够一起调度、一起运行&#xff0c;绝不会出现分离的情况&#xff0c;而且 Pod 属于 Kubernetes&#xff0c;可以在不触碰下层容器的…

存储或读取时转换JSON数据

一、 数据库类型 二、使用Hutool工具 存储时将数据转换为JSON数据 获取时将JSON数据转换为对象 发现问题&#xff1a; 原本数据对象是Address 和 Firend但是转换完成后数据变成了JSONArray和JSONObject 三、自定义TypeHandler继承Mybatis的BaseTypeHandler处理器 package …