搜索
首页web前端css教程使用香草JavaScript制作音频波形可视化器

使用香草JavaScript制作音频波形可视化器

作为UI设计师,我不断地想起知道如何编码的价值。我为在设计用户界面时考虑团队中的开发人员而感到自豪。但是有时候,我踏上了技术地雷。

几年前,作为WSJ.com的设计总监,我正在帮助重新设计《华尔街日报》的播客目录。该项目上的一位设计师正在播客播放器上工作,我遇到了扩音器的嵌入式播放器。

我之前曾在SoundCloud工作,并且知道这些可视化对跳过音频的用户很有用。我想知道我们是否可以在《华尔街日报》的网站上实现类似的外观。

工程学的答案:绝对不是。给定时间表和限制,该项目不可能。最终,我们使用了更简单的播客播放器将重新设计的页面运送了。

但是我迷上了这个问题。在晚上和周末,我砍掉了试图实现这一效果的尝试。我学到了很多关于音频在网络上的工作方式的知识,并最终能够以少于100行的JavaScript实现外观!

事实证明,这个示例是熟悉Web音频API的理想方法,以及如何使用Canvas API可视化音频数据。

但是首先,关于数字音频的工作方式

在真实的模拟世界中,声音是一波。随着声音从源(像扬声器)传播到您的耳朵时,它会以您的耳朵和大脑作为音乐,语音,言语或狗的树皮等的方式压缩和解压缩空气,等等。

但是在计算机的电子信号世界中,声音不是波浪。为了将平滑,连续的波变成可以存储的数据,计算机可以做一些称为采样的事情。采样意味着测量声波每秒击中数千次麦克风,然后存储这些数据点。播放音频时,您的计算机会逆转过程:它会重新创建声音,一次是音频的一小部分。

声音文件中的数据点数取决于其采样率。您可能以前看过这个数字; MP3文件的典型样本率为44.1 kHz。这意味着,对于音频的每一秒,都有44,100个单个数据点。对于立体声文件,每秒有88,200个-44,100,左通道为44,100。这意味着30分钟的播客具有描述音频的158,760,000个单个数据点!

网页如何读取mp3?

在过去的九年中,W3C(帮助维护Web标准的人们)开发了Web Audio API,以帮助网络开发人员使用音频。网络音频API是一个非常深入的主题。我们几乎不会在本文中破解表面。但这一切都从称为AudioContext的东西开始。

将AudioContext视为使用音频的沙盒。我们可以用几行JavaScript初始化它:

 //设置音频上下文
window.audiocontext = window.audiocontext || window.webkitaudiocontext;
const audiocontext = new AudioContext();
令CurrentBuffer = null;

评论之后的第一行是必要的,因为Safari已将AudioContext作为Webkitaudiocontext实现。

接下来,我们需要将新的AudioContext提供我们要可视化的MP3文件。让我们使用…fetch()来获取它!

 const vunaleizeaudio = url => {
  提取(url)
    。
    。
    。
};

此功能采用URL,获取它,然后将响应对象转换几次。

  • 首先,它调用ArrayBuffer()方法,该方法返回 - 您猜对了 - arraybuffer! ArrayBuffer只是二进制数据的容器。这是一种在JavaScript中移动大量数据的有效方法。
  • 然后,我们通过DecodeAudioData()方法将ArrayBuffer发送到我们的AudioContext。 Decodeaudiodata()采用一个ArrayBuffer并返回AudioBuffer,这是用于读取音频数据的专用阵列。您知道浏览器带有所有这些方便的对象吗?当我启动这个项目时,我绝对没有。
  • 最后,我们将顾客发送给可视化。

过滤数据

为了可视化我们的录音机,我们需要减少我们使用的数据量。就像我之前提到的那样,我们从数百万个数据点开始,但是在最终可视化中我们会少得多。

首先,让我们限制正在使用的渠道。频道代表发送给单个演讲者的音频。在立体声声音中,有两个频道。在5.1环绕声中,有六个。 AudioBuffer具有内置方法可以做到这一点:GetChanneldata()。致电AudioBuffer.getChanneldata(0),我们将留下一个频道的数据价值。

接下来,困难部分:循环浏览频道的数据,然后选择一组较小的数据点。有几种方法可以解决这个问题。假设我希望我的最终可视化有70个栏。我可以将音频数据分为70个相等的部分,并查看每个数据点。

 const filterdata = audiobuffer => {
  const rawdata = audiobuffer.getChanneldata(0); //我们只需要使用一个数据渠道
  cont样品= 70; //我们想在最终数据集中拥有的样本数量
  const blocksize = math.floor(rawdata.length / samples); //每个细分中的样本数
  const FilledData = [];
  (让i = 0; i <sample i filtereddata.push blocksize><p>输出使我措手不及!看起来我们根本没有模拟的可视化。有很多数据点接近或零。但这很有意义:在播客中,单词和句子之间有很多沉默。通过仅查看每个块中的第一个样本,我们很有可能会抓住一个非常安静的时刻。</p>
<p>让我们修改算法以找到样品的<em>平均值</em>。当我们使用它时,我们应该拿出数据的绝对价值,以使所有数据都是积极的。</p>
<pre rel="JavaScript" data-line=""> const filterdata = audiobuffer => {
  const rawdata = audiobuffer.getChanneldata(0); //我们只需要使用一个数据渠道
  cont样品= 70; //我们想在最终数据集中拥有的样本数量
  const blocksize = math.floor(rawdata.length / samples); //每个细分中的样本数量
  const FilledData = [];
  (让i = 0; i <sample i blocksize for j sum="sum" math.abs filtereddata.push><p>让我们看看这些数据是什么样的。</p>
<p>这很棒。只剩下一件事要做:因为我们在音频文件中有太多的沉默,所以数据点的结果平均值很小。为了确保此可视化适用于所有音频文件,我们需要将数据<em>标准化</em>;也就是说,更改数据的比例,以使最大的样本测量为1。</p>
<pre rel="JavaScript" data-line=""> const AranistrizedAta = FilterData => {
  cont Multiplier = Math.pow(Math.max(... FilledData),-1);
  返回FilteredData.map(n => n *乘数);
}

此功能通过Math.max()找到数组中最大的数据点,将其与Math.pow(n,-1)倒数,并将数组中的每个值乘以该数字。这确保最大数据点将设置为1,其余数据将按比例扩展。

现在我们有了正确的数据,让我们编写可以可视化它的功能。

可视化数据

为了创建可视化,我们将使用JavaScript帆布API。此API将图形绘制为HTML元素。使用Canvas API的第一步类似于Web音频API。

 const draw = normalizeddata => {
  //设置画布
  const canvas = document.queryselector(“ canvas”);
  const dpr = window.devicepixelratio || 1;
  start填充= 20;
  canvas.width = canvas.offsetWidth * dpr;
  canvas.height =(canvas.offsetheight填充 * 2) * dpr;
  const ctx = canvas.getContext(“ 2d”);
  CTX.Scale(DPR,DPR);
  ctx.translate(0,canvas.offsetheight / 2填充); //设置y = 0放在画布的中间
};

该代码在页面上找到元素,并检查浏览器的像素比(本质上是屏幕的分辨率),以确保我们的图形将以正确的尺寸绘制。然后,我们获得画布的上下文(其单个方法和价值集)。我们计算画布的像素尺寸,并以像素比并增加一些填充。最后,我们更改的坐标系统,默认情况下(0,0)位于盒子的顶部,但是我们可以通过设置(0,0)在左边缘中间来节省很多数学。

现在让我们画一些台词!首先,我们将创建一个将绘制单个段的函数。

 const drawlinesegnement =(ctx,x,y,width,iseven)=> {
  ctx.linewidth = 1; //线有多厚
  ctx.strokestyle =“ #fff”; //我们的颜色是什么颜色
  ctx.beginath();
  y = iseven? Y:-Y;
  ctx.moveto(x,0);
  ctx.lineto(x,y);
  ctx.arc(x宽度 / 2,y,width / 2,math.pi,0,iseven);
  ctx.lineto(x宽度,0);
  ctx.stroke();
};

帆布API使用称为“乌龟图形”的概念。想象一下,代码是用标记的一组指令。从基本的角度来看,dravlinesement()函数的工作原理如下:

  1. 从中心线开始,x = 0。
  2. 画一条垂直线。使线的高度相对于数据。
  3. 绘制半圆的宽度。
  4. 将一条垂直线恢复到中心线。

大多数命令很简单:ctx.moveto()和ctx.lineto()分别将乌龟移至指定的坐标,无需绘制或绘图时。

第5行,y = iseven? -y:y,告诉我们的乌龟是从中心线划定还是从中间线划下来。这些细分在中心线以上和下方之间交替,以形成平滑的波。在画布API的世界中,y值比积极的值更高。这有点违反直觉,因此请记住它是可能的错误来源。

在第8行上,我们绘制半圆。 ctx.arc()采用六个参数:

  • 圆心中心的XY坐标
  • 圆的半径
  • 在圆圈中开始绘画的位置(Math.pi或π是弧度,9点钟的位置)
  • 圆圈中的位置完成绘图(弧度为0代表3点钟)
  • 一个布尔值告诉我们的乌龟逆时针(如果为true)或顺时针(如果为false)。在最后一个参数中使用iseven意味着我们将在偶数段的段落中绘制一个圆的上半部分 - 顺时针从9点到3个时钟 - 偶数段,下半部分为奇数段。

好的,返回draw()函数。

 const draw = normalizeddata => {
  //设置画布
  const canvas = document.queryselector(“ canvas”);
  const dpr = window.devicepixelratio || 1;
  start填充= 20;
  canvas.width = canvas.offsetWidth * dpr;
  canvas.height =(canvas.offsetheight填充 * 2) * dpr;
  const ctx = canvas.getContext(“ 2d”);
  CTX.Scale(DPR,DPR);
  ctx.translate(0,canvas.offsetheight / 2填充); //设置y = 0放在画布的中间

  //绘制线段
  const width = canvas.offsetWidth / formoryData.length;
  for(让i = 0; i <normanizeddata.length i const x="宽度" canvas.offsetheight else if> canvas.offsetheight / 2){
        高度=高度> canvas.offsetheight / 2;
    }
    DravlinEseg(CTX,X,高度,宽度,(I 1)%2);
  }
};</normanizeddata.length>

在以前的设置代码之后,我们需要计算每个行段的像素宽度。这是画布的屏幕宽度,除以我们要显示的片段的数量。

然后,一个循环通过数组中的每个条目,并使用我们之前定义的函数绘制线段。我们将X值设置为当前迭代的索引,次数段宽度。高度,所需的细分市场高度来自将我们的归一数据乘以画布的高度,减去我们之前设置的填充。我们检查一些情况:减去填充物可能已将高度推入负面,因此我们将其重新定为零。如果该段的高度将导致一条线从画布的顶部提取,我们将高度重新设置为最大值。

我们通过片段宽度,对于十个值,我们使用一个整洁的技巧:(i 1)%2表示“找到i 1的其余部分除以2。”我们检查I 1是因为我们的计数器从0开始。如果我1是偶数,则其余部分为零(或false)。如果我很奇怪,其余的将为1或真实。

这就是她写的。让我们把这一切放在一起。这是整个脚本,其所有荣耀。

在drawaudio()函数中,我们在最终呼叫中添加了一些函数:draw(farrydata(audioBuffer)))。该链过滤器归一化,并最终绘制我们从服务器中恢复的音频。

如果一切都按计划进行,您的页面应该看起来像这样:

关于性能的注释

即使进行了优化,此脚本仍可能在浏览器中运行数十万个操作。根据浏览器的实现,这可能需要几秒钟才能完成,并且会对页面上发生的其他计算产生负面影响。它还在绘制可视化之前下载整个音频文件,这会消耗大量数据。有几种方法可以改进脚本解决这些问题:

  1. 分析服务器端的音频。由于音频文件不经常更改,因此我们可以利用服务器端计算资源来过滤和标准化数据。然后,我们只需要传输较小的数据集即可。无需下载MP3即可绘制可视化!
  2. 仅在用户需要时绘制可视化。无论我们如何分析音频,都可以将过程推迟到页面加载后。我们可以等到使用相交观察者查看元素,或者延迟更长的时间,直到用户与播客播放器进行交互。
  3. 渐进式增强。在探索扩音器的播客播放器时,我发现他们的可视化只是一个立面 - 每个播客都是相同的波形。这可能是我们(非常优越的)设计的巨大默认。使用渐进式增强原则,我们可以加载默认图像作为占位符。然后,我们可以检查在启动脚本之前加载真实波形是否有意义。如果用户已禁用JavaScript,则其浏览器不支持Web Audio API,或者他们的Save-Data标头集,则没有任何损坏。

我也很想听到你们对优化的任何想法。

一些结束的想法

这是一种可视化音频的非常非常不切实际的方式。它在客户端运行,将数百万个数据点处理为相当简单的可视化。

但这很酷!我在编写此代码方面学到了很多东西,甚至在撰写本文时就学到了更多。我重构了很多原始项目,并将整个过程缩小了一半。像这样的项目可能永远不会继续看到生产代码库,但是它们是发展新技能和对一些整洁的API现代浏览器支持的独特机会。

我希望这是一个有用的教程。如果您对如何改进它或主题的任何酷炫变化有想法,请伸出援手!我在Twitter上@ilikescience。

以上是使用香草JavaScript制作音频波形可视化器的详细内容。更多信息请关注PHP中文网其他相关文章!

声明
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn
Draggin&#039;和droppin&#039;在反应中Draggin&#039;和droppin&#039;在反应中Apr 17, 2025 am 11:52 AM

React生态系统为我们提供了许多库,所有库都集中在拖放的相互作用上。我们有反应,反应,可爱dnd,

快速软件快速软件Apr 17, 2025 am 11:49 AM

最近有一些关于快速软件的完美互连的事情。

带有背景折叠的嵌套梯度带有背景折叠的嵌套梯度Apr 17, 2025 am 11:47 AM

我可以说我经常使用背景折叠。 IT Wager IT几乎从未在日常CSS工作中使用。但是在斯特凡·朱迪斯(Stefan Judis)的帖子中,我想起了它,

使用React Hooks使用requestAnimationFrame使用React Hooks使用requestAnimationFrameApr 17, 2025 am 11:46 AM

使用RequestAnimationFrame进行动画化应该很容易,但是如果您还没有彻底阅读React的文档,那么您可能会遇到一些事情

需要滚动到页面顶部吗?需要滚动到页面顶部吗?Apr 17, 2025 am 11:45 AM

向用户提供此链接的最简单方法是针对元素上的ID的链接。如此...

最好的(GraphQl)API是您编写的API最好的(GraphQl)API是您编写的APIApr 17, 2025 am 11:36 AM

听着,我不是GraphQL专家,但我确实喜欢与之合作。作为前端开发人员,它向我曝光数据的方式非常酷。它就像一个菜单

在保留边框半径的同时,扩展盒子的各种方法在保留边框半径的同时,扩展盒子的各种方法Apr 17, 2025 am 11:19 AM

我最近注意到Codepen的一个有趣的更改:在悬停在主页上的笔时,有一个矩形,圆角在后面扩展。

See all articles

热AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智能驱动的应用程序,用于创建逼真的裸体照片

AI Clothes Remover

AI Clothes Remover

用于从照片中去除衣服的在线人工智能工具。

Undress AI Tool

Undress AI Tool

免费脱衣服图片

Clothoff.io

Clothoff.io

AI脱衣机

AI Hentai Generator

AI Hentai Generator

免费生成ai无尽的。

热门文章

R.E.P.O.能量晶体解释及其做什么(黄色晶体)
1 个月前By尊渡假赌尊渡假赌尊渡假赌
R.E.P.O.最佳图形设置
1 个月前By尊渡假赌尊渡假赌尊渡假赌
威尔R.E.P.O.有交叉游戏吗?
1 个月前By尊渡假赌尊渡假赌尊渡假赌

热工具

禅工作室 13.0.1

禅工作室 13.0.1

功能强大的PHP集成开发环境

WebStorm Mac版

WebStorm Mac版

好用的JavaScript开发工具

SublimeText3 Mac版

SublimeText3 Mac版

神级代码编辑软件(SublimeText3)

DVWA

DVWA

Damn Vulnerable Web App (DVWA) 是一个PHP/MySQL的Web应用程序,非常容易受到攻击。它的主要目标是成为安全专业人员在合法环境中测试自己的技能和工具的辅助工具,帮助Web开发人员更好地理解保护Web应用程序的过程,并帮助教师/学生在课堂环境中教授/学习Web应用程序安全。DVWA的目标是通过简单直接的界面练习一些最常见的Web漏洞,难度各不相同。请注意,该软件中

Dreamweaver Mac版

Dreamweaver Mac版

视觉化网页开发工具