一文详解Web Audi 绘制音频图谱

 更新时间:2023年03月09日 11:27:22   作者:前端zuker  
这篇文章主要为大家介绍了Web Audi 绘制音频图谱实现示例详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪

背景

前端处理音频,目前一些开源的插件和js库已经提供了非常好的支持。其中小编了解的比较多的是sound.js和wavasuffer.js这俩个库。其中sound.js是一个大而全的音频处理库,功能丰富,兼容性也处理的很好。wavesuffer则偏重于音频波形图绘制处理,相对比较轻量。小编此篇不在于比较二者的差异,而是和大家一起学习下如何自己实现一个简易的音频图谱绘制。

实现思路

先介绍下小编的整体思路吧。所谓的音频图谱,其实只是将声音的响度具象化为一个波形图,响度高对应的波形高,响度低波形也就低。所以第一步,我们可以通过xhr拿到一个音频文件的数据。那么,第二步便是如何处理这组数据,让数据能够比较真实的反应音频的响度。这时候就需要前端的Web Audio Api来发挥作用了,具体如何处理,我们后面详细说明。完成数据处理之后,最后一步就是需要根据数据绘制出波形图,这里我们使用canvas来做波形图的绘制。

获取音频文件

首先,我们利用fetch,来获取一个线上音频。这里,我们借用一下wavesuffer官网demo中用的线上音频来做示范。

// 音频url
let audioUrl = 'https://wavesurfer-js.org/example/media/demo.wav';
// 创建音频上下文
let audioCtx = new (window.AudioContext || window.webkitAudioContext)();
// 创建音频源
let source = audioCtx.createBufferSource();
/* 
 * 通过fetch下载音频,responseType设置为'arrayBuffer',我们以arrayBuffer格式接收返回的数据
*/
fetch(audioUrl, {
  method: 'GET',
  responseType: 'arraybuffer',
}).then(res => {
  return res.arrayBuffer();
}).then(data => {
  // 处理音频数据
  initAudio(data);
});

利用Web Audio Api 处理音频数据

拿到音频数据之后,我们需要利用Web Audio Api,来处理音频数据,实现音频的播放,暂停等操作以及我们后续的波形图绘制。这里简单介绍下,Web Audio Api是一组非常强大的Api,它提供了在Web中控制音频、处理音频的一整套有效通用的系统。它能够允许开发着,控制音频,自选音频源、对音频添加特效,使音频可视化,添加空间效果,添加混响等等。而我们今天要实现的功能,仅仅只用到了其中几个Api,整体流程如下:

// audio 初始化
function initAudio (data) {
  // 音频数据解码
  // decodeAudioData方法接收一个arrayBuffer数据作为参数,这也是为什么前面fetch音频时设置以arrayBuffer格式接收数据
  audioCtx.decodeAudioData(data).then(buffer => {
    // decodeAudioData解码完成后,返回一个AudioBuffer对象
    // 绘制音频波形图
    drawWave(buffer);
    // 连接音频源
    source.buffer = buffer;
    source.connect(audioCtx.destination);
    // 音频数据处理完毕
    alert('音频数据处理完毕!');
  });
}
// web audio 规范不允许音频自动播放,需要用户触发页面事件来触发播放,这里我们增加一个播放按钮,数据处理完毕后点击播放
document.querySelector('#btn').onclick = () => {
  // 播放音频
  source.start(0);
}

通过解码后的音频数据,绘制波形图

音频数据通过AudioContext解码后,返回一个AudioBuffer对象,这个对象,保存有音频的采样率、声道、pcm数据等信息。通过getChannelData方法可以获取到音频某个声道的pcm数据。返回的是一个Float32Array对象,数值范围在-1到1之间。音频数据比较庞大,每一秒钟可能包含成千上万的数据,因此我们在做图形绘制时,需要对数据进一步采样。比如,这里我们采用每1000条数据中,取一个最大值(正数)一个最小值(负数)来绘制图形;

// 绘制波形图
function drawWave (buffer) {
  // buffer.numberOfChannels返回音频的通道数量,1即为单声道,2代表双声道。这里我们只取一条通道的数据
  let data = [];
  let originData = buffer.getChannelData(0);
  // 存储所有的正数据
  let positives = [];
  // 存储所有的负数据
  let negatives = [];
  // 先每隔100条数据取1条
  for (let i = 0; i < originData.length; i += 100) {
    data.push(originData[i]);
  }
  // 再从data中每10条取一个最大值一个最小值
  for (let j = 0, len = parseInt(data.length / 10); j < len; j++) {
    let temp = data.slice(j * 10, (j + 1) * 10);
    positives.push(Math.max.apply(null, temp));
    negatives.push(Math.min.apply(null, temp));
  }
  // 创建canvas上下文
  let canvas = document.querySelector('#canvas');
  if (canvas.getContext) {
    let ctx = canvas.getContext('2d');
    canvas.width = positives.length;
    let x = 0;
    let y = 100;
    let offset = 0;
    ctx.fillStyle = '#fa541c';
    ctx.beginPath();
    ctx.moveTo(x, y);
    // canvas高度200,横坐标在canvas中点100px的位置,横坐标上方绘制正数据,下方绘制负数据
    // 先从左往右绘制正数据
    // x + 0.5是为了解决canvas 1像素线条模糊的问题
    for (let k = 0; k < positives.length; k++) {
      ctx.lineTo(x + k + 0.5, y - (100 * positives[k]));
    }
    // 再从右往左绘制负数据
    for (let l = negatives.length - 1; l >= 0; l--) {
      ctx.lineTo(x + l + 0.5, y + (100 * Math.abs(negatives[l])));
    }
    // 填充图形
    ctx.fill();
  }
};

这样,简单的音频波形图绘制就完成了。小编这里仅做抛砖引玉,简单介绍下Web Audio的一个应用场景。更多更复杂的应用,大家可以深入了解学习Web Audio相关api。最后,贴一下效果图:

以上就是一文详解Web Audi 绘制音频图谱的详细内容,更多关于Web Audio绘制音频图谱的资料请关注脚本之家其它相关文章!

相关文章

  • 基于JS实现导航条之调用网页助手小精灵的方法

    基于JS实现导航条之调用网页助手小精灵的方法

    在网站中加入网页助手小精灵,当用户访问网站时,向用户问好,或是传递一些网站的重要信息,给用户带来极好的体验感,那么基于js代码是如何调用网页助手小精灵的呢?下面跟着脚本之家小编一起学习吧
    2016-06-06
  • 浅聊一下TypeScript中的4种类型守卫

    浅聊一下TypeScript中的4种类型守卫

    类型守卫是TypeScript中特有的用于在运行时检查类型的方式,它显式的将javascript代码按类型划分,从而确保了运行安全,下面我们就来简单聊聊TypeScript中的4种类型守卫吧
    2023-08-08
  • 使用 Angular 服务器端渲染 Transfer State Service

    使用 Angular 服务器端渲染 Transfer State Service

    这篇文章主要介绍了使用 Angular 服务器端渲染 Transfer State Service,假设我们使用 Angular Universal 开发一个服务器端渲染的 Angular 应用,这个应用会消费一个第三方的 Restful API
    2022-06-06
  • js实现文本框选中的方法

    js实现文本框选中的方法

    这篇文章主要介绍了js实现文本框选中的方法,涉及javascript中select()方法的使用技巧,需要的朋友可以参考下
    2015-05-05
  • webpack下实现动态引入文件方法

    webpack下实现动态引入文件方法

    下面小编就为大家分享一篇webpack下实现动态引入文件方法,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2018-02-02
  • layui 实现表格某一列显示图标

    layui 实现表格某一列显示图标

    今天小编就为大家分享一篇layui 实现表格某一列显示图标的例子,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2019-09-09
  • 常用JS代码实例小结

    常用JS代码实例小结

    跟网上的一些常用的不太一样,个人都有个人常用的代码,大家看到好东西,不一定什么都会,起码要知道有这么个东西。方便以后用也方便找。
    2009-04-04
  • 理解Javascript_08_函数对象

    理解Javascript_08_函数对象

    如果你无法理解博文在讲什么,请回顾前面的系列博文。文章比较深入,如有不对之处,望请指正,谢谢。
    2010-10-10
  • uni-app中Navigator组件的用法详解及传参方式

    uni-app中Navigator组件的用法详解及传参方式

    这篇文章主要给大家介绍了关于uni-app中Navigator组件的用法详解及传参方式的相关资料,在实际应用开发中我们经常要使用到路由跳转,在uniapp官网中提供了navigator内置组件,供我们使用,需要的朋友可以参考下
    2023-09-09
  • javascript设计模式之Adapter模式【适配器模式】实现方法示例

    javascript设计模式之Adapter模式【适配器模式】实现方法示例

    这篇文章主要介绍了javascript设计模式之Adapter模式,结合实例形式分析了JS适配器模式的原理与具体实现方法,具有一定参考借鉴价值,需要的朋友可以参考下
    2017-01-01

最新评论