前端纯手工绘制音频波形图

前言

音频波形图是音频文件的可视化展示,需要解析音频数据,然后绘制为图形;而在现代浏览器中,已经具备了这两种能力。

本文将介绍了使用js原生接口,不借助第三方库,实现音频文件的波形绘制。通过使用<canvas>元素和Web Audio API,我们将音频数据以矩形波形的形式展示在画布上。

示例地址,可以体验一下:fabricjs-demo.videocovert.online/#/AudioWave

总体思路

用户选择音频文件后,代码将解析音频数据,并在画布上以矩形条的形式显示音频波形。每个矩形条表示音频的一个采样点,通过不同的高度来反映音频信号的振幅。

完整代码实现:

js 复制代码
<template>
  <div>
    <input type="file" @change="handleFileUpload" accept="audio/*" />
    <canvas ref="canvas" :width="canvasWidth" :height="canvasHeight"></canvas>
  </div>
</template>

<script setup>
import { ref } from 'vue';

const canvas = ref(null);
const canvasWidth = 800;  // 画布宽度
const canvasHeight = 200; // 画布高度

let audioContext;
let dataArray;

const handleFileUpload = (event) => {
  const file = event.target.files[0];
  if (file) {
    audioContext = new (window.AudioContext || window.webkitAudioContext)();
    const reader = new FileReader();

    reader.onload = function(e) {
      audioContext.decodeAudioData(e.target.result, buffer => {
        dataArray = buffer.getChannelData(0); // 获取左声道数据
        drawRectWaveform();
      });
    };

    reader.readAsArrayBuffer(file);
  }
};

const drawRectWaveform = () => {
  const canvasContext = canvas.value.getContext('2d');

  canvasContext.clearRect(0, 0, canvasWidth, canvasHeight);

  const barWidth = 2;    // 每个矩形的宽度
  const gap = 1;         // 每个矩形之间的间隔
  const step = Math.ceil(dataArray.length / (canvasWidth / (barWidth + gap))); // 采样步长
  let x = 0;

  for (let i = 0; i < dataArray.length; i += step) {
    const v = dataArray[i] * 0.5; // 适当缩放波形高度
    const y = (canvasHeight / 2) + (v * canvasHeight / 2);
    const height = Math.abs(v * canvasHeight); // 矩形高度
    
    canvasContext.fillStyle = 'rgb(0, 0, 255)';
    canvasContext.fillRect(x, canvasHeight / 2 - height / 2, barWidth, height);
    
    x += barWidth + gap;
  }
};
</script>

<style scoped>
canvas {
  border: 1px solid #ccc;
  margin-top: 10px;
}
</style>

处理文件上传并解析音频

javascript 复制代码
const handleFileUpload = (event) => {
  const file = event.target.files[0];
  if (file) {
    audioContext = new (window.AudioContext || window.webkitAudioContext)();
    const reader = new FileReader();

    reader.onload = function(e) {
      audioContext.decodeAudioData(e.target.result, buffer => {
        dataArray = buffer.getChannelData(0); // 获取左声道数据
        drawRectWaveform();
      });
    };

    reader.readAsArrayBuffer(file);
  }
};

handleFileUpload函数在用户选择音频文件时触发。首先,它通过FileReader读取文件,并将其解码为音频数据。decodeAudioData方法将音频数据转换为音频缓冲区对象,并获取左声道数据(getChannelData(0)),存储在dataArray中。最后,调用drawRectWaveform函数绘制波形图。

AudioContext对象

AudioContext是Web Audio API的核心接口,用于管理和处理音频内容。在这个语句中,我们创建了一个新的AudioContext实例,它为后续的音频解码和处理提供了基础。

由于不同浏览器对Web Audio API的支持情况可能有所不同,代码中使用了AudioContextwebkitAudioContext的兼容性写法,以确保在更多的浏览器中正常工作。

decodeAudioData方法

decodeAudioDataAudioContext提供的一个异步方法,用于将ArrayBuffer格式的音频文件解码为AudioBuffer对象。这个解码过程将音频文件解析为包含音频数据的对象,供后续的音频处理使用。

javascript 复制代码
audioContext.decodeAudioData(e.target.result, buffer => {

上面的代码中,e.target.result包含了音频文件的ArrayBuffer数据。decodeAudioData方法将其解码为AudioBuffer对象,并在解码成功后调用回调函数。

绘制波形图

ini 复制代码
javascript
复制代码
const drawRectWaveform = () => {
  const canvasContext = canvas.value.getContext('2d');

  canvasContext.clearRect(0, 0, canvasWidth, canvasHeight);

  const barWidth = 2;    // 每个矩形的宽度
  const gap = 1;         // 每个矩形之间的间隔
  const step = Math.ceil(dataArray.length / (canvasWidth / (barWidth + gap))); // 采样步长
  let x = 0;

  for (let i = 0; i < dataArray.length; i += step) {
    const v = dataArray[i] * 0.5; // 适当缩放波形高度
    const y = (canvasHeight / 2) + (v * canvasHeight / 2);
    const height = Math.abs(v * canvasHeight); // 矩形高度
    
    canvasContext.fillStyle = 'rgb(0, 0, 0)';
    canvasContext.fillRect(x, canvasHeight / 2 - height / 2, barWidth, height);
    
    x += barWidth + gap;
  }
};

drawRectWaveform函数负责在Canvas上绘制波形。首先,它清空画布,然后计算绘制波形的步长(step),确保在画布宽度内均匀分布波形数据。接着,在每个步长处绘制一个矩形,矩形的高度表示音频信号的振幅。canvasContext.fillRect用于在指定位置绘制矩形。

相关库推荐

wavesurfer.js

GitHub项目地址: github.com/katspaugh/w...

功能强大的音频波形图库,支持多种音频格式,并且可以通过插件进行扩展。wavesurfer.js可以创建交互式的波形图,并且提供了丰富的API和事件,方便开发者进行音频操作和界面自定义。

总结

在具体的开发时,成熟的音频波形图组件库和自己实现波形图,可以根据需求做选择。

如果我们的需求比较简单,我们可以自己实现,避免引入组件库中无关的功能,导致项目体积过大;或者我们的需求比较特殊、比较定制化,我们也可以自己实现。

但是如果需求比较复杂,并且组件库能够满足,那还是选择组件库,不需要重复造轮子,在工作中,时间就是金钱!

相关推荐
小毛驴8505 分钟前
创建 Vue 项目的 4 种主流方式
前端·javascript·vue.js
誰能久伴不乏34 分钟前
Linux如何执行系统调用及高效执行系统调用:深入浅出的解析
java·服务器·前端
涔溪2 小时前
响应式前端设计:CSS 自适应布局与字体大小的最佳实践
前端·css
今禾2 小时前
前端开发中的Mock技术:深入理解vite-plugin-mock
前端·react.js·vite
你这个年龄怎么睡得着的2 小时前
Babel AST 魔法:Vite 插件如何让你的 try...catch 不再“裸奔”?
前端·javascript·vite
我想说一句2 小时前
掘金移动端React开发实践:从布局到样式优化的完整指南
前端·react.js·前端框架
jqq6662 小时前
Vue3脚手架实现(九、渲染typescript配置)
前端
码间舞2 小时前
Zustand 与 useSyncExternalStore:现代 React 状态管理的极简之道
前端·react.js
Dream耀2 小时前
提升React移动端开发效率:Vant组件库
前端·javascript·前端框架
冰菓Neko2 小时前
HTML 常用标签速查表
前端·html