iT邦幫忙

2023 iThome 鐵人賽

DAY 18
0
自我挑戰組

JavaScript 是什麼?可以吃嗎?系列 第 18

Day18 - 使用 Audio API 實現音頻視覺化

  • 分享至 

  • xImage
  •  

嗨~大家好我是Eric~ 音頻視覺化是一個很有趣的領域,不僅可以讓我們更直觀地感受音樂,還能提供一個極富吸引力的用戶體驗,在這篇文章中,我會展示如何使用 Web Audio API 和 Canvas API 來實現一個簡單但功能強大的音頻視覺化工具。

HTML 結構

首先,我們需要一個 HTML 結構來放置我們的 canvas 和 audio 元素

<canvas id="canvas"></canvas>
<audio id="audio" src="./video.mp3" controls></audio>

初始化 Canvas 和 Audio

我們先取得 audio 和 canvas 的 DOM

const audioEle = document.getElementById('audio');
const cvs = document.getElementById('canvas');
const ctx = cvs.getContext('2d');

接著定義一個函數 initCanvas 來初始化 canvas 的寬和高,在這裡特別考慮了裝置的像素比,以確保畫面在 Retina 顯示器上也能清晰呈現

const size = 800;
function initCanvas() {
    cvs.width = size * window.devicePixelRatio;
    cvs.height = size * window.devicePixelRatio;
    cvs.style.width = cvs.style.height = size + 'px';
}
initCanvas();

繪製視覺化圖形

接下來是定義核心的 draw 函數,它負責根據音頻數據來繪製視覺化圖形,在這裡我們嘗試畫一個地極圖

function draw(datas, maxValue) {
    ctx.clearRect(0, 0, cvs.width, cvs.height);
    const centerX = cvs.width / 2;
    const centerY = cvs.height / 2;
    const radianInterval = 2 * Math.PI / datas.length;

    ctx.beginPath();

    for (let i = 0; i < datas.length; i++) {
        const value = datas[i];
        const radian = i * radianInterval;
        const length = (value / maxValue) * (cvs.width / 4);
        const x = centerX + Math.cos(radian) * length;
        const y = centerY + Math.sin(radian) * length;

        if (i === 0) {
            ctx.moveTo(x, y);
        } else {
            const prevX = centerX + Math.cos(radian - radianInterval) * (datas[i-1] / maxValue) * (cvs.width / 4);
            const prevY = centerY + Math.sin(radian - radianInterval) * (datas[i-1] / maxValue) * (cvs.width / 4);
            const cp1x = (prevX + x) / 2;
            const cp1y = (prevY + y) / 2;
            ctx.quadraticCurveTo(cp1x, cp1y, x, y);
        }
    }

    ctx.strokeStyle = _getRandomColor();
    ctx.lineWidth = 2;
    ctx.stroke();
}

音頻播放和實時更新

最後將 audio 元素設置了一個 onplay 事件,當音頻開始播放時,會初始化 AudioContext 和 AnalyserNode,並實時更新 canvas
update 函數負責在每一個畫面更新時調用 draw 函數,並傳遞當前的音頻數據。

let isInit = false;
let analyser, buffer;

audioEle.onplay = function () {
    if (isInit) return;
    const audioCtx = new AudioContext();
    analyser = audioCtx.createAnalyser();
    analyser.fftSize = 512;
    buffer = new Uint8Array(analyser.frequencyBinCount);

    const source = audioCtx.createMediaElementSource(audioEle);
    source.connect(analyser);
    analyser.connect(audioCtx.destination);
    isInit = true;

    update();
}

// 更新函數
function update() {
    // 請求動畫幀
    requestAnimationFrame(update);

    // 檢查是否已初始化
    if (!isInit) {
        return
    }
    // 獲取頻率數據
    analyser.getByteFrequencyData(buffer);
    const offset = Math.floor(buffer.length * 2 / 3);
    const datas = new Array(offset * 2);
    for (let i = 0; i < offset; i++) {
        datas[i] = datas[datas.length - i - 1] = buffer[i];
    }
    // 調用繪圖函數
    draw(datas, 255);
}

總結

以上就是如何使用 Web Audio API 和 Canvas API 來實現音頻視覺化的全過程,這次的分享就到這邊,我們明天見 ~


上一篇
Day17 - 標準化你的函數參數 - Parameter Normalization
下一篇
Day19 - 一道解構的面試題
系列文
JavaScript 是什麼?可以吃嗎?20
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言