js 选择一个音频文件,绘制音频的波形,从右向左逐渐前进。

选择一个音频文件,绘制波形,从右向左逐渐前进。

完整代码:

dart 复制代码
<template>
  <div>
    <input type="file" @change="handleFileChange" accept="audio/*" />
    <button @click="stopPlayback" :disabled="!isLoaded">停止</button>
    <canvas ref="canvas" width="1201" height="211"></canvas>
  </div>
</template>

<script>
import axios from "axios";

export default {
  data() {
    return {
      // audioUrl:"http://121.41.225.74:9091/mintti/app/storage/newFile/b26uvk9ipd8n5iop1lzs.wav",
      audioUrl: "http://121.41.225.74:9091/mintti/app/storage/newFile/c19xqqqtd8ywqyaf8gno.wav",
      // audioUrl: "http://121.41.225.74:9091/mintti/app/storage/newFile/d3msxipdfxrbyijm3ys0.wav",
      // audioUrl:"http://121.41.225.74:9091/mintti/app/storage/newFile/xm456t9dptsrigxye84q.wav",

      dataArray: [],
      isPlaying: false,
      isLoaded: false,
      drawInterval: 200, // 设置绘制的时间间隔(单位:毫秒)
      drawIntervalId: null,
      fileData: new Int8Array(0),
      index: 0,
      mWidth: 0,
      mHeight: 0,
    }
  },
  mounted() {
    const ctx = this.$refs.canvas.getContext('2d')
    this.drawGrid(ctx)
    this.downloadAudio()
  },
  methods: {
    // 下载音频文件
    downloadAudio() {
      axios({
        method: 'get',
        url: this.audioUrl,
        responseType: 'arraybuffer'
      }).then(res => {
        if (!res) {
          return;
        }

        console.log("decodeAudioData")
        this.loadAudio(res.data)
      }).catch(error => {
        console.error('下载音频时出错:', error);
      });;
    },

    handleFileChange(event) {
      this.isLoaded = false
      const file = event.target.files[0]

      this.stopPlayback()
      const reader = new FileReader();
      reader.onload = (e) => {
        console.log("onLoad")
        this.loadAudio(e.target.result)

      };

      reader.readAsArrayBuffer(file);
    },

    loadAudio(res) {
      this.dataArray = []
      this.isLoaded = true
      this.index = 0;
      // 获取文件的前 100 个字节
      this.fileData = new Int8Array(res);
      this.refreshData()

      this.drawIntervalId = setInterval(() => {
        console.log("定时器执行了")
        this.refreshData()
      }, this.drawInterval)//循环读取
    },
    refreshData() {
      let i = this.index
      console.log("文件总长度:" + this.fileData.byteLength + ",,i=" + i)
      if (i * 1600 + 44 > this.fileData.byteLength) {
        clearInterval(this.drawIntervalId)
        return
      }
      const byteArray = this.fileData.slice(i * 1600 + 44, (i + 1) * 1600 + 44);

      // 创建一个新的 Uint16Array,长度为 byteArray 的一半
      let shortArray = new Int16Array(byteArray.length / 2)

      //遍历 byteArray,将每两个字节合并成一个短整型
      for (let i = 0; i < byteArray.length; i += 2) {
        shortArray[i / 2] = (byteArray[i] & 0xFF) | (byteArray[i + 1] & 0xFF) << 8;
      }

      const step = 10;

      for (let i = 0; i < shortArray.length; i += step) {
        // console.log(i + "文件short值:" + shortArray[i])
        if (this.mWidth > 0 && this.dataArray.length >= this.mWidth) {
          this.dataArray.shift()
        }
        this.dataArray.push(shortArray[i])
      }

      this.isPlaying = true
      this.draw2();
      this.index += 1;
    },
    stopPlayback() {
      console.log("停止播放-stopPlayback")
      this.isPlaying = false

      clearInterval(this.drawIntervalId)
      const ctx = this.$refs.canvas.getContext('2d')
      ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height)
      this.drawGrid(ctx)
    },

    draw2() {
      if (!this.isPlaying) {
        return
      }
      // console.log('开始绘图-draw')

      const ctx = this.$refs.canvas.getContext('2d')
      ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height)
      this.drawGrid(ctx)
      this.drawWaveform(ctx)
    },
    drawGrid(ctx) {
      const { width, height } = ctx.canvas
      this.mWidth = ctx.canvas.width
      this.mHeight = ctx.canvas.height

      ctx.strokeStyle = '#ccc'
      ctx.lineWidth = 1

      for (let i = 0; i < height; i += 10) {
        ctx.beginPath()
        ctx.moveTo(0, i)
        ctx.lineTo(width, i)
        ctx.stroke()
      }

      for (let j = 0; j < width; j += 10) {
        ctx.beginPath()
        ctx.moveTo(j, 0)
        ctx.lineTo(j, height)
        ctx.stroke()
      }
    },
    drawWaveform(ctx) {
      ctx.beginPath()
      ctx.lineWidth = 1
      ctx.strokeStyle = '#25ebd7'


      let x = 0
      let len = this.dataArray.length;
      let index = this.mWidth - len;
      for (let i = index + 1; i < this.mWidth; i++) {
        const mCenterY = this.mHeight / 2;
        const y = mCenterY - (this.dataArray[i - index - 1] / (32768 / mCenterY));
        // console.log(`i=${i},position=${i - index - 1},,data=${this.dataArray[i - index - 1]},,y=${y},,mCenterY=${mCenterY}`)
        x = i - 1;

        ctx.lineTo(x, y)
        ctx.stroke()
      }
    },
  }
}
</script>
相关推荐
香芋芋圆2 小时前
AI 冲击内卷之下,普通前端如何破局?WebGIS—— 低门槛突围赛道
前端·javascript·人工智能·学习·职场发展
INS_KF2 小时前
【编程笔记】成员函数中两个 const 的区别(const Data &getData() const;)
前端·javascript·笔记
宿6743 小时前
vue3-async
前端·javascript·vue.js
excel4 小时前
Nuxt + Twin CSS 中宽度超过屏幕时底部出现空白的原因与解决方案
前端·javascript
一个游离的指针7 小时前
函数管道:消除深度嵌套调用
前端·javascript
柚yuzumi7 小时前
前端优化,从少触发一次开始:防抖与节流
前端·javascript
默_笙8 小时前
🚤 CSS 布局的"圈地运动":BFC 就是浏览器的独立领地
前端·javascript
用户938515635078 小时前
大模型记忆指南:从短时缓存到永久记忆,手把手带你吃透 LangChain Memory 管理
javascript·人工智能
xqchen8 小时前
Web Components 普及困境深度解析:技术标准与工程实践的落差
javascript
爱丶不疚8 小时前
写给前端工程师的现代 Python 工程化最佳实践:从 pnpm 到 uv,从 CommonJS 到 src-layout
javascript·python·typescript