音频可视化

321 阅读1分钟

音频可视化

先看效果

音频上下文

AudioContext

AnalyserNode

// 创建音频上下文
const audCtx = new AudioContext()
// 创建音频源节点
const source = audCtx.createMediaElementSource(audioEl)
const analyser = audCtx.createAnalyser() // 创建分析器节点
analyser.fftSize = 512
// 创建数组,接受分析器数据
const dataArray = new Uint8Array(analyser.frequencyBinCount) //frequencyBinCount 的值固定为 AnalyserNode 接口中 fftSize 值的一半
source.connect(analyser) // 分析器连接音频源节点
analyser.connect(audCtx.destination) // 分析器连接输出设备

实践-音乐可视化

首先实现html页面

<canvas></canvas>
<audio src="./music.mp3" controls></audio>

初始化canvas

const cvs = document.querySelector('canvas')
const ctx = cvs.getContext('2d')
function initCvs() {
    cvs.width = window.innerWidth
    cvs.height = window.innerHeight / 2
}
initCvs()

初始化音视频上下文

let isInit = false
let dataArray, analyser
audioEl.onplay = function () {
    console.log(666);
    if (isInit) return
    // 初始化
    // 创建音频上下文
    const audCtx = new AudioContext()
    // 创建音频源节点
    const source = audCtx.createMediaElementSource(audioEl)
    analyser = audCtx.createAnalyser() // 创建分析器节点
    analyser.fftSize = 512
    // 创建数组,接受分析器数据
    dataArray = new Uint8Array(analyser.frequencyBinCount)
    source.connect(analyser) // 分析器连接音频源节点
    analyser.connect(audCtx.destination) // 分析器连接输出设备
    isInit = true
}

绘制

function draw() {
    requestAnimationFrame(draw)
    // 清空画布
    const { width, height } = cvs
    ctx.clearRect(0, 0, width, height)
    if (!isInit) return
    // 让分析器节点分析出数据到数组中
    analyser.getByteFrequencyData(dataArray)
    const len = dataArray.length
    const barWidth = width / len
    ctx.fillStyle = '#78c5f7'
    for (let i = 0; i < len; i++) {
        const data = dataArray[i]
        const barHeight = data / 255 * height
        const x = i * barWidth
        const y = height - barHeight
        ctx.fillRect(x, y, barWidth, barHeight)
    }
}
draw()

略微优化一下

function draw() {
    requestAnimationFrame(draw)
    // 清空画布
    const { width, height } = cvs
    ctx.clearRect(0, 0, width, height)
    if (!isInit) return
    // 让分析器节点分析出数据到数组中
    analyser.getByteFrequencyData(dataArray)
    const len = dataArray.length / 2.5
    const barWidth = width / len / 2
    ctx.fillStyle = '#78c5f7'
    for (let i = 0; i < len; i++) {
        const data = dataArray[i]
        const barHeight = data / 255 * height
        const x1 = i * barWidth + width / 2
        const x2 = width / 2 - i * barWidth
        const y = height - barHeight
        ctx.fillRect(x1, y, barWidth - 2, barHeight)
        ctx.fillRect(x2, y, barWidth - 2, barHeight)
    }
}

music.gif

扩展-麦克风输入的音频进行可视化

<button onclick="startRecording()">开始录音</button>
function startRecording() {
    navigator.mediaDevices.getUserMedia({
            audio: true
        })
        .then(stream => {
            const audCtx = new AudioContext()
            // 创建音频源节点
            const source = audCtx.createMediaStreamSource(stream)
            analyser = audCtx.createAnalyser() // 创建分析器节点
            analyser.fftSize = 512
            // 创建数组,接受分析器数据
            dataArray = new Uint8Array(analyser.frequencyBinCount)
            source.connect(analyser) // 分析器连接音频源节点
            isInit = true
            
        })
        .catch(err => {
            console.log('访问麦克风失败:' + err.message);
        });
}