首页 > 其他分享 >使用js写一个音乐音谱图

使用js写一个音乐音谱图

时间:2023-11-16 15:23:12浏览次数:30  
标签:频谱 canvas 音谱 音乐 ctx js var 绘制 音频

我们经常看到在听乐音的时候,会有音谱图随着音乐的节奏不断变化给人视觉上的享受,那么我们通过js来实现以下这个效果,下面是简单的效果图

 首先我们需要有一个绘制音频的函数

function draw() {
// 请求下一帧动画
animationId = requestAnimationFrame(draw);

// 获取音频频谱数据
analyser.getByteFrequencyData(dataArray);

// 清空画布
ctx.fillStyle = 'black';
ctx.fillRect(0, 0, canvas.width, canvas.height);

// 计算每个频谱条的宽度
var barWidth = (canvas.width / bufferLength) * 2.5;
var barHeight;
var x = 0;

// 遍历频谱数据数组,绘制频谱条
for (var i = 0; i < bufferLength; i++) {
// 计算频谱条的高度
barHeight = dataArray[i] / 255 * canvas.height;

// 根据频谱条的索引值计算颜色(彩虹色)
var hue = i / bufferLength * 360;
ctx.fillStyle = 'hsl(' + hue + ', 100%, 50%)';

// 绘制频谱条矩形
ctx.fillRect(x, canvas.height - barHeight, barWidth, barHeight);

// 更新下一个频谱条的起始位置
x += barWidth + 1;
}
}

这个函数是用于绘制频谱图的核心部分。它使用requestAnimationFrame()方法来请求下一帧动画,并将自身作为回调函数。这样可以不断更新频谱图。

在函数内部,analyser.getByteFrequencyData(dataArray)用于获取当前的音频频谱数据,将数据存储在dataArray数组中。

然后,画布被清空,使用黑色填充整个画布。

接下来,通过计算每个频谱条的宽度,以及根据频谱数据计算每个频谱条的高度,来确定频谱条的绘制参数。

然后,使用彩虹色调的渐变来设置频谱条的颜色,颜色的HSL值根据频谱条的索引值计算。

最后,在画布上绘制每个频谱条的矩形,每个矩形之间留有间距。

通过不断调用requestAnimationFrame()方法并在每一帧更新频谱图,可以实现连续的动画效果。

 

 

接下来我们需要分析一下音频
 document.getElementById('playButton').addEventListener('click', function() {
            if (!audioContext) {
                audioContext = new (window.AudioContext || window.webkitAudioContext)();
                analyser = audioContext.createAnalyser();
                analyser.fftSize = 2048;

                audioElement = document.createElement('audio');
                audioElement.src = '1.mp3';
                audioElement.controls = true;
                audioElement.style.display = 'none';

                document.body.appendChild(audioElement);

                var source = audioContext.createMediaElementSource(audioElement);
                source.connect(analyser);
                analyser.connect(audioContext.destination);

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

            audioElement.play();
            draw();
        });

        document.getElementById('pauseButton').addEventListener('click', function() {
            audioElement.pause();
            cancelAnimationFrame(animationId);
        });

当用户点击"播放"按钮时,创建一个新的AudioContext对象用于处理音频,创建一个AnalyserNode对象用于分析音频频谱。然后创建一个audio元素并将其设置为要播放的音频文件。将audio元素连接到AnalyserNode,将AnalyserNode连接到AudioContext的目标(通常是扬声器)。设置频率分析器的参数,包括FFT大小。

当用户点击"播放"按钮时,音频开始播放,并且在draw()函数中的requestAnimationFrame(draw)中调用的循环中,更新频谱数据并绘制频谱图。首先,使用analyser.getByteFrequencyData(dataArray)获取音频频谱数据。然后,通过遍历数据数组,计算每个频谱条的高度,并根据频谱条的位置在画布上绘制矩形。颜色根据频谱条的索引值计算,使得频谱图呈现彩虹色的效果。

当用户点击"暂停"按钮时,音频暂停播放,并调用cancelAnimationFrame(animationId)来停止绘制频谱图。

请确保将audioElement.src中的路径替换为你要播放的实际音频文件的路径。

 

当然修改draw函数可以得到其他的音频图,比如波形图

具体的draw代码如下

这个函数主要用于绘制音频的时域波形图。它也使用了requestAnimationFrame()方法来请求下一帧动画,并将自身作为回调函数。

在函数内部,analyser.getByteTimeDomainData(dataArray)用于获取当前的音频时域数据,将数据存储在dataArray数组中。

然后,画布被清空,并将背景颜色设置为lime。

接下来,设置线条的宽度和颜色。

然后,开始绘制路径。

通过计算每个数据片段的宽度,以及根据时域数据计算每个点的纵坐标,确定波形图的绘制参数。

然后,根据波形点的位置,使用moveTo()方法将绘制路径移动到第一个点的位置,并使用lineTo()方法连接到下一个点的位置。这样就形成了一条完整的波形路径。

在遍历完所有的数据点后,使用lineTo()方法将最后一个点连接到画布的右侧中点,以形成闭合路径。

最后,使用stroke()方法绘制路径。

通过不断调用requestAnimationFrame()方法并在每一帧更新波形图,可以实现连续的动画效果。

  function draw() { // 请求下一帧动画 animationId = requestAnimationFrame(draw);
// 获取音频时域数据 analyser.getByteTimeDomainData(dataArray);
// 清空画布并设置背景颜色为lime ctx.fillStyle = 'lime'; ctx.fillRect(0, 0, canvas.width, canvas.height);
// 设置线条宽度和颜色 ctx.lineWidth = 2; ctx.strokeStyle = 'black';
// 开始绘制路径 ctx.beginPath();
// 计算每个数据片段的宽度 var sliceWidth = canvas.width * 1.0 / bufferLength; var x = 0;
// 遍历时域数据数组,绘制波形 for (var i = 0; i < bufferLength; i++) { // 将数据归一化到范围[-1, 1] var v = dataArray[i] / 128.0; // 计算波形点的纵坐标 var y = v * canvas.height / 2;
if (i === 0) { // 移动到第一个点的位置 ctx.moveTo(x, y); } else { // 连接到下一个点的位置 ctx.lineTo(x, y); }
// 更新下一个点的横坐标 x += sliceWidth; }
// 连接最后一个点到画布右侧中点,形成闭合路径 ctx.lineTo(canvas.width, canvas.height / 2);
    // 绘制路径    ctx.stroke(); }

标签:频谱,canvas,音谱,音乐,ctx,js,var,绘制,音频
From: https://www.cnblogs.com/ximenchuifa/p/17836340.html

相关文章

  • Vue3实战 - 第一章 node.js/npm安装、配置
    一、node.js 安装(windows)1、下载并安装nodehttps://nodejs.org/en安装到 D:\Java\nodes 路径2、配置环境变量检查是否安装成功3、配置全局包存放目录和缓存目录npmconfigsetprefix"D:\nodejs\node_global"npmconfigsetcache"D:\nodejs\node_cache"4、安......
  • js获取抖音弹幕
    functiondom(){   //创建一个MutationObserver实例   letmutationObserver=newMutationObserver(function(mutationsList,observer){     for(varmutationofmutationsList){       console.log("用户:",mutation.target.las......
  • python3 json.dumps(OrderDict类型) 报错:TypeError: Object of type datetime is not
    chatgpt给出的解决方案,在json.dumps()函数调用中传入default参数来指定如何处理datetime对象importjsonfromdatetimeimportdatetimedefdatetime_handler(obj):ifisinstance(obj,datetime):returnobj.__str__()#另一种处理,转换为自定义格式化字符串......
  • 网页调试(css,html,js)获取资源链接等
    进入调试模式F12或者右键检查在元素栏点击相应的组件可以查看html源码,图片视频音频链接等,或者点击组件右键检查还可以查看组件css布局变化html元素源码可以看到class类名,触发事件以及style样式在css上的改变是实时的,刷新页面会失效html的改变需要双击,同样刷新页面......
  • js中判断obj中是否含有某个属性的方法
    一般而言,比较常用的有in,hasOwnProperty,比较undefined。首先我们先定义一个对象letobj={a:1,b:2,d:undefined}hasOwnProperty()表明它查看对象自身的属性console.log(obj.hasOwnProperty('a'))//......
  • nodejs "Client does not support authentication protocol requested by server; con
    登录mysql输入以下命令:--选择mysql数据库:usemysql--laremehpe是登录用户名ALTERUSER'laremehpe'@'localhost'IDENTIFIEDBY'password'PASSWORDEXPIRENEVER;--laremehpe->用户名,123456->密码ALTERUSER'laremehpe'@'localhos......
  • 【随手记】解析 JSON 中的 Null 值遇到的问题
    在Java中解析JSON字符串时,不同的库会对JSON中的null值有不同的处理方式。本文探讨阿里巴巴的JSONObject和net.sf.json.JSONObject在处理null值时的差异。阿里巴巴的JSONObject阿里巴巴的JSONObject.parseObject方法,在解析JSON字符串时,会将JSON中的null......
  • 一些Js常用小方法总结
    1.js找出字符串中,出现最多次数的字母<scripttype="text/javascript">functiontest(){varstr="adadfdfseffserfefsefseeffffftsdg";varmaxLength=0;//命名一个变量放置字母出现的最高次数并初始化为0varresult='';//命名一个变量放置结果输入while(str!=''){/......
  • Node JS 多进程
    在Node.js中,可以使用多个进程来处理文件,并发执行任务以提高性能和效率。多进程可以提高应用程序的性能和可靠性,但同时也会带来一些额外的开销和复杂性问题优点可以高效利用多核CPU,将许多并发请求分配到不同的进程中处理,来提高应用程序性能。提高可靠性,多进程互相隔离,当一个......
  • 在js中,定义了两个同名函数后, 后面的函数会覆盖前面定义的函数。
    运行以下程序<script>varm=1,j=k=0;functionadd(n){returnn=n+1;}y=add(m);functionadd(n){returnn=n+3;}z=add(m);</script>A2,4B4,4C2,2D报异常正确答案:Bjs里面没有函数重载的......