Audio visualizer
Ở đây chúng ta khám phá một số thuộc tính audio của API và trình bày những kiến thức cơ bản về tạo audio visualizer. Hầu hết các visualizer đều dựa trên các nguyên tắc cơ bản giống nhau, vì vậy một chút luyện tập có thể cho ra kết quả rất thú vị.
Thuộc tính audio SignalRGB
Phần tiêu đề “Thuộc tính audio SignalRGB”Dữ liệu audio do SignalRGB cung cấp có thể được truy xuất thông qua một số thuộc tính trong code của bạn:
- engine.audio.level - trả về số từ -100 đến 0 đại diện cho âm lượng tổng thể của bài nhạc. 0 là to, -100 là rất yên tĩnh.
- engine.audio.density - trả về số từ 0 đến 1 đại diện cho độ thô của âm thanh; âm thanh thử nghiệm trả về 0, nhiễu trắng trả về 1.
- engine.audio.freq - trả về mảng với 200 phần tử chứa dữ liệu tần số của bài nhạc.
Mỗi thuộc tính yêu cầu các bước chuẩn hóa hoặc điều chỉnh khác nhau trước khi có thể sử dụng đúng cách, tôi sẽ đề cập đến điều này sớm.
Hãy bắt đầu với animation tần số cơ bản.
Tần số
Phần tiêu đề “Tần số”Tần số đại diện cho cao độ của âm thanh nghe được và là thuộc tính quan trọng nhất cho audio visualizer. Ở đây chúng ta lấy 200 mẫu cắt ngang của sóng tần số mỗi khung và chuyển đổi chúng thành hình thức trực quan. Luồng cơ bản là:
- Khởi tạo một mảng và điền vào đó dữ liệu tần số.
- Xử lý mảng này khi cần (lọc, ánh xạ, rút gọn, v.v.).
- Viết lớp “soundbar” để đại diện cho mỗi phần tử.
- Kết nối dữ liệu với lớp soundbar mỗi khung.
Điều quan trọng về tần số là chúng ta phải thực hiện hai hiệu chỉnh với dữ liệu thô. Đôi khi một phần tử đến với giá trị âm, điều này gây rối về mặt trực quan. Chiều cao của các phần tử trong ví dụ này cũng đến không chính xác. Vì các giá trị dương trong tùy chọn “height” của hình chữ nhật được vẽ xuống dưới từ điểm gốc của hình, chúng ta muốn đảo ngược chúng để trông giống visualizer thông thường.
Ví dụ — dữ liệu chưa xử lý:

Dữ liệu đã xử lý:

Ví dụ code với dữ liệu đã xử lý:
<head> <title>Visualizer Tutorial</title> <meta description="Basic Effects" /> <meta publisher="SignalRgb" /> </head>
<body style="margin: 0; padding: 0; background: #000;"> <canvas id="exCanvas" width="320" height="200"></canvas> </body>
<script> canvas = document.getElementById('exCanvas'); ctx = canvas.getContext('2d'); var effects = []; var reducedFreq = [];
function update() {
// "frequency" represents the full 200 elements from our frequency data var frequency = new Int8Array(engine.audio.freq) // "reducedFreq" filters the data down to every fourth result to save some CPU load reducedFreq = frequency.filter((element, index) => { return index % 4 === 0; })
// Create the effect if it does not yet exist if(effects.length < 1){ effects.push(new soundBars(20, 100)) }
// Background color DrawRect(0, 0, 320, 200, "black")
// Play effect effects.forEach((ele, i) => { ele.draw(); if (ele.lifetime <= 0) { effects.splice(i, 1); } });
window.requestAnimationFrame(update); }
function soundBars (x, y){ this.x = x; this.y = y; this.draw = function(){ for(let i = 0; i < reducedFreq.length; i++){ var x = this.x + 5 * i var y = this.y // Data processing occurs for "height". Find the absolute value of each element, then flip negative var height = -Math.abs(reducedFreq[i]) DrawRect(x, y, 5, height, "white") } } }
function DrawRect(x, y, width, height, color) { ctx.beginPath(); ctx.fillStyle = color; ctx.fillRect(x, y, width, height); };
window.requestAnimationFrame(update); </script>Tuy nhiên, với visualizer trên vẫn còn một số vấn đề. Mặc dù bài hát nghe có vẻ cân bằng trong tai nghe, chúng ta thấy rằng dữ liệu ưu tiên một số soundbar một cách quá mức và để những cái khác thường xuyên vô hình. Từ góc độ nghệ thuật, điều đó không lý tưởng, vì vậy chúng ta sẽ “chuẩn hóa” dữ liệu nhận được từ SignalRGB. Chuẩn hóa phân bổ đều dữ liệu giữa các giá trị tối đa và tối thiểu để thể hiện tốt hơn giá trị của chúng so với nhau. Khi chúng ta xác định giá trị tối đa và tối thiểu trong mảng tần số, phương trình tổng thể khá đơn giản: (x - min) / (max - min), trong đó “x” đại diện cho phần tử hiện tại.
Dữ liệu đã chuẩn hóa, đã xử lý:

Sau đó chúng ta thêm một chút phong cách bằng cách sắp xếp các soundbar thành vòng tròn:
function soundBars (x, y){ this.x = x; this.y = y;
this.draw = function(){
var max = Math.max(...reducedFreq) var min = Math.min(...reducedFreq)
for(let i = 0; i < reducedFreq.length; i++){ //Save the current state of the canvas ctx.save() //Add the circular component to your bars. "50" here is the radius of the circle var x = this.x + Math.cos(i) * 50 var y = this.y + Math.sin(i) * 50 var height = ((Math.abs(reducedFreq[i])) - min) / (max - min) * -40 // Determine the angle of the bar, to make it perpindicular to the circle var rotate = Math.atan2(y - this.y, x - this.x) + Math.PI / 2; //Translate to circle center ctx.translate(x, y) //Rotate the canvas ctx.rotate(rotate) //Translate back for drawing ctx.translate(-x, -y) DrawRect(x, y, 5, height, "white") //Restore the old state of the canvas after drawing to prevent positive feedback loops ctx.restore() } } }
Mật độ
Phần tiêu đề “Mật độ”Mật độ rất đơn giản để sử dụng. Giá trị trả về là số từ 0 đến 1 đại diện cho “độ sạch” của âm thanh. Âm thanh kỹ thuật số gần với 0, âm thanh analog gần với 1. Trong ví dụ này tôi sử dụng nó để chỉnh sửa màu sắc của soundbar, điều này mang lại cho một số âm điệu nhất định trong bài hát các tông màu nhất định.
function soundBars (x, y){
this.x = x; this.y = y; this.rotate = 0;
this.draw = function(){ //Hue is calculated each frame, the resulting amount will be a proper hue option for hsl var hue = engine.audio.density * 360
var max = Math.max(...reducedFreq) var min = Math.min(...reducedFreq) for(let i = 0; i < reducedFreq.length; i++){ ctx.save() var x = this.x + Math.cos(i) * 50 var y = this.y + Math.sin(i) * 50 //Height slightly edited for visability var height = ((Math.abs(reducedFreq[i])) - min) / (max - min) * -50 - 5 this.rotate = Math.atan2(y - this.y, x - this.x) + Math.PI / 2; ctx.translate(x, y) ctx.rotate(this.rotate) ctx.translate(-x, -y) //Insert the edited hue value each frame DrawRect(x, y, 5, height, `hsl(${hue}, 100%, 50%)`) this.rotate = 0; ctx.restore() } } }
Mức âm lượng
Phần tiêu đề “Mức âm lượng”Thuộc tính này chỉ đơn giản trả về âm lượng của bài nhạc theo decibel, tạo ra các số từ -100 đến 0. -100 là rất yên tĩnh và 0 là rất to. Chúng ta cần xử lý dữ liệu này một chút để sử dụng theo cách mong muốn, và tôi vẽ form này trong hàm update. Ở đây mức âm lượng của bài nhạc chỉnh sửa độ sáng của vòng tròn đen bên trong.
function update() {
var frequency = new Int8Array(engine.audio.freq) reducedFreq = frequency.filter((element, index) => { return index % 4 === 0; })
if(effects.length < 1){ effects.push(new soundBars(160, 100)) }
DrawRect(0, 0, 320, 200, "black")
effects.forEach((ele, i) => { ele.draw(); if (ele.lifetime <= 0) { effects.splice(i, 1); } });
// Hue calcualtion to match the rest of the visualizer var hue = engine.audio.density * 360 //The level has been multiplied by 10, then added to 100. This is arbitrary, but some combination of actions here will give you the result you want DrawCircle(160, 100, 50, `hsl(${hue}, 100%, ${100 + engine.audio.level * 10}%)`)
window.requestAnimationFrame(update); }