如何用p5.js实现歌曲时间线可视化:按网格分段获取音频电平
解决方向与实现步骤
核心思路
你需要预分析整首音频的频谱数据,将音频时长按网格元素数量分割成对应分段,提前计算每个分段的bass/treble/mid电平(取平均或峰值),最后用这些预计算的数据绘制网格,而非实时读取播放中的电平。
具体步骤
1. 预加载并分析音频
首先要加载完整音频,然后逐段遍历音频的时间轴,用FFT分析每个时间段的能量值并存储:
- 用
loadSound加载音频文件,确保音频加载完成后再开始分析 - 计算每个分段的时长:
segmentDuration = 总音频时长 / 网格总元素数(比如10×10网格就是100段) - 遍历每个分段,跳到对应时间点,用FFT分析该段的频谱能量,将bass/treble/mid值存入数组
2. 存储分段数据
创建二维数组(对应网格的x/y坐标)来存储每个分段的电平数据,比如:
// 假设是10×10网格 let segmentData = Array(10).fill().map(() => Array(10).fill({bass:0, treble:0, mid:0}));
遍历过程中把每个分段计算出的电平值填充到这个数组里。
3. 修改绘制逻辑
把原来draw函数里实时获取FFT数据的部分,替换成读取预存的segmentData,直接用对应网格位置的数值来绘制图形。
代码示例(修改版)
let sound; let fft; let gridCols = 10; let gridRows = 10; let segmentData = []; let isAnalyzed = false; function preload() { sound = loadSound('your-song.mp3'); // 替换成你的音频路径 } function setup() { createCanvas(600, 600); fft = new p5.FFT(); sound.play(); sound.pause(); // 先暂停,完成预分析再播放(可选) // 开始预分析 analyzeAudioSegments(); } function analyzeAudioSegments() { const totalSegments = gridCols * gridRows; const totalDuration = sound.duration(); const segmentLength = totalDuration / totalSegments; // 初始化数据数组 segmentData = Array(gridRows).fill().map(() => Array(gridCols).fill({bass:0, treble:0, mid:0})); // 遍历每个分段 for (let i = 0; i < totalSegments; i++) { const time = i * segmentLength; sound.jump(time); // 跳到该分段的起始时间 fft.analyze(); // 触发FFT分析 // 获取该时间点的能量值(可以取该分段内多个点的平均值,这里简化为取起始点) const bass = fft.getEnergy("bass"); const treble = fft.getEnergy("treble"); const mid = fft.getEnergy("mid"); // 计算对应的网格坐标 const row = floor(i / gridCols); const col = i % gridCols; segmentData[row][col] = {bass, treble, mid}; } isAnalyzed = true; // 分析完成后可以选择播放音频 // sound.play(); } function draw() { background(0); if (!isAnalyzed) return; // 未分析完成时不绘制 const cellWidth = width / gridCols; const cellHeight = height / gridRows; for (let y = 0; y < gridRows; y++) { for (let x = 0; x < gridCols; x++) { const data = segmentData[y][x]; // 绘制BASS矩形 let bassAmp = map(data.bass, 0, 512, 0, cellWidth); noFill(); stroke(0, 255, 0); // 用绿色区分bass rect(x*cellWidth, y*cellHeight, bassAmp, bassAmp); // 绘制TREBLE矩形 let trebleAmp = map(data.treble, 0, 512, 0, cellWidth); stroke(255, 0, 0); // 红色区分treble rect(x*cellWidth, y*cellHeight, trebleAmp, trebleAmp); // 绘制MID矩形 let midAmp = map(data.mid, 0, 512, 0, cellWidth); stroke(0, 0, 255); // 蓝色区分mid rect(x*cellWidth, y*cellHeight, midAmp, midAmp); } } }
搜索关键词参考
- p5.js 音频预分析
- p5.js FFT 离线分析
- p5.js SoundFile 逐帧处理
- p5.js 音频分段频谱提取
内容的提问来源于stack exchange,提问作者Irrlicht
相关产品推荐
相关产品推荐

