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

前言

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

本文将介绍了使用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和事件,方便开发者进行音频操作和界面自定义。

总结

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

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

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

相关推荐
夜郎king12 分钟前
HTML5 SVG 实现日出日落动画与实时天气可视化
前端·html5·svg 日出日落
夏幻灵1 小时前
HTML5里最常用的十大标签
前端·html·html5
Mr Xu_1 小时前
Vue 3 中 watch 的使用详解:监听响应式数据变化的利器
前端·javascript·vue.js
未来龙皇小蓝1 小时前
RBAC前端架构-01:项目初始化
前端·架构
程序员agions2 小时前
2026年,微前端终于“死“了
前端·状态模式
万岳科技系统开发2 小时前
食堂采购系统源码库存扣减算法与并发控制实现详解
java·前端·数据库·算法
程序员猫哥_2 小时前
HTML 生成网页工具推荐:从手写代码到 AI 自动生成网页的进化路径
前端·人工智能·html
龙飞052 小时前
Systemd -systemctl - journalctl 速查表:服务管理 + 日志排障
linux·运维·前端·chrome·systemctl·journalctl
我爱加班、、2 小时前
Websocket能携带token过去后端吗
前端·后端·websocket
AAA阿giao2 小时前
从零拆解一个 React + TypeScript 的 TodoList:模块化、数据流与工程实践
前端·react.js·ui·typescript·前端框架