WebCRT Audio(AudioContext) - 绘制音波图,超级好玩!

背景

最近正在做一个开源的项目–pear-rec,pear-rec 是一个跨平台的截图、录屏、录音、录像软件。截图功能上篇文章已经讲过了,现在正在做录音功能。其实录音功能不难,但是我想做的美观,所以我想实现一个波形图。来展示正在录音。
在这里插入图片描述

工具

实现

原理逻辑

页面加载一段音频(audio),然后通过AudioContext对象解析音频的数据,然后转换成Uint8Array的数组,最后通过canvas实时画出数组的数据。就可以完成了!其实并不难,接下来我们就来实现吧!

在这里插入图片描述

准备工作

因为我们是简单实现一个demo,我选择了jqueryecharts来辅助我来开发,其实也可以不用。

<script src="https://fastly.jsdelivr.net/npm/jquery@3.7.0/dist/jquery.min.js"></script>
<script src="https://fastly.jsdelivr.net/npm/echarts@5/dist/echarts.min.js"></script>

静态页面

css 代码
             html,
                body {
                    margin: 0;
                    overflow: hidden;
                    font-family: sans-serif;
                    background: #13091B;
                    height: 100%;
                }

                body {
                    background: url(https://p1.music.126.net/gAmIGjlWnYXE_0O8LFp5-w==/109951164382001054.jpg) no-repeat;
                    background-size: cover;
                }

                #canvas {
                    position: fixed;
                    left: 0;
                    top: 0;
                    width: 100%;
                    height: 100%;
                }

                audio {
                    visibility: hidden;
                }

                #pause-btn,
                #play-btn {
                    position: absolute;
                    top: 50%;
                    left: 50%;
                    transform: translate(-50%, -50%);
                    color: #13091B;
                    background: #007A99;
                    display: block;
                    width: 150px;
                    height: 45px;
                    line-height: 45px;
                    font-size: 18px;
                    cursor: pointer;
                    border-radius: 4px;
                    letter-spacing: 0.1em;
                    z-index: 1;
                    text-align: center;
                    text-decoration: none;
                }

                #play-btn {
                    transform: translate(-105%, -50%);
                }

                #pause-btn {
                    transform: translate(5%, -50%);
                }
html代码
        <div id="content">
                <div id="main" style="width: 100%;height:100vh;"></div>
                <canvas id="canvas"></canvas>
                <audio id="audio" controls
                    src="//m8.music.126.net/21180815163607/04976f67866d4b4d11575ab418904467/ymusic/515a/5508/520b/f0cf47930abbbb0562c9ea61707c4c0b.mp3?infoId=92001"
                    crossOrigin="anonymous"></audio>
                <a href="javascript:;" id="play-btn">PLAY</a>
                <a href="javascript:;" id="pause-btn">pause </a>
            </div>

实现效果

在这里插入图片描述

音频解析

首先我们页面上有两个按钮,一个是播放,另一个是暂停,点击播放时候,用AudioContext解析当时的音频数据,并转换成Uint8Array数组。

1.获取dom
var btn = document.getElementById("play-btn");
    var pause = document.getElementById("pause-btn");
    var audio = document.getElementById("audio");
2. 播放按钮绑定点击事件
btn.onclick = function () {
    btn.style.display = "none";
    btn.style.display = "block";
    audio.play();
    onLoadAudio();
};
3. 暂停按钮绑定点击事件
pause.onclick = function () {
    pause.style.display = "none";
    btn.style.display = "block";
    audio.pause();
};
4. AudioContext解析音频

AudioContext接口表示由音频模块连接而成的音频处理图,每个模块对应一个AudioNodeAudioContext可以控制它所包含的节点的创建,以及音频处理、解码操作的执行。做任何事情之前都要先创建AudioContext对象,因为一切都发生在这个环境之中。

function onLoadAudio() {
        var context = new (window.AudioContext || window.webkitAudioContext)();
        var analyser = context.createAnalyser();
        analyser.fftSize = 512;
        var source = context.createMediaElementSource(audio);

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

        var bufferLength = analyser.frequencyBinCount;
        var dataArray = new Uint8Array(bufferLength);

        var canvas = document.getElementById("canvas");
        canvas.width = window.innerWidth;
        canvas.height = window.innerHeight;

        var ctx = canvas.getContext("2d");
        var WIDTH = canvas.width;
        var HEIGHT = canvas.height;

        var barWidth = WIDTH / bufferLength * 1;
        var barHeight;
    }

方法一(echarts)

在这里插入图片描述

function initChart(bufferLength, barHeightList) {
        var chartDom = document.getElementById('main');
        var myChart = echarts.init(chartDom);
        var option;
        var xAxisData = [];

        option = {
            graphic: {
                elements: [
                    {
                        type: 'group',
                        bottom: 0,
                        children: new Array(bufferLength).fill(0).map((val, i) =>
                        ({
                            type: 'rect',
                            x: i * 15,
                            shape: {
                                width: 10,
                                height: -barHeightList[i]
                            },
                            style: {
                                fill: `rgb(${barHeightList[i] + 25 * (i / bufferLength)}, ${250 * (i / bufferLength)}, ${50})`
                            },
                        })
                        )
                    }
                ]
            }
        };

        option && myChart.setOption(option);
    }

方法二(canvas)推荐

在这里插入图片描述

有的同学会说我也可以不用echarts直接用canvas来实现,其实也不难就是用fillRect方法来画柱状图。
如果有不太懂的同学可以去查看文档

语法:

context.fillRect(x,y,width,height);
参数描述
x矩形左上角的 x 坐标。
y矩形左上角的 y 坐标。
width矩形的宽度,以像素计。
height矩形的高度,以像素计。

具体代码:

function renderFrame() {
            requestAnimationFrame(renderFrame);

            analyser.getByteFrequencyData(dataArray);

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

            var barHeightList = [];
            var xAxisList = [];
            for (var i = 0, x = 0; i < bufferLength; i++) {
                barHeight = dataArray[i];

                barHeightList.push(barHeight);
                var r = barHeight + 25 * (i / bufferLength);
                var g = 250 * (i / bufferLength);
                var b = 50;

                ctx.fillStyle = "rgb(" + r + "," + g + "," + b + ")";
                ctx.fillRect(x, HEIGHT - barHeight, barWidth, barHeight);

                x += barWidth + 2;
            }
        }

总结

  • 使用 AudioContext 创建 analyser,将音频流 stream 连接到 analyser
  • 设置 analyserfft 参数,以此获取音频数据
  • 通过递归调用 requestAnimationFrame 来实现动画效果
  • 使用 Canvasecharts来绘制条形图以及小浮块,将这绘制操作放在 requestAnimationFrame 的回调中,从而展示动态的频谱图。

如果你看完还是做不出来,可以在 pear-rec 里直接看源码实现。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值