omar1232 commited on
Commit
555abe2
·
verified ·
1 Parent(s): 71895d0

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +137 -104
app.py CHANGED
@@ -43,134 +43,170 @@ def audio_visualizer(audio_file, audio_record):
43
  elif audio_record:
44
  vis_data, audio_output = process_audio(audio_record)
45
  else:
46
- return "Please upload an audio file or record audio.", None, None
47
 
48
- return vis_data, audio_output, vis_data
49
 
50
  # Custom CSS and JavaScript for the visualizer
51
  visualizer_html = """
52
- <div class="visualizer-container" id="visualizer" style="position: relative; width: 100%; height: 500px; background: linear-gradient(135deg, #1a1a2e 0%, #16213e 100%); border-radius: 16px; overflow: hidden; box-shadow: 0 15px 40px rgba(0, 0, 0, 0.4);">
53
- <div class="audio-wave" id="audioWave" style="position: absolute; bottom: 0; left: 0; width: 100%; height: 120px; background: linear-gradient(to top, rgba(0, 180, 219, 0.2), transparent);"></div>
54
- <div class="loading-spinner" id="loadingSpinner" style="display: none; position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); border: 4px solid rgba(255,255,255,0.3); border-top: 4px solid #00b4db; border-radius: 50%; width: 40px; height: 40px; animation: spin 1s linear infinite;"></div>
55
- </div>
56
 
57
  <style>
58
- .visualizer-container { transition: background 0.5s; }
59
- .bar {
60
- position: absolute;
61
- bottom: 0;
62
- background: linear-gradient(to top, #00b4db, #0083b0);
63
- border-radius: 6px 6px 0 0;
64
- transition: height 0.1s cubic-bezier(0.4, 0, 0.2, 1);
65
- }
66
- @keyframes spin {
67
- 0% { transform: translate(-50%, -50%) rotate(0deg); }
68
- 100% { transform: translate(-50%, -50%) rotate(360deg); }
69
- }
70
- .bg-animated {
71
- animation: gradient 15s ease infinite;
72
- background-size: 400% 400%;
73
- }
74
- @keyframes gradient {
75
- 0% { background-position: 0% 50%; }
76
- 50% { background-position: 100% 50%; }
77
- 100% { background-position: 0% 50%; }
78
  }
79
  </style>
80
 
81
  <script>
82
  document.addEventListener('DOMContentLoaded', () => {
83
- const visualizer = document.getElementById('visualizer');
84
- const audioWave = document.getElementById('audioWave');
85
- let bars = [];
86
- const barCount = 80;
87
- const barSpacing = 2;
88
-
89
- // Create bars
90
- function createBars() {
91
- visualizer.querySelectorAll('.bar').forEach(bar => bar.remove());
92
- bars = [];
93
- const containerWidth = visualizer.clientWidth;
94
- if (containerWidth === 0) {
95
- console.warn('Visualizer container width is 0, retrying on resize');
96
- return;
97
- }
98
- const barWidth = (containerWidth / barCount) - barSpacing;
99
- for (let i = 0; i < barCount; i++) {
100
- const bar = document.createElement('div');
101
- bar.className = 'bar';
102
- bar.style.left = `${i * (barWidth + barSpacing)}px`;
103
- bar.style.width = `${barWidth}px`;
104
- bar.style.height = '0px';
105
- visualizer.appendChild(bar);
106
- bars.push(bar);
107
- }
108
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
109
 
110
- // Initial creation of bars
111
- createBars();
 
 
 
112
 
113
- // Reset visualization
114
- function resetVisualization() {
115
- bars.forEach(bar => {
116
- bar.style.height = '0px';
117
- });
118
- audioWave.style.clipPath = `path('M0 ${visualizer.clientHeight / 2} L${visualizer.clientWidth} ${visualizer.clientHeight / 2} Z')`;
119
  }
120
 
121
- // Function to update visualization
122
- function updateVisualization(data) {
123
- if (!data || !data.frequencies || data.frequencies.length === 0) {
124
- resetVisualization();
125
- return;
126
- }
127
- const { frequencies } = data;
128
- const maxFreq = Math.max(...frequencies);
129
- if (maxFreq === 0) {
130
- resetVisualization();
131
- return;
 
132
  }
133
- const freqCount = Math.min(frequencies.length, barCount);
134
- for (let i = 0; i < freqCount; i++) {
135
- const height = (frequencies[i] / maxFreq) * visualizer.clientHeight * 0.8;
136
- bars[i].style.height = `${height}px`;
 
 
 
 
 
 
 
 
 
137
  }
138
- const wavePoints = frequencies.map((f, i) => [
139
- (i / frequencies.length) * visualizer.clientWidth,
140
- visualizer.clientHeight * (1 - (f / maxFreq))
141
- ]);
142
- let wavePath = `path('M0 ${visualizer.clientHeight / 2} `;
143
- wavePoints.forEach(([x, y]) => {
144
- wavePath += `L${x} ${y} `;
145
- });
146
- wavePath += `L${visualizer.clientWidth} ${visualizer.clientHeight / 2} Z')`;
147
- audioWave.style.clipPath = wavePath;
148
  }
149
 
150
- // Listen for updates from Gradio
151
- window.addEventListener('message', (event) => {
152
- if (event.data && event.data.vis_data) {
153
- updateVisualization(event.data.vis_data);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
154
  }
155
- });
156
 
157
- window.addEventListener('resize', createBars);
 
 
 
 
 
 
 
 
 
158
 
159
- // Poll the hidden JSON output for updates
 
 
 
160
  setInterval(() => {
161
- const visDataOutput = document.querySelector('#vis_data_output textarea');
 
162
  if (visDataOutput && visDataOutput.value) {
163
  try {
164
- const data = JSON.parse(visDataOutput.value);
165
- updateVisualization(data);
166
  } catch (e) {
167
  console.error('Error parsing visualization data:', e);
168
- resetVisualization();
169
  }
170
  } else {
171
- resetVisualization();
 
172
  }
173
- }, 500);
174
  });
175
  </script>
176
  """
@@ -178,7 +214,7 @@ visualizer_html = """
178
  # Gradio interface
179
  with gr.Blocks() as demo:
180
  gr.Markdown("# Advanced Audio Visualizer")
181
- gr.Markdown("Upload an audio file or record audio to visualize frequencies and beats.")
182
 
183
  with gr.Row():
184
  audio_file = gr.Audio(sources=["upload"], type="filepath", label="Upload Audio")
@@ -188,9 +224,6 @@ with gr.Blocks() as demo:
188
  vis_output = gr.JSON(label="Visualization Data")
189
  audio_output = gr.Audio(label="Audio Playback", type="filepath")
190
 
191
- # Hidden output to pass data to JavaScript
192
- vis_data_output = gr.JSON(elem_id="vis_data_output", visible=False)
193
-
194
  with gr.Row():
195
  submit = gr.Button("Visualize")
196
  clear = gr.Button("Clear")
@@ -201,12 +234,12 @@ with gr.Blocks() as demo:
201
  submit.click(
202
  fn=audio_visualizer,
203
  inputs=[audio_file, audio_record],
204
- outputs=[vis_output, audio_output, vis_data_output]
205
  )
206
  clear.click(
207
- fn=lambda: (None, None, None),
208
  inputs=[],
209
- outputs=[audio_file, audio_record, vis_data_output]
210
  )
211
 
212
  # Launch Gradio app
 
43
  elif audio_record:
44
  vis_data, audio_output = process_audio(audio_record)
45
  else:
46
+ return "Please upload an audio file or record audio.", None
47
 
48
+ return vis_data, audio_output
49
 
50
  # Custom CSS and JavaScript for the visualizer
51
  visualizer_html = """
52
+ <canvas id="visualizerCanvas" style="width: 100%; height: 500px; background: #1a1a2e; border-radius: 16px; box-shadow: 0 15px 40px rgba(0, 0, 0, 0.4);"></canvas>
 
 
 
53
 
54
  <style>
55
+ canvas {
56
+ display: block;
57
+ max-width: 800px;
58
+ margin: 0 auto;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
59
  }
60
  </style>
61
 
62
  <script>
63
  document.addEventListener('DOMContentLoaded', () => {
64
+ const canvas = document.getElementById('visualizerCanvas');
65
+ const ctx = canvas.getContext('2d');
66
+ let audioElement = null;
67
+ let data = { frequencies: [], beat_times: [], volume: 0 };
68
+ let particles = [];
69
+ let lastBeatIndex = 0;
70
+
71
+ // Set canvas size to match its CSS size
72
+ function resizeCanvas() {
73
+ canvas.width = canvas.offsetWidth;
74
+ canvas.height = canvas.offsetHeight;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
75
  }
76
+ resizeCanvas();
77
+ window.addEventListener('resize', resizeCanvas);
78
+
79
+ // Particle class for beat effects
80
+ class Particle {
81
+ constructor(x, y, radius, speedX, speedY) {
82
+ this.x = x;
83
+ this.y = y;
84
+ this.radius = radius;
85
+ this.speedX = speedX;
86
+ this.speedY = speedY;
87
+ this.alpha = 1;
88
+ }
89
 
90
+ update() {
91
+ this.x += this.speedX;
92
+ this.y += this.speedY;
93
+ this.alpha -= 0.02;
94
+ }
95
 
96
+ draw() {
97
+ ctx.beginPath();
98
+ ctx.arc(this.x, this.y, this.radius, 0, Math.PI * 2);
99
+ ctx.fillStyle = `rgba(0, 180, 219, ${this.alpha})`;
100
+ ctx.fill();
101
+ }
102
  }
103
 
104
+ // Spawn particles on beats
105
+ function spawnParticles(volume) {
106
+ const centerX = canvas.width / 2;
107
+ const centerY = canvas.height / 2;
108
+ const particleCount = Math.floor(volume / 2) + 5; // More particles for higher volume
109
+ for (let i = 0; i < particleCount; i++) {
110
+ const angle = Math.random() * Math.PI * 2;
111
+ const speed = Math.random() * 5 + 2;
112
+ const speedX = Math.cos(angle) * speed;
113
+ const speedY = Math.sin(angle) * speed;
114
+ const radius = Math.random() * 5 + 2;
115
+ particles.push(new Particle(centerX, centerY, radius, speedX, speedY));
116
  }
117
+ }
118
+
119
+ // Check for beats based on audio playback time
120
+ function checkBeats() {
121
+ if (!audioElement || !data.beat_times) return;
122
+ const currentTime = audioElement.currentTime;
123
+ for (let i = lastBeatIndex; i < data.beat_times.length; i++) {
124
+ if (currentTime >= data.beat_times[i]) {
125
+ spawnParticles(data.volume);
126
+ lastBeatIndex = i + 1;
127
+ } else {
128
+ break;
129
+ }
130
  }
 
 
 
 
 
 
 
 
 
 
131
  }
132
 
133
+ // Animation loop
134
+ function animate() {
135
+ requestAnimationFrame(animate);
136
+
137
+ // Clear canvas
138
+ ctx.fillStyle = 'rgba(26, 26, 46, 0.8)';
139
+ ctx.fillRect(0, 0, canvas.width, canvas.height);
140
+
141
+ // Center of the canvas
142
+ const centerX = canvas.width / 2;
143
+ const centerY = canvas.height / 2;
144
+ const radius = Math.min(canvas.width, canvas.height) * 0.2;
145
+
146
+ // Draw glowing center circle (pulsing with volume)
147
+ const glowRadius = radius * (1 + data.volume / 100);
148
+ const gradient = ctx.createRadialGradient(centerX, centerY, 0, centerX, centerY, glowRadius);
149
+ gradient.addColorStop(0, `rgba(0, 180, 219, ${0.5 + data.volume / 200})`);
150
+ gradient.addColorStop(1, 'rgba(0, 180, 219, 0)');
151
+ ctx.beginPath();
152
+ ctx.arc(centerX, centerY, glowRadius, 0, Math.PI * 2);
153
+ ctx.fillStyle = gradient;
154
+ ctx.fill();
155
+
156
+ // Draw circular spectrum
157
+ const freqCount = data.frequencies.length;
158
+ const barCount = 100; // Number of bars in the circle
159
+ const angleStep = (Math.PI * 2) / barCount;
160
+ for (let i = 0; i < barCount; i++) {
161
+ const freqIndex = Math.floor((i / barCount) * freqCount);
162
+ const freqValue = freqIndex < freqCount ? data.frequencies[freqIndex] : 0;
163
+ const maxFreq = Math.max(...data.frequencies) || 1;
164
+ const barLength = (freqValue / maxFreq) * 100 + 20; // Scale bar length
165
+ const angle = i * angleStep;
166
+
167
+ const x1 = centerX + Math.cos(angle) * radius;
168
+ const y1 = centerY + Math.sin(angle) * radius;
169
+ const x2 = centerX + Math.cos(angle) * (radius + barLength);
170
+ const y2 = centerY + Math.sin(angle) * (radius + barLength);
171
+
172
+ ctx.beginPath();
173
+ ctx.moveTo(x1, y1);
174
+ ctx.lineTo(x2, y2);
175
+ ctx.strokeStyle = `hsl(${i * (360 / barCount)}, 80%, 50%)`;
176
+ ctx.lineWidth = 2;
177
+ ctx.stroke();
178
  }
 
179
 
180
+ // Update and draw particles
181
+ particles = particles.filter(p => p.alpha > 0);
182
+ particles.forEach(particle => {
183
+ particle.update();
184
+ particle.draw();
185
+ });
186
+
187
+ // Check for beats
188
+ checkBeats();
189
+ }
190
 
191
+ // Start animation
192
+ animate();
193
+
194
+ // Poll the visible JSON output for updates
195
  setInterval(() => {
196
+ const visDataOutput = document.querySelector('div[label="Visualization Data"] textarea');
197
+ audioElement = document.querySelector('audio'); // Get the audio player
198
  if (visDataOutput && visDataOutput.value) {
199
  try {
200
+ data = JSON.parse(visDataOutput.value);
 
201
  } catch (e) {
202
  console.error('Error parsing visualization data:', e);
203
+ data = { frequencies: [], beat_times: [], volume: 0 };
204
  }
205
  } else {
206
+ data = { frequencies: [], beat_times: [], volume: 0 };
207
+ lastBeatIndex = 0; // Reset beat index
208
  }
209
+ }, 100); // Poll more frequently for smoother animations
210
  });
211
  </script>
212
  """
 
214
  # Gradio interface
215
  with gr.Blocks() as demo:
216
  gr.Markdown("# Advanced Audio Visualizer")
217
+ gr.Markdown("Upload an audio file or record audio to visualize frequencies and beats with dynamic effects.")
218
 
219
  with gr.Row():
220
  audio_file = gr.Audio(sources=["upload"], type="filepath", label="Upload Audio")
 
224
  vis_output = gr.JSON(label="Visualization Data")
225
  audio_output = gr.Audio(label="Audio Playback", type="filepath")
226
 
 
 
 
227
  with gr.Row():
228
  submit = gr.Button("Visualize")
229
  clear = gr.Button("Clear")
 
234
  submit.click(
235
  fn=audio_visualizer,
236
  inputs=[audio_file, audio_record],
237
+ outputs=[vis_output, audio_output]
238
  )
239
  clear.click(
240
+ fn=lambda: (None, None),
241
  inputs=[],
242
+ outputs=[audio_file, audio_record]
243
  )
244
 
245
  # Launch Gradio app