web visualizer 0.7
stavo cercando un effetto audio, tipo equalizzatore grafico, da utilizzare per "vedere" un effetto visivo sull'audio in entrata dal microfono, ho provato a mettere su qualcosa modificando il file di configurazione di cava, se non conoscete cava vi invito a leggere l'artico musica dentro il terminale in questo disgraziato blog, dicevo, ho cercato una via modificando la configurazione di cava ma con risultati tutt'altro che soddisfacenti, ho pensato allora di cercare qualcosa in giro per il mare magno di internet ma anche in questo caso con risultati deludenti. Ho pensato allora di provvedere direttamente con un codice eseguibile nel browser che poi come semplice codice HTML può essere utilizzato come plugin dentro OBS-Studio
Non sono uno sviluppatore e si vede, per adesso dopo una serie infinita di revisione del codice sono arrivato ad una versione quantomeno utilizzabile, di seguito vi inserisco il codice sorgente cosi che possiate a vostra volta provarlo e nel caso migliorarlo, potete anche provare l'effetto direttamente a questo link.
1<!DOCTYPE html>
2<html lang="it">
3<head>
4 <meta charset="UTF-8">
5 <meta name="viewport" content="width=device-width, initial-scale=1.0">
6 <title>Visualizzatore Audio Configurabile</title>
7 <style>
8 body {
9 margin: 0;
10 overflow: hidden;
11 background-color: #00FF00; /* Verde per chroma key */
12 font-family: 'Arial', sans-serif;
13 }
14
15 #container {
16 display: flex;
17 flex-direction: column;
18 justify-content: center;
19 align-items: center;
20 height: 100vh;
21 width: 100vw;
22 }
23
24 .visualizer-container {
25 position: relative;
26 width: 600px;
27 display: flex;
28 flex-direction: column;
29 align-items: center;
30 }
31
32 .channel-label {
33 color: rgba(255, 255, 255, 0.7);
34 margin: 5px 0;
35 font-size: 12px;
36 text-shadow: 0 0 3px rgba(0, 0, 0, 0.5);
37 }
38
39 #visualizer-top, #visualizer-bottom {
40 display: flex;
41 justify-content: space-between;
42 align-items: flex-end;
43 height: 150px;
44 width: 600px;
45 }
46
47 #visualizer-bottom {
48 align-items: flex-start;
49 margin-top: 2px;
50 }
51
52 #visualizer-horizontal {
53 display: flex;
54 justify-content: center;
55 align-items: center;
56 height: 300px;
57 width: 600px;
58 }
59
60 #visualizer-sequence {
61 display: flex;
62 justify-content: center;
63 align-items: center;
64 height: 300px;
65 width: 600px;
66 }
67
68 .bar-vertical {
69 width: 10px;
70 height: 5px;
71 margin: 0 2px;
72 background: linear-gradient(to top, #ff0055, #ff9500, #ffff00);
73 border-radius: 2px;
74 transition: height 0.05s ease;
75 }
76
77 .bar-horizontal-left {
78 width: 5px;
79 height: 10px;
80 margin: 2px 0;
81 background: linear-gradient(to left, #ff0055, #ff9500, #ffff00);
82 border-radius: 2px 0 0 2px;
83 transition: width 0.05s ease;
84 }
85
86 .bar-horizontal-right {
87 width: 5px;
88 height: 10px;
89 margin: 2px 0;
90 background: linear-gradient(to right, #ff0055, #ff9500, #ffff00);
91 border-radius: 0 2px 2px 0;
92 transition: width 0.05s ease;
93 }
94
95 .bar-sequence {
96 width: 5px;
97 height: 10px;
98 margin: 0 2px;
99 background: linear-gradient(to right, #ff0055, #ff9500, #ffff00);
100 border-radius: 2px;
101 transition: height 0.05s ease;
102 }
103
104 .divider-horizontal {
105 height: 2px;
106 width: 600px;
107 background: rgba(255, 255, 255, 0.5);
108 box-shadow: 0 0 10px rgba(255, 255, 255, 0.8);
109 }
110
111 .divider-vertical {
112 width: 2px;
113 height: 300px;
114 background: rgba(255, 255, 255, 0.5);
115 box-shadow: 0 0 10px rgba(255, 255, 255, 0.8);
116 }
117
118 .bar-pair {
119 display: flex;
120 flex-direction: row;
121 height: 5px;
122 }
123
124 #startButton {
125 position: absolute;
126 top: 20px;
127 left: 20px;
128 padding: 10px 20px;
129 background-color: rgba(255, 255, 255, 0.8);
130 border: none;
131 border-radius: 5px;
132 cursor: pointer;
133 font-weight: bold;
134 z-index: 10;
135 }
136
137 #controls {
138 position: absolute;
139 bottom: 20px;
140 left: 20px;
141 padding: 10px;
142 background-color: rgba(255, 255, 255, 0.8);
143 border-radius: 5px;
144 display: flex;
145 flex-direction: column;
146 gap: 10px;
147 z-index: 10;
148 }
149
150 .glow {
151 filter: drop-shadow(0 0 5px rgba(255, 255, 255, 0.5));
152 }
153
154 .horizontal-container {
155 display: flex;
156 flex-direction: column;
157 justify-content: center;
158 align-items: center;
159 height: 300px;
160 }
161
162 .sequence-container {
163 display: flex;
164 flex-direction: column;
165 justify-content: center;
166 align-items: center;
167 height: 300px;
168 }
169 </style>
170</head>
171<body>
172 <div id="container">
173 <div class="visualizer-container" id="main-visualizer">
174 <!-- Il contenuto verrà generato dinamicamente in base alle impostazioni -->
175 </div>
176 </div>
177
178 <button id="startButton">Inizia Visualizzazione</button>
179
180 <div id="controls">
181 <div>
182 <label for="mirrorMode">Modalità Visualizzazione:</label>
183 <select id="mirrorMode">
184 <option value="none">Nessuno</option>
185 <option value="vertical" selected>Verticale (Y) - ".zZ Zz."</option>
186 <option value="horizontal">Orizzontale (X) - ".zZ Zz."</option>
187 <option value="sequence">Sequenza (X) - orizzontale </option>
188 </select>
189 </div>
190 <div>
191 <label for="barColor">Colore barre:</label>
192 <select id="barColor">
193 <option value="default">Predefinito</option>
194 <option value="blue">Blu</option>
195 <option value="purple">Viola</option>
196 <option value="rainbow">Arcobaleno</option>
197 <option value="cyan">Ciano</option>
198 </select>
199 </div>
200 <div>
201 <label for="sensitivity">Sensibilità:</label>
202 <input type="range" id="sensitivity" min="1" max="10" value="5">
203 </div>
204 <div>
205 <label for="numBars">Numero barre:</label>
206 <input type="range" id="numBars" min="16" max="128" value="64">
207 </div>
208 <div>
209 <label for="bgColor">Colore sfondo:</label>
210 <select id="bgColor">
211 <option value="#00FF00">Verde (Default)</option>
212 <option value="#0000FF">Blu</option>
213 <option value="#FF00FF">Magenta</option>
214 </select>
215 </div>
216 <div>
217 <label for="glowEffect">Effetto luminoso:</label>
218 <input type="checkbox" id="glowEffect">
219 </div>
220 </div>
221
222 <script>
223 document.addEventListener('DOMContentLoaded', function() {
224 const mainVisualizer = document.getElementById('main-visualizer');
225 const startButton = document.getElementById('startButton');
226 const mirrorModeSelect = document.getElementById('mirrorMode');
227 const barColorSelect = document.getElementById('barColor');
228 const sensitivityInput = document.getElementById('sensitivity');
229 const numBarsInput = document.getElementById('numBars');
230 const bgColorSelect = document.getElementById('bgColor');
231 const glowEffectCheckbox = document.getElementById('glowEffect');
232
233 let audioContext;
234 let analyser;
235 let microphone;
236 let dataArray;
237 let currentMode = "vertical";
238 let allBars = {};
239
240 // Inizializza layout visualizzatore
241 function setupVisualizer(mode) {
242 mainVisualizer.innerHTML = '';
243 allBars = {};
244
245 switch(mode) {
246 case "none":
247 createSingleVisualizer();
248 break;
249 case "vertical":
250 createVerticalMirrorVisualizer();
251 break;
252 case "horizontal":
253 createHorizontalVisualizer();
254 break;
255 case "sequence":
256 createSequenceVisualizer();
257 break;
258 default:
259 createVerticalMirrorVisualizer();
260 }
261
262 currentMode = mode;
263 }
264
265 // Crea un singolo visualizzatore
266 function createSingleVisualizer() {
267 const visualizer = document.createElement('div');
268 visualizer.id = "visualizer-single";
269 visualizer.style.cssText = "display: flex; justify-content: space-between; align-items: flex-end; height: 300px; width: 600px;";
270 mainVisualizer.appendChild(visualizer);
271
272 allBars.single = [];
273
274 const numBars = parseInt(numBarsInput.value);
275 for (let i = 0; i < numBars; i++) {
276 const bar = document.createElement('div');
277 bar.className = 'bar-vertical';
278 visualizer.appendChild(bar);
279 allBars.single.push(bar);
280 }
281 }
282
283 // Crea visualizzatore con specchio verticale (Y) - Pattern ".zZ Zz."
284 function createVerticalMirrorVisualizer() {
285 // Label superiore
286 const labelTop = document.createElement('div');
287 labelTop.className = 'channel-label';
288 labelTop.textContent = 'CANALE SINISTRO';
289 mainVisualizer.appendChild(labelTop);
290
291 // Visualizzatore superiore
292 const visualizerTop = document.createElement('div');
293 visualizerTop.id = "visualizer-top";
294 mainVisualizer.appendChild(visualizerTop);
295
296 // Divisore
297 const divider = document.createElement('div');
298 divider.className = 'divider-horizontal';
299 mainVisualizer.appendChild(divider);
300
301 // Visualizzatore inferiore
302 const visualizerBottom = document.createElement('div');
303 visualizerBottom.id = "visualizer-bottom";
304 mainVisualizer.appendChild(visualizerBottom);
305
306 // Label inferiore
307 const labelBottom = document.createElement('div');
308 labelBottom.className = 'channel-label';
309 labelBottom.textContent = 'CANALE DESTRO';
310 mainVisualizer.appendChild(labelBottom);
311
312 allBars.top = [];
313 allBars.bottom = [];
314
315 const numBars = parseInt(numBarsInput.value);
316 for (let i = 0; i < numBars; i++) {
317 const barTop = document.createElement('div');
318 barTop.className = 'bar-vertical';
319 visualizerTop.appendChild(barTop);
320 allBars.top.push(barTop);
321
322 const barBottom = document.createElement('div');
323 barBottom.className = 'bar-vertical';
324 barBottom.style.borderRadius = '0 0 2px 2px';
325 visualizerBottom.appendChild(barBottom);
326 allBars.bottom.push(barBottom);
327 }
328 }
329
330 // Crea visualizzatore orizzontale (X) con pattern ".zZ Zz."
331 function createHorizontalVisualizer() {
332 const container = document.createElement('div');
333 container.className = 'horizontal-container';
334 mainVisualizer.appendChild(container);
335
336 // Label
337 const label = document.createElement('div');
338 label.className = 'channel-label';
339 label.textContent = 'VISUALIZZATORE ORIZZONTALE .zZ Zz.';
340 container.appendChild(label);
341
342 // Visualizzatore orizzontale con divisore centrale
343 const visualizerHorizontal = document.createElement('div');
344 visualizerHorizontal.id = "visualizer-horizontal";
345 visualizerHorizontal.style.cssText = "display: flex; position: relative; justify-content: center; align-items: center; height: 300px; width: 600px;";
346 container.appendChild(visualizerHorizontal);
347
348 // Divisore verticale centrale
349 const divider = document.createElement('div');
350 divider.className = 'divider-vertical';
351 divider.style.position = "absolute";
352 visualizerHorizontal.appendChild(divider);
353
354 // Contenitore per la metà sinistra
355 const leftContainer = document.createElement('div');
356 leftContainer.style.cssText = "display: flex; flex-direction: column; justify-content: center; align-items: flex-end; height: 100%; width: 300px;";
357 visualizerHorizontal.appendChild(leftContainer);
358
359 // Contenitore per la metà destra
360 const rightContainer = document.createElement('div');
361 rightContainer.style.cssText = "display: flex; flex-direction: column; justify-content: center; align-items: flex-start; height: 100%; width: 300px;";
362 visualizerHorizontal.appendChild(rightContainer);
363
364 allBars.left = [];
365 allBars.right = [];
366
367 const numBars = parseInt(numBarsInput.value) / 2; // Dividiamo per metà sinistra e destra
368
369 for (let i = 0; i < numBars; i++) {
370 // Barra sinistra (estende verso sinistra)
371 const barLeft = document.createElement('div');
372 barLeft.className = 'bar-horizontal-left';
373 leftContainer.appendChild(barLeft);
374 allBars.left.push(barLeft);
375
376 // Barra destra (estende verso destra)
377 const barRight = document.createElement('div');
378 barRight.className = 'bar-horizontal-right';
379 rightContainer.appendChild(barRight);
380 allBars.right.push(barRight);
381 }
382 }
383
384 // Crea visualizzatore con sequenza orizzontale (123456-654321)
385 function createSequenceVisualizer() {
386 const container = document.createElement('div');
387 container.className = 'sequence-container';
388 mainVisualizer.appendChild(container);
389
390 // Label
391 const label = document.createElement('div');
392 label.className = 'channel-label';
393 label.textContent = 'SEQUENZA ORIZZONTALE (123456-654321)';
394 container.appendChild(label);
395
396 // Visualizzatore sequenziale
397 const visualizerSequence = document.createElement('div');
398 visualizerSequence.id = "visualizer-sequence";
399 visualizerSequence.style.cssText = "display: flex; position: relative; justify-content: center; align-items: center; height: 300px; width: 600px;";
400 container.appendChild(visualizerSequence);
401
402 // Divisore centrale
403 const divider = document.createElement('div');
404 divider.className = 'divider-vertical';
405 divider.style.position = "absolute";
406 visualizerSequence.appendChild(divider);
407
408 // Contenitore per la metà sinistra (crescente)
409 const leftContainer = document.createElement('div');
410 leftContainer.style.cssText = "display: flex; justify-content: center; align-items: flex-end; height: 100%; width: 300px;";
411 visualizerSequence.appendChild(leftContainer);
412
413 // Contenitore per la metà destra (decrescente)
414 const rightContainer = document.createElement('div');
415 rightContainer.style.cssText = "display: flex; justify-content: center; align-items: flex-end; height: 100%; width: 300px;";
416 visualizerSequence.appendChild(rightContainer);
417
418 allBars.sequenceLeft = [];
419 allBars.sequenceRight = [];
420
421 const numBars = parseInt(numBarsInput.value) / 2; // Dividiamo per metà sinistra e destra
422
423 for (let i = 0; i < numBars; i++) {
424 // Barra sinistra (crescente 1-2-3...)
425 const barLeft = document.createElement('div');
426 barLeft.className = 'bar-sequence';
427 leftContainer.appendChild(barLeft);
428 allBars.sequenceLeft.push(barLeft);
429
430 // Barra destra (decrescente ...-3-2-1)
431 const barRight = document.createElement('div');
432 barRight.className = 'bar-sequence';
433 rightContainer.appendChild(barRight);
434 allBars.sequenceRight.push(barRight);
435 }
436 }
437
438 // Inizializza il visualizzatore audio
439 startButton.addEventListener('click', async function() {
440 try {
441 // Richiedi l'accesso al microfono
442 const stream = await navigator.mediaDevices.getUserMedia({ audio: true });
443
444 // Crea il contesto audio
445 audioContext = new (window.AudioContext || window.webkitAudioContext)();
446 analyser = audioContext.createAnalyser();
447 microphone = audioContext.createMediaStreamSource(stream);
448
449 // Configura l'analizzatore
450 microphone.connect(analyser);
451 analyser.fftSize = 256;
452 const bufferLength = analyser.frequencyBinCount;
453 dataArray = new Uint8Array(bufferLength);
454
455 // Avvia la visualizzazione
456 startButton.style.display = 'none';
457 visualize();
458 } catch (error) {
459 alert('Errore nell\'accesso al microfono: ' + error);
460 }
461 });
462
463 // Funzione di visualizzazione
464 function visualize() {
465 requestAnimationFrame(visualize);
466
467 if (!analyser) return;
468
469 analyser.getByteFrequencyData(dataArray);
470 const sensitivity = parseFloat(sensitivityInput.value) / 5;
471
472 switch(currentMode) {
473 case "none":
474 updateSingleVisualizer(sensitivity);
475 break;
476 case "vertical":
477 updateVerticalMirrorVisualizer(sensitivity);
478 break;
479 case "horizontal":
480 updateHorizontalVisualizer(sensitivity);
481 break;
482 case "sequence":
483 updateSequenceVisualizer(sensitivity);
484 break;
485 }
486 }
487
488 // Aggiorna visualizzatore singolo
489 function updateSingleVisualizer(sensitivity) {
490 const bars = allBars.single;
491 const numBars = bars.length;
492
493 for (let i = 0; i < numBars; i++) {
494 const index = Math.floor(i / numBars * dataArray.length);
495 const value = dataArray[index] * sensitivity;
496
497 bars[i].style.height = value + 'px';
498 updateBarColor(bars[i], value, i, 'vertical');
499 }
500 }
501
502 // Aggiorna visualizzatore con specchio verticale (Pattern ".zZ Zz.")
503 function updateVerticalMirrorVisualizer(sensitivity) {
504 const barsTop = allBars.top;
505 const barsBottom = allBars.bottom;
506 const numBars = barsTop.length;
507
508 for (let i = 0; i < numBars; i++) {
509 const index = Math.floor(i / numBars * dataArray.length);
510
511 // Calcola il valore di base
512 let baseValue = dataArray[index] * sensitivity;
513
514 // Applica il pattern ".zZ Zz." alternando valori più alti e più bassi
515 const positionFactor = Math.sin((i / numBars) * Math.PI * 2);
516 const modValue = 1 + (positionFactor * 0.3);
517
518 // Differenziazione stereo
519 const valueTop = baseValue * modValue * (i % 2 === 0 ? 1.1 : 0.9);
520 const valueBottom = baseValue * modValue * (i % 2 === 0 ? 0.9 : 1.1);
521
522 barsTop[i].style.height = valueTop + 'px';
523 barsBottom[i].style.height = valueBottom + 'px';
524
525 updateBarColor(barsTop[i], valueTop, i, 'vertical', true);
526 updateBarColor(barsBottom[i], valueBottom, i, 'vertical', false);
527 }
528 }
529
530 // Aggiorna visualizzatore orizzontale con pattern ".zZ Zz."
531 function updateHorizontalVisualizer(sensitivity) {
532 const barsLeft = allBars.left;
533 const barsRight = allBars.right;
534 const numBars = barsLeft.length;
535
536 for (let i = 0; i < numBars; i++) {
537 const index = Math.floor(i / numBars * dataArray.length);
538
539 // Valore base dall'audio
540 let baseValue = dataArray[index] * sensitivity;
541
542 // Applica il pattern ".zZ Zz." usando una funzione sinusoidale
543 const positionFactor = Math.sin((i / numBars) * Math.PI * 3); // Fattore di posizione per l'effetto ondulatorio
544 const modValue = 1 + (positionFactor * 0.3); // Modifica l'intensità dell'onda
545
546 // Applica un leggero effetto stereo (variazione casuale)
547 const valueLeft = baseValue * modValue * (i % 2 === 0 ? 1.1 : 0.9);
548 const valueRight = baseValue * modValue * (i % 2 === 0 ? 0.9 : 1.1);
549
550 // Impostiamo la larghezza delle barre
551 barsLeft[i].style.width = valueLeft + 'px';
552 barsRight[i].style.width = valueRight + 'px';
553
554 // Aggiorniamo i colori
555 updateBarColor(barsLeft[i], valueLeft, i, 'horizontal-left');
556 updateBarColor(barsRight[i], valueRight, i, 'horizontal-right');
557 }
558 }
559
560 // Aggiorna visualizzatore con sequenza orizzontale (123456-654321)
561 function updateSequenceVisualizer(sensitivity) {
562 const barsLeft = allBars.sequenceLeft;
563 const barsRight = allBars.sequenceRight;
564 const numBars = barsLeft.length;
565
566 for (let i = 0; i < numBars; i++) {
567 const index = Math.floor(i / numBars * dataArray.length);
568
569 // Valore base dall'audio
570 let baseValue = dataArray[index] * sensitivity;
571
572 // Creiamo l'effetto ascendente a sinistra (123456...)
573 // Più ci avviciniamo al centro, più l'altezza aumenta
574 const leftFactor = (i + 1) / numBars;
575 const valueLeft = baseValue * leftFactor * 1.5;
576
577 // Creiamo l'effetto discendente a destra (...654321)
578 // Più ci allontaniamo dal centro, più l'altezza diminuisce
579 const rightFactor = (numBars - i) / numBars;
580 const valueRight = baseValue * rightFactor * 1.5;
581
582 // Impostiamo l'altezza delle barre (entrambe crescono verticalmente)
583 barsLeft[i].style.height = valueLeft + 'px';
584 barsRight[(numBars - 1) - i].style.height = valueRight + 'px';
585
586 // Manteniamo la larghezza fissa
587 barsLeft[i].style.width = '5px';
588 barsRight[(numBars - 1) - i].style.width = '5px';
589
590 // Aggiorniamo i colori
591 updateBarColor(barsLeft[i], valueLeft, i, 'vertical');
592 updateBarColor(barsRight[(numBars - 1) - i], valueRight, (numBars - 1) - i, 'vertical');
593 }
594 }
595
596 // Aggiorna il colore delle barre
597 function updateBarColor(bar, value, index, orientation, isTopOrLeft = true) {
598 const colorStyle = barColorSelect.value;
599 let direction;
600
601 switch (orientation) {
602 case 'vertical':
603 direction = isTopOrLeft ? 'to top' : 'to bottom';
604 break;
605 case 'horizontal-left':
606 direction = 'to left';
607 break;
608 case 'horizontal-right':
609 direction = 'to right';
610 break;
611 default:
612 direction = 'to top';
613 }
614
615 switch (colorStyle) {
616 case 'blue':
617 bar.style.background = `linear-gradient(${direction}, #0033ff, #00aaff, #00ffff)`;
618 break;
619 case 'purple':
620 bar.style.background = `linear-gradient(${direction}, #6600ff, #aa00ff, #ff00ff)`;
621 break;
622 case 'cyan':
623 bar.style.background = `linear-gradient(${direction}, #00ffff, #00aaff, #0066ff)`;
624 break;
625 case 'rainbow':
626 const hue = (index / (bar.parentNode.children.length || 10) * 360) % 360;
627 bar.style.background = `hsl(${hue}, 100%, 50%)`;
628 break;
629 default:
630 bar.style.background = `linear-gradient(${direction}, #ff0055, #ff9500, #ffff00)`;
631 }
632
633 // Applica effetto luminoso se attivato
634 if (glowEffectCheckbox.checked) {
635 bar.classList.add('glow');
636 } else {
637 bar.classList.remove('glow');
638 }
639 }
640
641 // Gestione degli eventi per i controlli
642 mirrorModeSelect.addEventListener('change', function() {
643 setupVisualizer(this.value);
644 });
645
646 numBarsInput.addEventListener('change', function() {
647 setupVisualizer(mirrorModeSelect.value);
648 });
649
650 bgColorSelect.addEventListener('change', function() {
651 document.body.style.backgroundColor = this.value;
652 });
653
654 // Cambio colore delle barre (aggiornato nella funzione visualize)
655 barColorSelect.addEventListener('change', function() {
656 // Il colore viene aggiornato automaticamente nella funzione updateBarColor
657 });
658
659 // Inizializzazione
660 setupVisualizer(mirrorModeSelect.value);
661 });
662 </script>
663</body>
664</html>
Al momento questa è la versione che posso indicare in 0.7, funzionante ma ancora non completa, vorrei migliorare l'effetto di specchio orizzontale e implementare la possibilità di nascondere i menu opzionali.