使用js写一个音乐音谱图

我们经常看到在听乐音的时候,会有音谱图随着音乐的节奏不断变化给人视觉上的享受,那么我们通过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();
}
posted @ 2023-11-16 15:11  小不点灬  阅读(677)  评论(0编辑  收藏  举报