音频可视化小工具

embedded/2025/1/19 10:46:46/

音频可视化小工具

    • 文章说明
    • 功能特点
    • 使用说明
    • 技术细节
    • 核心代码
    • 效果展示
    • 源码下载

文章说明

这是一个基于 JavaScriptWeb Audio API 实现的音频可视化小工具。通过上传音频文件,工具可以实时展示音频的可视化效果,包括条形频谱、圆形频谱等多种模式。代码由 GPT 生成,适合用于学习、演示或嵌入到网页中。


功能特点

  1. 音频可视化

    • 支持条形频谱(Bar Mode)、圆形频谱(Circle Mode)和扇形频谱(Arc Mode)三种可视化模式。
    • 实时动态展示音频的频率数据。
  2. 音频播放控制

    • 支持播放、暂停功能。
    • 上传音频文件后自动播放。
  3. 元数据展示

    • 自动提取音频文件的元数据(如歌曲名称、艺术家、封面图片)。
    • 如果元数据不存在,则显示文件名作为歌曲名称。
  4. 交互设计

    • 提供上传按钮、模式切换按钮和暂停按钮,操作简单直观。
    • 界面美观,支持浅蓝色主题和毛玻璃效果。

使用说明

  1. 上传音频文件

    • 点击 Upload Audio File 按钮,选择本地音频文件(支持 MP3、WAV 等格式)。
    • 上传后,音频会自动播放,并显示可视化效果。
  2. 切换可视化模式

    • 点击 Bar ModeCircle ModeArc Mode 按钮,切换不同的可视化效果。
  3. 播放/暂停音频

    • 点击 Pause 按钮可以暂停音频,再次点击可以继续播放。
  4. 查看元数据

    • 上传音频后,工具会自动显示歌曲名称、艺术家和封面图片(如果音频文件包含元数据)。

技术细节

  1. 核心技术

    • 使用 Web Audio API 分析音频数据。
    • 通过 Canvas 绘制实时可视化效果。
    • 使用 jsmediatags 库提取音频文件的元数据(如 ID3 标签)。
  2. 可视化模式

    • 条形频谱:将音频频率数据绘制为条形图。
    • 圆形频谱:将音频频率数据围绕中心点绘制为圆形。
    • 扇形频谱:将音频频率数据围绕中心点绘制为扇形。
  3. 界面设计

    • 使用 CSS 实现毛玻璃效果和浅蓝色主题。
    • 动态切换按钮状态,提升用户体验。

核心代码

一个HTML页面

html"><!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Audio Visualization</title><style>body {display: flex;justify-content: center;align-items: center;height: 100vh;margin: 0;background: linear-gradient(135deg, #1e3c72, #2a5298);color: #fff;font-family: 'Arial', sans-serif;overflow: hidden;}.container {display: flex;flex-direction: column;align-items: center;background: rgba(255, 255, 255, 0.1);border-radius: 20px;padding: 20px;box-shadow: 0 8px 32px rgba(0, 0, 0, 0.2);backdrop-filter: blur(10px);width: 90%;max-width: 800px;}canvas {background: rgba(255, 255, 255, 0.1);border-radius: 15px;margin-top: 20px;width: 100%;height: 300px;}#upload-label {background: rgba(255, 255, 255, 0.2);padding: 10px 20px;border-radius: 25px;cursor: pointer;font-size: 16px;color: #fff;transition: background 0.3s ease;margin-bottom: 20px;}#upload-label:hover {background: rgba(255, 255, 255, 0.3);}#audioFile {display: none;}.music-info {display: flex;align-items: center;width: 100%;margin-bottom: 20px;}.music-cover {width: 100px;height: 100px;border-radius: 10px;margin-right: 20px;background: rgba(255, 255, 255, 0.1);box-shadow: 0 4px 12px rgba(0, 0, 0, 0.2);object-fit: cover;opacity: 0;/* Initially hidden */transition: opacity 0.5s ease;}.music-cover.visible {opacity: 1;/* Show when cover is loaded */}.music-details {display: flex;flex-direction: column;justify-content: center;}.music-title {font-size: 20px;font-weight: bold;margin: 0;}.music-artist {font-size: 16px;color: rgba(255, 255, 255, 0.8);margin: 5px 0 0 0;}.mode-switcher {display: flex;gap: 10px;margin-top: 20px;}.mode-switcher button {background: rgba(255, 255, 255, 0.2);border: none;padding: 10px 20px;border-radius: 25px;color: #fff;font-size: 14px;cursor: pointer;transition: background 0.3s ease;}.mode-switcher button:hover {background: rgba(255, 255, 255, 0.3);}.mode-switcher button.active {background: #00b4db;}#pause-button {background: rgba(255, 255, 255, 0.2);border: none;padding: 10px 20px;border-radius: 25px;color: #fff;font-size: 14px;cursor: pointer;transition: background 0.3s ease;margin-top: 20px;}#pause-button:hover {background: rgba(255, 255, 255, 0.3);}</style></head><body><div class="container"><label for="audioFile" id="upload-label">Upload Audio File</label><input type="file" id="audioFile" accept="audio/*"/><div class="music-info"><div class="music-cover" id="music-cover"></div><div class="music-details"><p class="music-title" id="music-title">Song Title</p><p class="music-artist" id="music-artist">Artist</p></div></div><canvas id="visualizer" width="800" height="300"></canvas><div class="mode-switcher"><button id="bar-mode" class="active">Bar Mode</button><button id="circle-mode">Circle Mode</button><button id="arc-mode">Arc Mode</button></div><button id="pause-button">Pause</button></div><!-- Include jsmediatags library --><script src="https://unpkg.com/jsmediatags@3.9.7/dist/jsmediatags.min.js"></script><script>html" title=javascript>javascript">const canvas = document.getElementById('visualizer');const ctx = canvas.getContext('2d');const audioFileInput = document.getElementById('audioFile');const musicCover = document.getElementById('music-cover');const musicTitle = document.getElementById('music-title');const musicArtist = document.getElementById('music-artist');const barModeButton = document.getElementById('bar-mode');const circleModeButton = document.getElementById('circle-mode');const arcModeButton = document.getElementById('arc-mode');const pauseButton = document.getElementById('pause-button');let audioContext;let analyser;let source;let dataArray;let bufferLength;let currentMode = 'bar'; // Default modelet audio; // Audio elementlet isPlaying = false; // Track playback state// Event listeners for mode switchingbarModeButton.addEventListener('click', () => switchMode('bar'));circleModeButton.addEventListener('click', () => switchMode('circle'));arcModeButton.addEventListener('click', () => switchMode('arc'));// Event listener for pause buttonpauseButton.addEventListener('click', togglePlayback);function switchMode(mode) {currentMode = mode;barModeButton.classList.remove('active');circleModeButton.classList.remove('active');arcModeButton.classList.remove('active');if (mode === 'bar') barModeButton.classList.add('active');if (mode === 'circle') circleModeButton.classList.add('active');if (mode === 'arc') arcModeButton.classList.add('active');}function togglePlayback() {if (isPlaying) {audio.pause();pauseButton.textContent = 'Play';} else {audio.play();pauseButton.textContent = 'Pause';}isPlaying = !isPlaying;}audioFileInput.addEventListener('change', function (event) {const file = event.target.files[0];if (file) {const fileURL = URL.createObjectURL(file);audio = new Audio(fileURL);setupAudioContext(audio);audio.play();isPlaying = true;pauseButton.textContent = 'Pause';// Extract metadata using jsmediatagsjsmediatags.read(file, {onSuccess: function (tag) {const tags = tag.tags;// Update song titleif (tags.title) {musicTitle.textContent = tags.title;} else {musicTitle.textContent = file.name.replace(/\.[^/.]+$/, ""); // Fallback to file name}// Update artistif (tags.artist) {musicArtist.textContent = tags.artist;} else {musicArtist.textContent = "Unknown Artist";}// Update cover imageif (tags.picture) {const picture = tags.picture;const base64String = Array.from(picture.data).map(byte => String.fromCharCode(byte)).join('');const base64 = `data:${picture.format};base64,${window.btoa(base64String)}`;musicCover.style.backgroundImage = `url(${base64})`;musicCover.classList.add('visible'); // Show cover} else {musicCover.style.backgroundImage = ''; // No covermusicCover.classList.remove('visible'); // Hide cover}},onError: function (error) {console.error("Error reading metadata:", error);// Fallback to file name if metadata cannot be readmusicTitle.textContent = file.name.replace(/\.[^/.]+$/, "");musicArtist.textContent = "Unknown Artist";musicCover.style.backgroundImage = ''; // No covermusicCover.classList.remove('visible'); // Hide cover}});}});function setupAudioContext(audio) {audioContext = new (window.AudioContext || window.webkitAudioContext)();analyser = audioContext.createAnalyser();source = audioContext.createMediaElementSource(audio);source.connect(analyser);analyser.connect(audioContext.destination);analyser.fftSize = 512;bufferLength = analyser.frequencyBinCount;dataArray = new Uint8Array(bufferLength);draw();}function draw() {requestAnimationFrame(draw);analyser.getByteFrequencyData(dataArray);ctx.clearRect(0, 0, canvas.width, canvas.height);if (currentMode === 'bar') {drawBars();} else if (currentMode === 'circle') {drawCircle();} else if (currentMode === 'arc') {drawArc();}}function drawBars() {const barWidth = (canvas.width / bufferLength) * 1.5; // Thinner barslet barHeight;let x = 0;for (let i = 0; i < bufferLength; i++) {barHeight = dataArray[i] / 2;// Create a gradient for each barconst gradient = ctx.createLinearGradient(x, canvas.height, x + barWidth, canvas.height - barHeight);gradient.addColorStop(0, '#00b4db');gradient.addColorStop(1, '#0083b0');ctx.fillStyle = gradient;ctx.fillRect(x, canvas.height - barHeight, barWidth, barHeight);// Add glow effectctx.shadowBlur = 10;ctx.shadowColor = 'rgba(0, 180, 219, 0.7)';x += barWidth + 1;}}function drawCircle() {const centerX = canvas.width / 2;const centerY = canvas.height / 2;const radius = 100;const barWidth = 2; // Thinner barsconst angleStep = (Math.PI * 2) / bufferLength;for (let i = 0; i < bufferLength; i++) {const barHeight = dataArray[i] / 2;const angle = i * angleStep;const x = centerX + Math.cos(angle) * radius;const y = centerY + Math.sin(angle) * radius;const endX = centerX + Math.cos(angle) * (radius + barHeight);const endY = centerY + Math.sin(angle) * (radius + barHeight);// Create a gradient for each barconst gradient = ctx.createLinearGradient(x, y, endX, endY);gradient.addColorStop(0, '#00b4db');gradient.addColorStop(1, '#0083b0');ctx.strokeStyle = gradient;ctx.lineWidth = barWidth;ctx.beginPath();ctx.moveTo(x, y);ctx.lineTo(endX, endY);ctx.stroke();// Add glow effectctx.shadowBlur = 10;ctx.shadowColor = 'rgba(0, 180, 219, 0.7)';}}function drawArc() {const centerX = canvas.width / 2;const centerY = canvas.height / 2;const radius = 100;const barWidth = 2; // Thinner barsconst angleStep = (Math.PI * 2) / bufferLength;for (let i = 0; i < bufferLength; i++) {const barHeight = dataArray[i] / 2;const startAngle = i * angleStep;const endAngle = startAngle + angleStep;// Create a gradient for each barconst gradient = ctx.createRadialGradient(centerX, centerY, radius,centerX, centerY, radius + barHeight);gradient.addColorStop(0, '#00b4db');gradient.addColorStop(1, '#0083b0');ctx.strokeStyle = gradient;ctx.lineWidth = barWidth;ctx.beginPath();ctx.arc(centerX, centerY, radius + barHeight, startAngle, endAngle);ctx.stroke();// Add glow effectctx.shadowBlur = 10;ctx.shadowColor = 'rgba(0, 180, 219, 0.7)';}}</script></body></html>

效果展示

条形频谱模式
在这里插入图片描述

圆形频谱模式
在这里插入图片描述

源码下载

音频可视化小工具


http://www.ppmy.cn/embedded/155197.html

相关文章

nolo sonic 2使用串流方式运行steamVR时报错301(VRApplicationError_IPCFailed)

1. 问题描述 最近换了一台新电脑&#xff0c;使用nolo sonic 2 VR眼镜&#xff0c;尝试和自己的笔记本通过串流方式连接steamVR。无奈连接后就开始报错&#xff1a; 点开“更多信息”后&#xff0c;提示&#xff1a; 2. 解决过程 一开始认为是电脑内安装的软件冲突的问题…

[UE4图文系列] 5.字符串转中文乱码问题说明

原文连接&#xff1a;[UE4图文系列] 5.字符串转中文乱码问题说明 - 哔哩哔哩 本例以原生C和UE4 C字符串传输中出现的中文乱码问题进行说明 一.乱码示例: 1.直接用中文字符串初始化FString,在蓝图中进行打印 FString GetStrWithChinese() {FString fstr"这是一句中文"…

安卓端使用线程下载文件卡滞

需求&#xff1a;从阿里云oss下载大量的图片&#xff08;4000&#xff09;到手机的内部存储 /// <summary>/// 指定要下载的文件列表/// </summary>/// <param name"aliyunFile">阿里云上要下载文件所在文件夹</param>/// <param name&qu…

财务RPA就是财务机器人吗?有什么作用

近年来&#xff0c;财务RPA&#xff08;机器人流程自动化&#xff09;逐渐成为财务领域的热门话题。很多人初次听到“财务RPA”时&#xff0c;可能会疑惑&#xff1a;财务RPA是不是财务机器人&#xff1f;它到底能做什么&#xff1f;带着这些问题&#xff0c;我们一起来探讨财务…

职场沟通与行为

职场沟通与行为 引言 在职场上&#xff0c;你是否曾遇到过困惑的沟通&#xff1f;是否对同事的行为有过疑虑&#xff1f;这不仅是个别现象&#xff0c;而是我们这个时代工作文化中的普遍问题。许多职场的摩擦&#xff0c;来自沟通不畅或是行为不当。那么&#xff0c;如何才能…

使用 ChatGPT 生成和改进你的论文

文章目录 零、前言一、操作引导二、 生成段落或文章片段三、重写段落四、扩展内容五、生成大纲内容六、提高清晰度和精准度七、解决特定的写作挑战八、感受 零、前言 我是虚竹哥&#xff0c;目标是带十万人玩转ChatGPT。 ChatGPT 是一个非常有用的工具&#xff0c;可以帮助你…

随机森林分类算法原理与实验分析

随机森林分类算法原理与实验分析 1. 引言 随机森林(Random Forest)是一种集成学习方法,它通过构建多个决策树并结合它们的预测结果来进行分类。你可以把它想象成一个“团队决策”的过程:团队中的每个成员(决策树)都独立发表意见,最后通过投票决定最终结果。这种方法不…

【springboot】Spring 官方抛弃了 Java 8!新idea如何创建java8项目

解决idea至少创建jdk17项目 问题 idea现在只能创建最少jdk17&#xff0c;不能创建java8了吗?解决 问题 idea现在只能创建最少jdk17&#xff0c;不能创建java8了吗 我本来以为是 IDEA 版本更新导致的 Bug&#xff0c;开始还没在意。 直到我今天自己初始化项目时才发现&am…