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

前言

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

本文将介绍了使用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的支持情况可能有所不同,代码中使用了AudioContext和webkitAudioContext的兼容性写法,以确保在更多的浏览器中正常工作。

decodeAudioData方法

decodeAudioData是AudioContext提供的一个异步方法,用于将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和事件,方便开发者进行音频操作和界面自定义。

总结

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

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

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

相关推荐
风骏时光牛马19 分钟前
用户管理模块接口开发
前端
彭亚川Allen41 分钟前
从零手搓 RAG 之检索全流程,给大模型接上自己的知识库
前端·后端·ai编程
IT_陈寒43 分钟前
别让Java字符串拼接偷偷吃掉你的内存
前端·人工智能·后端
软件技术NINI1 小时前
圣罗兰彩妆html+css 4页
前端·css·html
2601_951615202 小时前
网站模板 网站网页模板下载
前端·网络·html
酷炫码神2 小时前
Day 1|HTML5:一个商城首页的骨架,从 DOCTYPE 到语义化
前端·html·html5
AI源码3 小时前
鲜花商城(HTML 版)
前端·html
hiahiahia1233 小时前
Message 和对话
服务器·前端·数据库
溪语流沙3 小时前
【Web全栈进阶】Composition API与组件通信:props深入、emit、插槽
前端·css·vue.js
ZoeLandia3 小时前
TypeScript 03:泛型与工具类型
前端·typescript