Sunradiance commited on
Commit
a4e8fa9
·
verified ·
1 Parent(s): 2b146c7

Add 2 files

Browse files
Files changed (2) hide show
  1. README.md +7 -5
  2. index.html +380 -19
README.md CHANGED
@@ -1,10 +1,12 @@
1
  ---
2
- title: Spectral Analyzer
3
- emoji: 🐢
4
- colorFrom: indigo
5
- colorTo: red
6
  sdk: static
7
  pinned: false
 
 
8
  ---
9
 
10
- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
1
  ---
2
+ title: spectral-analyzer
3
+ emoji: 🐳
4
+ colorFrom: blue
5
+ colorTo: blue
6
  sdk: static
7
  pinned: false
8
+ tags:
9
+ - deepsite
10
  ---
11
 
12
+ Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
index.html CHANGED
@@ -1,19 +1,380 @@
1
- <!doctype html>
2
- <html>
3
- <head>
4
- <meta charset="utf-8" />
5
- <meta name="viewport" content="width=device-width" />
6
- <title>My static Space</title>
7
- <link rel="stylesheet" href="style.css" />
8
- </head>
9
- <body>
10
- <div class="card">
11
- <h1>Welcome to your static Space!</h1>
12
- <p>You can modify this app directly by editing <i>index.html</i> in the Files and versions tab.</p>
13
- <p>
14
- Also don't forget to check the
15
- <a href="https://huggingface.co/docs/hub/spaces" target="_blank">Spaces documentation</a>.
16
- </p>
17
- </div>
18
- </body>
19
- </html>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>Spectral Analyzer</title>
7
+ <script src="https://cdn.tailwindcss.com"></script>
8
+ <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
9
+ <style>
10
+ .spectrum-bg {
11
+ background: linear-gradient(135deg, #0f172a 0%, #1e293b 100%);
12
+ }
13
+ .bar {
14
+ transition: height 0.1s ease-out;
15
+ background: linear-gradient(to top, #3b82f6, #8b5cf6);
16
+ }
17
+ .frequency-line {
18
+ position: absolute;
19
+ width: 100%;
20
+ height: 1px;
21
+ background-color: rgba(255, 255, 255, 0.1);
22
+ }
23
+ .frequency-label {
24
+ position: absolute;
25
+ right: 0;
26
+ transform: translateY(50%);
27
+ color: rgba(255, 255, 255, 0.5);
28
+ font-size: 0.75rem;
29
+ }
30
+ .visualizer-container {
31
+ position: relative;
32
+ }
33
+ canvas {
34
+ width: 100%;
35
+ height: 100%;
36
+ }
37
+ </style>
38
+ </head>
39
+ <body class="bg-gray-900 text-gray-100 min-h-screen">
40
+ <div class="container mx-auto px-4 py-8">
41
+ <div class="text-center mb-8">
42
+ <h1 class="text-4xl font-bold mb-2 bg-gradient-to-r from-blue-400 to-purple-500 bg-clip-text text-transparent">
43
+ Spectral Analyzer
44
+ </h1>
45
+ <p class="text-gray-400">Real-time audio frequency visualization</p>
46
+ </div>
47
+
48
+ <div class="grid grid-cols-1 lg:grid-cols-3 gap-6">
49
+ <div class="lg:col-span-2">
50
+ <div class="spectrum-bg rounded-xl shadow-xl p-4">
51
+ <div class="flex items-center justify-between mb-4">
52
+ <div class="flex items-center space-x-2">
53
+ <div class="w-3 h-3 rounded-full bg-red-500"></div>
54
+ <div class="w-3 h-3 rounded-full bg-yellow-500"></div>
55
+ <div class="w-3 h-3 rounded-full bg-green-500"></div>
56
+ </div>
57
+ <div class="text-sm text-gray-400">
58
+ <span id="status">Waiting for input...</span>
59
+ </div>
60
+ </div>
61
+
62
+ <div class="visualizer-container relative h-64 w-full" id="visualizer">
63
+ <!-- Frequency labels will be added here -->
64
+ <canvas id="canvas" class="rounded-lg"></canvas>
65
+ <div id="frequencyLabels" class="absolute top-0 left-0 w-full h-full pointer-events-none"></div>
66
+ </div>
67
+ </div>
68
+ </div>
69
+
70
+ <div>
71
+ <div class="bg-gray-800 rounded-xl shadow-xl p-6 h-full">
72
+ <h2 class="text-xl font-semibold mb-4 text-purple-400">Controls</h2>
73
+
74
+ <button id="toggleMic" class="w-full py-3 px-4 bg-blue-600 hover:bg-blue-700 rounded-lg flex items-center justify-center transition mb-4">
75
+ <i class="fas fa-microphone mr-2"></i>
76
+ <span>Start Analysis</span>
77
+ </button>
78
+
79
+ <div class="space-y-4">
80
+ <div>
81
+ <label class="block text-sm font-medium text-gray-300 mb-1">FFT Size</label>
82
+ <select id="fftSize" class="w-full bg-gray-700 rounded-lg px-4 py-2 text-sm focus:ring-2 focus:ring-purple-500 outline-none">
83
+ <option value="64">64</option>
84
+ <option value="128">128</option>
85
+ <option value="256" selected>256</option>
86
+ <option value="512">512</option>
87
+ <option value="1024">1024</option>
88
+ <option value="2048">2048</option>
89
+ <option value="4096">4096</option>
90
+ </select>
91
+ </div>
92
+
93
+ <div>
94
+ <label class="block text-sm font-medium text-gray-300 mb-1">Smoothing</label>
95
+ <input id="smoothing" type="range" min="0" max="1" step="0.01" value="0.6" class="w-full" />
96
+ <div class="text-xs text-gray-400 flex justify-between">
97
+ <span>Sharp</span>
98
+ <span>Smooth</span>
99
+ </div>
100
+ </div>
101
+
102
+ <div class="flex justify-between items-center pt-4 border-t border-gray-700">
103
+ <span class="text-sm text-gray-400">Volume</span>
104
+ <div id="volumeMeter" class="flex items-center text-sm text-green-400">
105
+ <i class="fas fa-volume-up mr-1"></i>
106
+ <span>0.00</span>
107
+ </div>
108
+ </div>
109
+ </div>
110
+ </div>
111
+ </div>
112
+ </div>
113
+
114
+ <div class="mt-8 bg-gray-800 rounded-xl shadow-xl p-6">
115
+ <h2 class="text-xl font-semibold mb-4 text-purple-400">Frequency Details</h2>
116
+ <div class="grid grid-cols-2 md:grid-cols-4 gap-4">
117
+ <div class="bg-gray-700 p-3 rounded-lg">
118
+ <div class="text-sm text-gray-400">Dominant Freq</div>
119
+ <div id="dominantFreq" class="text-xl font-mono">0 Hz</div>
120
+ </div>
121
+ <div class="bg-gray-700 p-3 rounded-lg">
122
+ <div class="text-sm text-gray-400">Bass (20-250Hz)</div>
123
+ <div id="bassLevel" class="text-xl font-mono">0.00</div>
124
+ </div>
125
+ <div class="bg-gray-700 p-3 rounded-lg">
126
+ <div class="text-sm text-gray-400">Mid (250-4kHz)</div>
127
+ <div id="midLevel" class="text-xl font-mono">0.00</div>
128
+ </div>
129
+ <div class="bg-gray-700 p-3 rounded-lg">
130
+ <div class="text-sm text-gray-400">Treble (4k-20kHz)</div>
131
+ <div id="trebleLevel" class="text-xl font-mono">0.00</div>
132
+ </div>
133
+ </div>
134
+ </div>
135
+ </div>
136
+
137
+ <script>
138
+ document.addEventListener('DOMContentLoaded', () => {
139
+ // Audio context variables
140
+ let audioContext;
141
+ let analyser;
142
+ let microphone;
143
+ let dataArray;
144
+ let isAnalyzing = false;
145
+ let animationId;
146
+
147
+ // DOM elements
148
+ const toggleMicBtn = document.getElementById('toggleMic');
149
+ const statusElement = document.getElementById('status');
150
+ const canvas = document.getElementById('canvas');
151
+ const ctx = canvas.getContext('2d');
152
+ const fftSizeSelect = document.getElementById('fftSize');
153
+ const smoothingInput = document.getElementById('smoothing');
154
+ const volumeMeter = document.getElementById('volumeMeter');
155
+ const dominantFreqElement = document.getElementById('dominantFreq');
156
+ const bassLevelElement = document.getElementById('bassLevel');
157
+ const midLevelElement = document.getElementById('midLevel');
158
+ const trebleLevelElement = document.getElementById('trebleLevel');
159
+ const frequencyLabels = document.getElementById('frequencyLabels');
160
+
161
+ // Initialize visualizer
162
+ function initVisualizer() {
163
+ // Set canvas to full size of its container
164
+ const container = document.getElementById('visualizer');
165
+ canvas.width = container.clientWidth;
166
+ canvas.height = container.clientHeight;
167
+
168
+ // Set visual properties
169
+ ctx.fillStyle = 'rgb(0, 0, 0)';
170
+ ctx.fillRect(0, 0, canvas.width, canvas.height);
171
+
172
+ // Add frequency labels
173
+ addFrequencyLabels();
174
+ }
175
+
176
+ // Add frequency labels to the visualizer
177
+ function addFrequencyLabels() {
178
+ // Clear existing labels
179
+ frequencyLabels.innerHTML = '';
180
+
181
+ const frequencies = [20, 50, 100, 200, 500, 1000, 2000, 5000, 10000, 20000];
182
+ const height = frequencyLabels.clientHeight;
183
+
184
+ frequencies.forEach(freq => {
185
+ // Convert frequency to position (logarithmic scale)
186
+ const minFreq = 20;
187
+ const maxFreq = 20000;
188
+ const minPosition = 0;
189
+ const maxPosition = height;
190
+
191
+ // Logarithmic scaling
192
+ const position = height - (
193
+ (Math.log10(freq) - Math.log10(minFreq)) /
194
+ (Math.log10(maxFreq) - Math.log10(minFreq)) * maxPosition
195
+ );
196
+
197
+ // Create line
198
+ const line = document.createElement('div');
199
+ line.className = 'frequency-line';
200
+ line.style.top = `${position}px`;
201
+ frequencyLabels.appendChild(line);
202
+
203
+ // Create label
204
+ const label = document.createElement('div');
205
+ label.className = 'frequency-label pr-1';
206
+ label.style.top = `${position}px`;
207
+ label.textContent = freq >= 1000 ? `${Math.round(freq/1000)}k` : freq;
208
+ frequencyLabels.appendChild(label);
209
+ });
210
+ }
211
+
212
+ // Start audio analysis
213
+ async function startAnalysis() {
214
+ try {
215
+ // Initialize audio context
216
+ audioContext = new (window.AudioContext || window.webkitAudioContext)();
217
+ analyser = audioContext.createAnalyser();
218
+
219
+ // Set FFT size
220
+ analyser.fftSize = parseInt(fftSizeSelect.value);
221
+ const bufferLength = analyser.frequencyBinCount;
222
+ dataArray = new Uint8Array(bufferLength);
223
+
224
+ // Get microphone input
225
+ const stream = await navigator.mediaDevices.getUserMedia({ audio: true });
226
+ microphone = audioContext.createMediaStreamSource(stream);
227
+ microphone.connect(analyser);
228
+
229
+ // Update status
230
+ statusElement.textContent = 'Analyzing audio...';
231
+ toggleMicBtn.innerHTML = '<i class="fas fa-microphone-slash mr-2"></i><span>Stop Analysis</span>';
232
+ toggleMicBtn.classList.remove('bg-blue-600', 'hover:bg-blue-700');
233
+ toggleMicBtn.classList.add('bg-red-600', 'hover:bg-red-700');
234
+ isAnalyzing = true;
235
+
236
+ // Start visualization
237
+ draw();
238
+ } catch (error) {
239
+ console.error('Error accessing microphone:', error);
240
+ statusElement.textContent = 'Error: ' + error.message;
241
+ }
242
+ }
243
+
244
+ // Stop audio analysis
245
+ function stopAnalysis() {
246
+ if (microphone && audioContext) {
247
+ microphone.disconnect();
248
+ audioContext.close();
249
+ }
250
+
251
+ if (animationId) {
252
+ cancelAnimationFrame(animationId);
253
+ }
254
+
255
+ // Clear canvas
256
+ ctx.fillStyle = 'rgb(0, 0, 0)';
257
+ ctx.fillRect(0, 0, canvas.width, canvas.height);
258
+
259
+ // Update status
260
+ statusElement.textContent = 'Analysis stopped';
261
+ toggleMicBtn.innerHTML = '<i class="fas fa-microphone mr-2"></i><span>Start Analysis</span>';
262
+ toggleMicBtn.classList.remove('bg-red-600', 'hover:bg-red-700');
263
+ toggleMicBtn.classList.add('bg-blue-600', 'hover:bg-blue-700');
264
+ isAnalyzing = false;
265
+
266
+ // Reset indicators
267
+ volumeMeter.innerHTML = '<i class="fas fa-volume-up mr-1"></i><span>0.00</span>';
268
+ dominantFreqElement.textContent = '0 Hz';
269
+ bassLevelElement.textContent = '0.00';
270
+ midLevelElement.textContent = '0.00';
271
+ trebleLevelElement.textContent = '0.00';
272
+ }
273
+
274
+ // Draw the frequency visualization
275
+ function draw() {
276
+ animationId = requestAnimationFrame(draw);
277
+
278
+ analyser.smoothingTimeConstant = parseFloat(smoothingInput.value);
279
+ analyser.getByteFrequencyData(dataArray);
280
+
281
+ // Clear canvas
282
+ ctx.fillStyle = 'rgba(0, 0, 0, 0.1)';
283
+ ctx.fillRect(0, 0, canvas.width, canvas.height);
284
+
285
+ const barWidth = canvas.width / dataArray.length;
286
+ let volumeSum = 0;
287
+ let maxValue = 0;
288
+ let maxIndex = 0;
289
+
290
+ // Calculate frequency ranges
291
+ let bassSum = 0;
292
+ let midSum = 0;
293
+ let trebleSum = 0;
294
+ const bassEnd = Math.floor(250 / (audioContext.sampleRate / 2) * dataArray.length);
295
+ const midEnd = Math.floor(4000 / (audioContext.sampleRate / 2) * dataArray.length);
296
+
297
+ for (let i = 0; i < dataArray.length; i++) {
298
+ const value = dataArray[i] / 255;
299
+ volumeSum += value;
300
+
301
+ // Track maximum frequency
302
+ if (value > maxValue) {
303
+ maxValue = value;
304
+ maxIndex = i;
305
+ }
306
+
307
+ // Accumulate frequency ranges
308
+ if (i < bassEnd) {
309
+ bassSum += value;
310
+ } else if (i < midEnd) {
311
+ midSum += value;
312
+ } else {
313
+ trebleSum += value;
314
+ }
315
+
316
+ // Draw frequency bar
317
+ const barHeight = value * canvas.height;
318
+ const x = i * barWidth;
319
+
320
+ // Create gradient for the bar
321
+ const gradient = ctx.createLinearGradient(x, canvas.height - barHeight, x, canvas.height);
322
+ gradient.addColorStop(0, 'rgba(59, 130, 246, 0.8)');
323
+ gradient.addColorStop(1, 'rgba(139, 92, 246, 0.8)');
324
+
325
+ ctx.fillStyle = gradient;
326
+ ctx.fillRect(x, canvas.height - barHeight, barWidth - 1, barHeight);
327
+ }
328
+
329
+ // Calculate and display volume
330
+ const avgVolume = volumeSum / dataArray.length;
331
+ volumeMeter.innerHTML = `<i class="fas fa-volume-${avgVolume > 0.5 ? 'up' : avgVolume > 0.1 ? 'down' : 'mute'} mr-1"></i><span>${avgVolume.toFixed(2)}</span>`;
332
+
333
+ // Calculate dominant frequency
334
+ const dominantFreq = maxIndex * (audioContext.sampleRate / 2) / dataArray.length;
335
+ dominantFreqElement.textContent = `${Math.round(dominantFreq)} Hz`;
336
+
337
+ // Calculate and display frequency ranges
338
+ const bassAvg = bassSum / bassEnd;
339
+ const midAvg = midSum / (midEnd - bassEnd);
340
+ const trebleAvg = trebleSum / (dataArray.length - midEnd);
341
+
342
+ bassLevelElement.textContent = bassAvg.toFixed(2);
343
+ midLevelElement.textContent = midAvg.toFixed(2);
344
+ trebleLevelElement.textContent = trebleAvg.toFixed(2);
345
+ }
346
+
347
+ // Event listeners
348
+ toggleMicBtn.addEventListener('click', () => {
349
+ if (isAnalyzing) {
350
+ stopAnalysis();
351
+ } else {
352
+ startAnalysis();
353
+ }
354
+ });
355
+
356
+ fftSizeSelect.addEventListener('change', () => {
357
+ if (analyser && isAnalyzing) {
358
+ analyser.fftSize = parseInt(fftSizeSelect.value);
359
+ const bufferLength = analyser.frequencyBinCount;
360
+ dataArray = new Uint8Array(bufferLength);
361
+ }
362
+ });
363
+
364
+ // Handle window resize
365
+ window.addEventListener('resize', () => {
366
+ initVisualizer();
367
+ });
368
+
369
+ // Initialize
370
+ initVisualizer();
371
+
372
+ // Add FFT size explanation
373
+ const fftSizeLabel = document.createElement('div');
374
+ fftSizeLabel.className = 'text-xs text-gray-400 mt-1';
375
+ fftSizeLabel.textContent = 'Higher values provide more frequency detail';
376
+ fftSizeSelect.parentNode.appendChild(fftSizeLabel);
377
+ });
378
+ </script>
379
+ <p style="border-radius: 8px; text-align: center; font-size: 12px; color: #fff; margin-top: 16px;position: fixed; left: 8px; bottom: 8px; z-index: 10; background: rgba(0, 0, 0, 0.8); padding: 4px 8px;">Made with <a href="https://enzostvs-deepsite.hf.space" style="color: #fff;" target="_blank" >DeepSite</a> <img src="https://enzostvs-deepsite.hf.space/logo.svg" alt="DeepSite Logo" style="width: 16px; height: 16px; vertical-align: middle;"></p></body>
380
+ </html>