1

看到跳动的音符你难道不想知道它是如何实现的么?开撸!
问:实现这个有什么用呢?
答:装杯用!😥其实这个操作能实现音频效果器。就是主播开麦后大妈声音变成少女,男人声音可以变女声!😱因为音频分析器可以拿到音频的频谱,既可以改变音调啊音色啊什么的就可以实现音频效果器!🐷
不想看我啰嗦的直接看最后,有整体html+js代码,粘贴到你的html页面内,换以下音频资源链接就能看到效果了!
效果图:
频谱图

技术分析

  1. 音频加载
    html内增加audio标签。将音乐加载进来。给audio标签增加controls属性用来控制音乐播放。如下:

    <html>
    <body>
     <audio src="/source/YOU.m4a" controls></audio>
    </body>
    </html>
  2. 频谱获取
    要获取音频的频谱需要使用javascriptAudioContext对象。

    var audio = document.querySelector("audio");
    let dataArray = new Uint8Array(512);
    let analyser = null
     audio.onplay = function() {
         let audctx = new AudioContext() // 创建音频上下文
            let source = audctx.createMediaElementSource(audio) // 创建音频源
            analyser = audctx.createAnalyser() // 创建音频分析器
            analyser.fftSize = 512 // 设置分析器大小, 必须为2^n次方
            // 创建一个数组,用于存放分析器节点数据
            // analyser.frequencyBinCount可以拿到傅里叶变换的值,因为傅里叶变换是对称的因此只要一半即可(傅里叶变换请自行搜索)
            dataArray = new Uint8Array(analyser.frequencyBinCount)
            source.connect(analyser) // 音频源连接到音频分析器
            analyser.connect(audctx.destination) // 音频分析器连接到音频输出
    }

    至此我们已经获取到了音频的频谱数据。

  3. 频谱可视化
    对于高效绘制需要用到画布,既canvas标签,在body内增加canvas标签,并起idcvs

    <body>
        <canvas  id="cvs"></canvas>
        <audio src="/source/YOU.m4a" controls></audio>
    </body>
    var cvs = document.getElementById("cvs");
    var ctx = cvs.getContext("2d");
    function initCanvas() {
        // 设置canvas宽度为浏览器可视区域
        cvs.width = window.innerWidth * devicePixelRatio;
        // 设置canvas高度为浏览器可视区域的一半
        cvs.height = window.innerHeight / 2 * devicePixelRatio;
    }
    // 窗口变化时候重新设置canvas宽高
    window.onresize = function() {
        initCanvas();
    }
    // 定义draw方法用于绘制频谱
    function draw() {
        // 使用浏览器自带帧动画函数实现每一帧都绘制。
        requestAnimationFrame(draw);
        // 清空画布
        ctx.clearRect(0, 0, cvs.width, cvs.height);
        const len = dataArray.length / 1.2; // 截取频谱绘制长度
        const barWidth = cvs.width / len;  // 每一根柱子的宽度
        ctx.fillStyle = '#c875fc'; // 给柱子填充颜色
        // 遍历每一根柱子,设置其位置以及边框。
        for (let i = 0; i < len; i++) {
            const data = dataArray[i]
            const barHeight = data / 255 * cvs.height
            const x = i * barWidth
            const y = cvs.height - barHeight
            
            drawBorder(x,y,barWidth, barHeight)
            ctx.fillStyle = '#8ca86d'
            ctx.fillRect(x, y, barWidth, barHeight)
        }
    }

    至此基本完成了绘制方面的任务。

    全部合并后就是以下代码,将以下代码在你本机创建一个html复制进去就可以看到效果(注意把音频资源换成自己的):

    <!DOCTYPE html>
    <html lang="en">
    <head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>声音波浪</title>
    </head>
    <style>
    #cvs {
        border: 1px solid #000;
    }
    audio {
        display: block;
    }
    </style>
    <body>
    <canvas  id="cvs"></canvas>
    <audio src="/source/YOU.m4a" controls></audio>
    </body>
    <script>
    var cvs = document.getElementById("cvs");
    var ctx = cvs.getContext("2d");
    var audio = document.querySelector("audio");
    let isInitAudio = false;
    let analyser = null
    let dataArray = new Uint8Array(512);
    /* 初始化canvas */
    function initCanvas() {
        cvs.width = window.innerWidth * devicePixelRatio;
        cvs.height = window.innerHeight / 2 * devicePixelRatio;
    }
    
    window.onresize = function() {
        initCanvas();
    }
    
    /* 初始化音频 */
    function initAudio() {
        audio.onplay = function() {
            let audctx = new AudioContext() // 创建音频上下文
            let source = audctx.createMediaElementSource(audio) // 创建音频源
            analyser = audctx.createAnalyser() // 创建音频分析器
            analyser.fftSize = 512 // 设置分析器大小, 必须为2^n次方
            // 创建一个数组,用于存放分析器节点数据
            // analyser.frequencyBinCount可以拿到傅里叶变换的值,因为傅里叶变换是对称的因此只要一半即可
            dataArray = new Uint8Array(analyser.frequencyBinCount)
            draw()
            
            source.connect(analyser) // 音频源连接到音频分析器
            analyser.connect(audctx.destination) // 音频分析器连接到音频输出
            
            let bufferLength = analyser.frequencyBinCount
        }
    }
    
    function draw() {
        requestAnimationFrame(draw);
        // 清空画布
        ctx.clearRect(0, 0, cvs.width, cvs.height);
        analyser.getByteFrequencyData(dataArray)
        const len = dataArray.length / 1.2
        const barWidth = cvs.width / len
        ctx.fillStyle = '#c875fc'
        
        for (let i = 0; i < len; i++) {
            const data = dataArray[i]
            const barHeight = data / 255 * cvs.height
            const x = i * barWidth
            const y = cvs.height - barHeight
            
            drawBorder(x,y,barWidth, barHeight)
            ctx.fillStyle = '#8ca86d'
            ctx.fillRect(x, y, barWidth, barHeight)
        }
    }
    function drawBorder(xPos, yPos, width, height, thickness = 1) {
        ctx.fillStyle='#000';
        ctx.fillRect(xPos - (thickness), yPos - (thickness), width + (thickness * 2), height + (thickness * 2));
    }
    
    function initAll() {
        initCanvas();
        initAudio()
    }
    initAll()
    </script>
    </html>

smallStone
444 声望71 粉丝

前端一枚^_-