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

前言

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

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

总结

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

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

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

相关推荐
漫天黄叶远飞2 分钟前
把原型链画成地铁图:坐 3 站路就能看懂 JS 的“继承”怎么跑
前端·javascript
bank_dreamer5 分钟前
VSCODE前端代码风格格式化
前端·css·vscode·html·js·prettier·代码格式化
IT_陈寒25 分钟前
90%的Python开发者不知道:这5个内置函数让你的代码效率提升300%
前端·人工智能·后端
网络点点滴27 分钟前
Vue3的生命周期
前端·javascript·vue.js
梵得儿SHI43 分钟前
Vue 核心语法之组件基础与通信:从创建到注册的完整指南
前端·javascript·vue.js·组件化开发·全局注册·vue组件的本质·局部注册和异步组件
MQliferecord1 小时前
如何快速实现响应式多屏幕适配
前端
韭菜炒大葱1 小时前
从回调到async/await:JavaScript异步编程的进化之路
前端·javascript·面试
凌晨起床1 小时前
前端开发规范
前端
Cache技术分享1 小时前
247. Java 集合 - 为什么要远离 Stack 类?
前端·后端