音頻視覺化工具
這裡我們將深入了解一些 API 音頻屬性,並向您展示音頻視覺化工具建立的基礎知識。大多數視覺化工具都是基於相同的基本原則建立的,因此一點練習就能產生非常有趣的結果。
SignalRGB 音頻屬性
Section titled “SignalRGB 音頻屬性”SignalRGB 提供的音頻資料可以透過程式碼中的幾個屬性存取:
- engine.audio.level — 返回 -100 到 0 之間的數字,代表音軌的整體音量大小。0 很響,-100 很安靜。
- engine.audio.density — 返回 0 到 1 之間的數字,代表音調的粗糙度,測試音調返回 0,白噪音返回 1。
- engine.audio.freq — 返回包含 200 個元素的陣列,包含音軌的頻率資料。
每個屬性在正確使用之前都需要不同程度的正規化或調整,我稍後會詳細說明。
讓我們從基本的頻率動畫開始。
頻率代表我們聽到的聲音的音調,是音頻視覺化工具最重要的屬性。我們在這裡做的是每個影格擷取 200 個頻率波片段並將其轉換為視覺形式。基本過程是:
- 實例化一個陣列並用頻率資料填充它。
- 編輯此陣列以適應您的需求(篩選、映射、縮減等)。
- 撰寫一個「聲音條」類別來代表每個元素。
- 每個影格將資料連接到聲音條類別。
頻率的重要部分是我們必須對原始資料進行兩項調整。有時元素會以負值出現,這在視覺上很突兀。元素的高度在此範例中也會以不正確的方式出現。由於矩形「height」選項中的正值從形狀的原點向下繪製,我們需要翻轉這些值以類似於一般的視覺化工具。
範例——未處理的資料:

已處理的資料:

使用已處理資料的程式碼範例:
<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" 代表頻率資料中完整的 200 個元素 var frequency = new Int8Array(engine.audio.freq) // "reducedFreq" 將資料篩選為每四個結果以節省 CPU 負載 reducedFreq = frequency.filter((element, index) => { return index % 4 === 0; })
// 如果特效不存在,則建立它 if(effects.length < 1){ effects.push(new soundBars(20, 100)) }
// 背景顏色 DrawRect(0, 0, 320, 200, "black")
// 播放特效 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 // "height" 的資料處理。取每個元素的絕對值,然後翻轉為負 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>但是,上面的視覺化工具仍然存在一些問題。雖然通過耳機聽歌聲音很圓潤,但我們可以看到資料大量偏向某些聲音條,而其他聲音條通常完全不可見。從藝術角度來看,這並不理想,所以我們將「正規化」從 SignalRGB 接收的資料。正規化涉及在最大值和最小值之間均勻分配資料,以更好地說明它們相對彼此的值。在確定頻率陣列中的最大值和最小值之後,方程式整體相當簡單:(x - min) / (max - min),其中「x」代表當前元素。
正規化後的已處理資料:

接下來,我們將透過將聲音條排列成圓形來增加一些華麗效果:
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++){ // 儲存 canvas 的當前狀態 ctx.save() // 為您的條形添加圓形分量。此處「50」是圓的半徑 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 // 確定條形角度,使其垂直於圓 var rotate = Math.atan2(y - this.y, x - this.x) + Math.PI / 2; // 平移到圓心 ctx.translate(x, y) // 旋轉 canvas ctx.rotate(rotate) // 平移回來以繪製 ctx.translate(-x, -y) DrawRect(x, y, 5, height, "white") // 繪製後恢復 canvas 的舊狀態以防止正反饋迴圈 ctx.restore() } } }
密度使用起來很簡單。返回的值將是 0 到 1 之間的數字,代表音調的「純淨度」。數位音調將接近 0,模擬音調將接近 1。在此範例中,我將使用它來編輯聲音條的顏色,這將使您歌曲中不同的音調具有不同的顏色。
function soundBars (x, y){
this.x = x; this.y = y; this.rotate = 0;
this.draw = function(){ // 每個影格計算色相,結果將是 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 // 為了可見性略微編輯高度 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) // 每個影格插入已編輯的色相值 DrawRect(x, y, 5, height, `hsl(${hue}, 100%, 50%)`) this.rotate = 0; ctx.restore() } } }
此屬性只返回音軌的響度(以分貝為單位),技巧在於它產生 -100 到 0 之間的數字。-100 非常安靜,0 非常響。我們需要對這些資料進行一些編輯以按我想要的方式使用,我將在 update 函式中繪製此形狀。在這裡,音軌音量將編輯內部黑色圓圈的亮度。
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); } });
// 計算色相以匹配其餘視覺化工具 var hue = engine.audio.density * 360 // 音量乘以 10,然後加到 100。這是任意的,但此處某種組合的操作將給出您想要的結果 DrawCircle(160, 100, 50, `hsl(${hue}, 100%, ${100 + engine.audio.level * 10}%)`)
window.requestAnimationFrame(update); }