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

本文就通过Web Audio API来实现对音乐可视化的案例
基础结构
整个界面结构比较简单,需要一个播放音频的audio和用于可视化的canvas。
<canvas id="canvas"></canvas>
<audiosrc="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); // Huelet s = parseInt(hsl[2], 10); // Saturationlet 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
关注我的公众号【前端筱园】,不错过每一篇推送

相关文章:
音频可视化:原生音频API为前端带来的全新可能!
音频API是一组提供给网页开发者的接口,允许他们直接在浏览器中处理音频内容。这些API使得在不依赖任何外部插件的情况下操作和控制音频成为可能。 Web Audio API 可以进行音频的播放、处理、合成以及分析等操作。借助于这些工具,开发者可以实现自定义的音…...
【中等】保研/考研408机试-动态规划1(01背包、完全背包、多重背包)
背包问题基本上都是模板题,重点:弄熟多重背包模板 dp[j]max(dp[j-v[i]]w[i],dp[j]) //核心思路代码(一维数组版) dp[i][j]max(dp[i-1][j], dp[i-1][j-v[i]]w[i])//二维数字版 一、 0-1背包 一般输入两个变量:体积&…...
[DEMO]给两个字符串取交集的词语
要求:2个英文字符串中,取相同的大于等于4个字母的词组 比如: 字符串1:" xingMeiLingabcdef WorldHello", 字符串2:"mnjqlup WorldLingLing xingMeiLingHello" 获取交接: [xingMeiLing…...
leetcode53-Maximum Subarray
题目 给你一个整数数组 nums ,请你找出一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和。 子数组 是数组中的一个连续部分。 示例 1: 输入:nums [-2,1,-3,4,-1,2,1,-5,4] 输出…...
Python 基于 OpenCV 视觉图像处理实战 之 OpenCV 简单人脸检测/识别实战案例 之七 简单进行人脸检测并添加面具特效实现
Python 基于 OpenCV 视觉图像处理实战 之 OpenCV 简单人脸检测/识别实战案例 之七 简单进行人脸检测并添加面具特效实现 目录...
【go项目01_学习记录06】
学习记录 1 使用中间件1.1 测试一下1.2 push代码 2 URI 中的斜杆2.1 StrictSlash2.2 兼容 POST 请求 1 使用中间件 代码中存在重复率很高的代码 w.Header().Set("Content-Type", "text/html; charsetutf-8")统一对响应做处理的,我们可以使用中…...
Vue中Element的下载
打开vscode让项目在终端中打开 输入npm install element-ui2.15.3 然后进行下载 在node_modules中出现element-ui表示下载完成 然后在输入Vue.use(ElementUI); import Vue from vue import App from ./App.vue import router from ./router import ElementUI from element-ui…...
机器人项目相关
机器人项目相关 1. Nvidia 1.1 Jetson 1.1.1 初步安装Riva教程 llamaspeakJetson AGX Orin踩坑记录(1)安装Riva 参考知乎链接:https://zhuanlan.zhihu.com/p/670007305 1.1.2 NVIDIA Jetson AI Lab 借助 NVIDIA Jetson™ 将生成式 AI…...
Mac升级go版本某种错误情况处理
当看到 "go1.21 is keg-only, which means it was not symlinked into /opt/homebrew" 这样的信息时,意味着Homebrew没有自动为你创建指向新版本Go的符号链接(symlink),因为这是一个旧版本Go的替代版本。 Homebrew中的…...
美团KV存储squirrel和Celler学习
文章目录 美团在KV存储squirrel优化和改进在水平方向1、对Gossip协议进行优化 在垂直扩展方面1、forkless RDB数据复制优化2、使用多线程,充分利用机器的多核能力 在高可用方面 美团持久化kv存储celler优化和改进水平扩展优化1、使用bulkload进行数据导入2、线程模型…...
Python学习笔记------处理数据和生成折线图
给定数据: jsonp_1629344292311_69436({"status":0,"msg":"success","data":[{"name":"美国","trend":{"updateDate":["2.22","2.23","2.24",&qu…...
知识图谱与大语言模型的协同(RAG)——MindMap
MindMap : Knowledge Graph Prompting Sparks Graph of Thoughts in Large Language Models 论文地址: https://arxiv.org/abs/2308.09729 代码:https://github.com/wylwilling/MindMap 1.概述 大型语言模型(LLMs)在处理新信息、防止生成幻觉内容、以及增强决策过程透明度…...
奶爸预备 |《P.E.T.父母效能训练:让亲子沟通如此高效而简单:21世纪版》 / 托马斯·戈登——读书笔记
目录 引出致中国读者译序前言第1章 父母总是被指责,而非受训练第2章 父母是人,不是神第3章 如何听,孩子才会说:接纳性语言第4章 让积极倾听发挥作用第5章 如何倾听不会说话的婴幼儿第6章 如何听,孩子才肯听第8章 通过改…...
【WebGIS实例】(13)MapboxGL 加载地形高程数据
前言 官网示例:Add 3D terrain to a map | Mapbox GL JS | Mapbox 大佬博客:Mapbox GL基础(七):地形数据的处理与加载 (jl1mall.com) 加载Mapbox地形数据 map.once(style.load, () > {map.addSource(mapbox-dem,…...
Node.js -- MongoDB
文章目录 1. 相关介绍2. 核心概念3. 命令行交互3.1数据库命令3.2 集合命令3.3 文档命令 4. 数据库应用场景4.1 新增4.2 删除4.3 更新4.4 查询 5. 图形化工具Robo 3T 1. 相关介绍 一、简介 Mongodb是什么 MongoDB是一个基于分布式文件存储的数据库,官方地址https://…...
语音识别--单声道转换与降采样
⚠申明: 未经许可,禁止以任何形式转载,若要引用,请标注链接地址。 全文共计3077字,阅读大概需要3分钟 🌈更多学习内容, 欢迎👏关注👀【文末】我的个人微信公众号…...
基于springboot+vue+Mysql的点餐平台网站
开发语言:Java框架:springbootJDK版本:JDK1.8服务器:tomcat7数据库:mysql 5.7(一定要5.7版本)数据库工具:Navicat11开发软件:eclipse/myeclipse/ideaMaven包:…...
数据库优化
一、主从读写分离 主库:主要负责数据的写入。 从库:主要负责数据的查询。 引出问题: 可能会存在主从延迟,导致主从一致性问题。查询主库的量级需要控制。数据量庞大,索引也占据存储空间,磁盘空间不足,当主库宕机后会影响所有模块的写入,需要进行数据分片,因此引出分库…...
专业渗透测试 Phpsploit-Framework(PSF)框架软件小白入门教程(一)
本系列课程,将重点讲解Phpsploit-Framework框架软件的基础使用! 本文章仅提供学习,切勿将其用于不法手段! Phpsploit-Framework(简称 PSF)框架软件,是一款什么样的软件呢? Phpspl…...
Web安全研究(七)
NDSS 2023 开源地址:https://github.com/bfpmeasurementgithub/browser-fingeprint-measurement 霍普金斯大学 文章结构 introbackground threat model measurement methodology step1: traffic analysisstep2: fingerprint analysis dataset attack statisticsbro…...
基于大模型的 UI 自动化系统
基于大模型的 UI 自动化系统 下面是一个完整的 Python 系统,利用大模型实现智能 UI 自动化,结合计算机视觉和自然语言处理技术,实现"看屏操作"的能力。 系统架构设计 #mermaid-svg-2gn2GRvh5WCP2ktF {font-family:"trebuchet ms",verdana,arial,sans-…...
阿里云ACP云计算备考笔记 (5)——弹性伸缩
目录 第一章 概述 第二章 弹性伸缩简介 1、弹性伸缩 2、垂直伸缩 3、优势 4、应用场景 ① 无规律的业务量波动 ② 有规律的业务量波动 ③ 无明显业务量波动 ④ 混合型业务 ⑤ 消息通知 ⑥ 生命周期挂钩 ⑦ 自定义方式 ⑧ 滚的升级 5、使用限制 第三章 主要定义 …...
【算法训练营Day07】字符串part1
文章目录 反转字符串反转字符串II替换数字 反转字符串 题目链接:344. 反转字符串 双指针法,两个指针的元素直接调转即可 class Solution {public void reverseString(char[] s) {int head 0;int end s.length - 1;while(head < end) {char temp …...
IoT/HCIP实验-3/LiteOS操作系统内核实验(任务、内存、信号量、CMSIS..)
文章目录 概述HelloWorld 工程C/C配置编译器主配置Makefile脚本烧录器主配置运行结果程序调用栈 任务管理实验实验结果osal 系统适配层osal_task_create 其他实验实验源码内存管理实验互斥锁实验信号量实验 CMISIS接口实验还是得JlINKCMSIS 简介LiteOS->CMSIS任务间消息交互…...
【VLNs篇】07:NavRL—在动态环境中学习安全飞行
项目内容论文标题NavRL: 在动态环境中学习安全飞行 (NavRL: Learning Safe Flight in Dynamic Environments)核心问题解决无人机在包含静态和动态障碍物的复杂环境中进行安全、高效自主导航的挑战,克服传统方法和现有强化学习方法的局限性。核心算法基于近端策略优化…...
处理vxe-table 表尾数据是单独一个接口,表格tableData数据更新后,需要点击两下,表尾才是正确的
修改bug思路: 分别把 tabledata 和 表尾相关数据 console.log() 发现 更新数据先后顺序不对 settimeout延迟查询表格接口 ——测试可行 升级↑:async await 等接口返回后再开始下一个接口查询 ________________________________________________________…...
深入浅出深度学习基础:从感知机到全连接神经网络的核心原理与应用
文章目录 前言一、感知机 (Perceptron)1.1 基础介绍1.1.1 感知机是什么?1.1.2 感知机的工作原理 1.2 感知机的简单应用:基本逻辑门1.2.1 逻辑与 (Logic AND)1.2.2 逻辑或 (Logic OR)1.2.3 逻辑与非 (Logic NAND) 1.3 感知机的实现1.3.1 简单实现 (基于阈…...
怎么让Comfyui导出的图像不包含工作流信息,
为了数据安全,让Comfyui导出的图像不包含工作流信息,导出的图像就不会拖到comfyui中加载出来工作流。 ComfyUI的目录下node.py 直接移除 pnginfo(推荐) 在 save_images 方法中,删除或注释掉所有与 metadata …...
MySQL:分区的基本使用
目录 一、什么是分区二、有什么作用三、分类四、创建分区五、删除分区 一、什么是分区 MySQL 分区(Partitioning)是一种将单张表的数据逻辑上拆分成多个物理部分的技术。这些物理部分(分区)可以独立存储、管理和优化,…...
手机平板能效生态设计指令EU 2023/1670标准解读
手机平板能效生态设计指令EU 2023/1670标准解读 以下是针对欧盟《手机和平板电脑生态设计法规》(EU) 2023/1670 的核心解读,综合法规核心要求、最新修正及企业合规要点: 一、法规背景与目标 生效与强制时间 发布于2023年8月31日(OJ公报&…...
