HTML 利用 Web Audio API 进行音频可视化

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接: https://blog.csdn.net/qq_20535249/article/details/100086704

利用Web Audio API 进行音乐可视化

1.什么是 Web Audio API:

官方:Web Audio API 提供了在Web上控制音频的一个非常有效通用的系统,允许开发者来自选音频源,对音频添加特效,使音频可视化,添加空间效果 (如平移),等等。
使用这个API,时间可以被非常精确地控制,几乎没有延迟,这样开发人员可以准确地响应事件,并且可以针对采样数据进行编程,甚至是较高的采样率。这样,鼓点和节拍是准确无误的。

简洁来说就是使用这个API我们可以对音频进行各种操作,比如控制音量(简单的audio标签就可以进行控制音量,使用WebAudioAPI是对这个API的大材小用)、控制左右声道的播放,对音效进行优化,音频可视化(最常见的使用)。
这是我自己制作的一个小的demo效果如下:白色的条纹就是音乐可视化的部分,代表了当前播放的音乐的频谱
在这里插入图片描述

以下是官方的概念及使用方法地址:https://developer.mozilla.org/zh-CN/docs/Web/API/Web_Audio_API


2.重要概念

1.音频上下文 AudioContext :所有对音频的操作都是在这个对象中,创建一个音频上下文也是所有对音频进行操作的第一步。

2.音频源 :即你要处理的音频对象,就是你要处理的音频来自于哪里,比如一个或标签(MediaElementAudioSourceNode)、麦克风或者摄像头(MediaStreamAudioSourceNode)、内存中的一段音频数据(AudioBuffer)、一段随时间变化的波形(OscillatorNode)等。

3.音频目的地:AudioDestinationNode 定义了最后音频要输出到哪里,通常是输出到你的扬声器。

4.(可选)其他 Effect音效 节点:这些节点如 音效节点、数据分析和可视化节点、分离和合并声道节点等等,具体参照官方文档。这些节点用来改变或者分析我们的音频源,对音频进行相关的操作。


3.处理流程

Web音频的简单典型工作流程如下所示:

1.创建音频上下文。
2.在上下文中,创建源 - 例如,振荡器或流。
3.创建效果节点,例如混响,双二阶滤波器,平移器或压缩器。(可选)
4.选择音频的最终目的地,例如用户的计算机扬声器。(可选,因为在默认情况下只是简单的代表使用者系统的输出 (例如他们的扬声器))
5.将以上四个步骤连接起来,也就是进行 connect操作 ,通过零个或多个效果建立来自音频源的连接,最终在所选目的地结束。


4.具体代码解析

第一步:创建一个容器(对象:音频上下文 AudioContext),在此处命名为audioCtx

var audioCtx = new (window.AudioContext || window.webkitAudioContext)();

兼容性处理: webkit / blink 浏览器需要前缀,Safari 浏览器需要在有窗口的情况下工作。

第二步定义音频上下文的音频源节点

source = audioCtx.createMediaElementSource(playingAudioTag);

在此处使用一个标签作为音频源,playingAudioTag为网页中的一个标签,可以使用document.getElementById等来获得

第三步:定义一个音频上下文的 数据分析与可视化节点

    var analyser = audioCtx.createAnalyser();
        analyser.minDecibels = -90;
        analyser.maxDecibels = -10;
        analyser.smoothingTimeConstant = 0.85;

Analyser节点表示一个可以提供实时频率分析与时域分析的切点,这些分析数据可以用做数据分析和可视化。
Analyser节点 赋予了节点可以提供实时频率及时间域分析的信息。它使一个 AudioNode 通过音频流不做修改的从输入到输出, 但允许你获取生成的数据, 处理它并创建音频可视化.

AnalyserNode.minDecibels(分析结果的最小阈值)
是一个双精度值,表示FFT分析频域数据并转换为无符号字节值时,对输入的功率数据的最小阈值 - 基本上,它限定了调用getByteFrequencyData()时结果范围的最小值

AnalyserNode.maxDecibels(分析结果的最大阈值)
是一个双精度值,表示FFT分析频域数据并转换为无符号字节值时,对输入的功率数据的最大阈值 - 基本上,它限定了调用getByteFrequencyData()时结果范围的最大值

AnalyserNode.smoothingTimeConstant
是一个双精度浮点型(double)的值,表示最后一个分析帧的平均常数 — 基本上,它随时间使值之间的过渡更平滑。

第四步:将各节点连接

使用connect进行连接

    source.connect(analyser);
    analyser.connect(audioCtx.destination);

在这里插入图片描述

第五步:进行可视化绘图

        var canvas = document.querySelector('canvas');
        var canvasCtx = canvas.getContext("2d");
        var intendedWidth = document.querySelector('canvas').clientWidth;
        canvas.setAttribute('width',intendedWidth);
        visualize();

    function visualize() {

        var WIDTH = canvas.width;
        var HEIGHT = canvas.height;

        analyser.fftSize = 256;
        <--注释:AnalyserNode.fftSize一个无符号长整形(unsigned long)的值, 
        用于确定频域的 FFT (快速傅里叶变换) 的大小。-->

        var bufferLengthAlt = analyser.frequencyBinCount;
        console.log(bufferLengthAlt);
        
        <--注释:AnalyserNode.frequencyBinCount 只读一个无符号长整形(unsigned long)的值, 
        值为fftSize的一半。这通常等于将要用于可视化的数据值的数量。-->

        var dataArrayAlt = new Uint8Array(bufferLengthAlt);
        // console.log(dataArrayAlt);

        canvasCtx.clearRect(0, 0, WIDTH, HEIGHT);

        // var s = 0;
        var drawAlt = function() {
        <--注释:回调函数,每绘制完一次执行一次-->

            // setTimeout(drawAlt,1000);
            // console.log("进入drawAlt");
            drawVisual = requestAnimationFrame(drawAlt);

            analyser.getByteFrequencyData(dataArrayAlt);
            <--注释:AnalyserNode.getFloatFrequencyData()将当前频域数据拷贝进Float32Array数组。
            获取频域数据,绘制完成一次后进行一次获取,并将获取到的数据绘制在canvas中
            数组dataArrayAlt中的数据将来用做频谱图中长方形的高  -->

            canvasCtx.clearRect(0, 0, WIDTH, HEIGHT);
            canvasCtx.fillStyle = 'rgba(250, 250, 0, 0)';
            canvasCtx.fillRect(0, 0, WIDTH, HEIGHT);

            var barWidth = (WIDTH / bufferLengthAlt) ;
            var barHeight;
            var x = 0;

            for(var i = 0; i < bufferLengthAlt; i++) {

                barHeight = dataArrayAlt[i] * 0.7;

                // console.log(barHeight);

                canvasCtx.fillStyle = 'rgb(' + (barHeight + 170) + ','+ (barHeight + 170) +',' + (barHeight+ 170) + ')';
                canvasCtx.fillRect(i*(barWidth+1),HEIGHT-barHeight/2,barWidth,barHeight/2);
                x += barWidth + 1;
            }
        };

        drawAlt();

    }

5.一个自己写的demo中的音频处理模块具体代码

function spectrogram(playingAudioTag) {

        console.log("已经进入频谱显示模块");
        console.log(playingAudioTag);

        var source;
        // var audioCtx = null;
        var audioCtx = new (window.AudioContext || window.webkitAudioContext)();

        var analyser = audioCtx.createAnalyser();
        analyser.minDecibels = -90;
        analyser.maxDecibels = -10;
        analyser.smoothingTimeConstant = 0.85;

        //canvas绘图区域设置
        var canvas = document.querySelector('canvas');
        var canvasCtx = canvas.getContext("2d");
        var intendedWidth = document.querySelector('canvas').clientWidth;
        canvas.setAttribute('width',intendedWidth);

        var drawVisual;

        source = audioCtx.createMediaElementSource(playingAudioTag);
        // console.log("source输出");
        // console.log(source);
        source.connect(analyser);
        analyser.connect(audioCtx.destination);


        console.log(audioCtx);

        visualize();

        function visualize() {

            var WIDTH = canvas.width;
            var HEIGHT = canvas.height;

            analyser.fftSize = 256;
            var bufferLengthAlt = analyser.frequencyBinCount;
            console.log(bufferLengthAlt);
            var dataArrayAlt = new Uint8Array(bufferLengthAlt);
            // console.log(dataArrayAlt);

            canvasCtx.clearRect(0, 0, WIDTH, HEIGHT);

            // var s = 0;
            var drawAlt = function() {

                // setTimeout(drawAlt,1000);
                // console.log("进入drawAlt");
                drawVisual = requestAnimationFrame(drawAlt);

                analyser.getByteFrequencyData(dataArrayAlt);

                canvasCtx.clearRect(0, 0, WIDTH, HEIGHT);
                canvasCtx.fillStyle = 'rgba(250, 250, 0, 0)';
                canvasCtx.fillRect(0, 0, WIDTH, HEIGHT);

                var barWidth = (WIDTH / bufferLengthAlt) ;
                var barHeight;
                var x = 0;

                for(var i = 0; i < bufferLengthAlt; i++) {

                    barHeight = dataArrayAlt[i] * 0.7;

                    // console.log(barHeight);

                    canvasCtx.fillStyle = 'rgb(' + (barHeight + 170) + ','+ (barHeight + 170) +',' + (barHeight+ 170) + ')';
                    canvasCtx.fillRect(i*(barWidth+1),HEIGHT-barHeight/2,barWidth,barHeight/2);
                    x += barWidth + 1;
                }
            };

            drawAlt();

        }

    }

6.效果图

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/qq_20535249/article/details/100086704