使用js寫一個音樂音譜圖

2023-11-16 18:01:35

我們經常看到在聽樂音的時候,會有音譜圖隨著音樂的節奏不斷變化給人視覺上的享受,那麼我們通過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();
}