Files
djaudiovisualizer/src/control.html
T
2026-07-31 13:06:35 +02:00

425 lines
22 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!DOCTYPE html>
<html lang="it">
<head>
<meta charset="UTF-8" />
<meta http-equiv="Content-Security-Policy"
content="default-src 'self'; img-src 'self' blob: data:; style-src 'self' 'unsafe-inline'; script-src 'self'" />
<title>DJ Visualizer — Controlli</title>
<link rel="stylesheet" href="style.css" />
</head>
<body class="control">
<header id="topbar">
<strong>🎛 DJ Visualizer</strong>
<span id="status"><span id="fps">–</span> · <span id="scene-name">—</span></span>
</header>
<nav id="tabs">
<button data-tab="audio" class="active">🎵 Audio</button>
<button data-tab="playlist">🎶 Playlist</button>
<button data-tab="effetti">🌀 Effetti</button>
<button data-tab="anim3d">🧊 Animazioni 3D</button>
<button data-tab="pad">🎹 Pad</button>
<button data-tab="immagini">🖼 Immagini</button>
<button data-tab="testo">🔤 Testo</button>
<button data-tab="registra">🔴 Registra</button>
<button data-tab="schermo">🖥 Schermo</button>
</nav>
<main id="content">
<!-- ============ AUDIO ============ -->
<div class="tab-panel active" data-tab="audio">
<section>
<label>Sorgente audio</label>
<button id="btn-play" class="wide" disabled>⏸ Pausa</button>
<label>Input live</label>
<select id="device-select"><option value="">— Input live (mic/line/BlackHole) —</option></select>
<button id="btn-use-input" class="wide">🎤 Usa input selezionato</button>
<label>Uscita audio (casse)</label>
<select id="output-select"><option value="">— Uscita predefinita —</option></select>
</section>
<section class="meters">
<label>Livelli</label>
<div class="meter"><span>BASS</span><div class="bar"><i id="m-bass"></i></div></div>
<div class="meter"><span>MID</span><div class="bar"><i id="m-mid"></i></div></div>
<div class="meter"><span>HIGH</span><div class="bar"><i id="m-treble"></i></div></div>
</section>
<section>
<label>Reattività <span id="gain-val">1.0×</span></label>
<input type="range" id="gain" min="0" max="3" step="0.05" value="1" />
<label>Velocità <span id="speed-val">1.0×</span></label>
<input type="range" id="speed" min="0" max="3" step="0.05" value="1" />
</section>
<section>
<label>Equalizzatore visual</label>
<label class="band band-bass">Bassi <span id="bass-gain-val">1.0×</span></label>
<input type="range" id="bass-gain" min="0" max="3" step="0.05" value="1" />
<label class="band band-mid">Medi <span id="mid-gain-val">1.0×</span></label>
<input type="range" id="mid-gain" min="0" max="3" step="0.05" value="1" />
<label class="band band-treble">Alti <span id="treble-gain-val">1.0×</span></label>
<input type="range" id="treble-gain" min="0" max="3" step="0.05" value="1" />
</section>
</div>
<!-- ============ PLAYLIST ============ -->
<div class="tab-panel" data-tab="playlist">
<section class="span2">
<label>Playlist <span id="playlist-count">0 brani</span></label>
<div class="row">
<button id="btn-add-tracks">➕ Aggiungi brani/video</button>
<button id="btn-add-interlude">✨ Intermezzo effetti</button>
<button id="btn-clear-playlist">🗑 Svuota</button>
<label class="chk"><input type="checkbox" id="repeat-playlist" /> Ripeti</label>
</div>
<div class="row">
<button id="btn-pl-save">💾 Salva playlist…</button>
<button id="btn-pl-load">📂 Carica playlist…</button>
</div>
<input type="file" id="tracks-input" accept="audio/*,video/*" multiple hidden />
<input type="file" id="scene-img-input" accept="image/*" hidden />
<ol id="playlist" class="playlist"></ol>
<div class="hint-mini">Clic sul <b>nome</b> = avvia/riavvia · ▶/⏸ pausa · ⠿ riordina · ⌨ tasto rapido · 🎬 <b>scena</b> · ✨ <b>Intermezzo</b> = solo-effetti tra i brani · sulla <b>forma d'onda</b>: clic/trascina = sposta la riproduzione, maniglie 🟢🔴 = inizio/fine</div>
</section>
<section class="span2">
<label>Resa video in playlist</label>
<select id="tvid-fit">
<option value="contain">Adatta (bordi neri)</option>
<option value="cover">Riempie schermo</option>
</select>
<label>Fusione</label>
<select id="tvid-blend">
<option value="normal">normale</option>
<option value="screen">screen</option>
<option value="overlay">overlay</option>
<option value="multiply">multiply</option>
<option value="lighten">lighten</option>
</select>
<label>Opacità <span id="tvid-op-val">100%</span></label>
<input type="range" id="tvid-op" min="0" max="100" step="1" value="100" />
<div class="hint-mini">Si applica al video riprodotto dalla playlist (per fonderlo con gli effetti).</div>
</section>
</div>
<!-- ============ EFFETTI ============ -->
<div class="tab-panel" data-tab="effetti">
<section class="span2" id="effects-section">
<label>Libreria effetti <span id="effects-count"></span></label>
<div class="row">
<button id="auto-vj" title="L'Auto VJ ascolta il brano (energia, BPM, drop) e cambia effetto da solo a tempo di musica">🤖 Auto VJ</button>
<select id="av-mode" title="Da quali famiglie pesca l'Auto VJ">
<option value="smart">🎩 Smart (segue il mood)</option>
<option value="all">🌐 Tutte le famiglie</option>
<option value="custom">🎯 Solo famiglie scelte…</option>
</select>
<select id="effect-family"><option value="-1">Tutte le famiglie</option></select>
<input type="text" id="effect-search" class="textfield" placeholder="Cerca effetto…" />
</div>
<div id="av-fams" class="av-fams hidden"></div>
<div id="effect-list" class="effect-list"></div>
</section>
<section class="span2" id="svg-section">
<label>Effetti SVG / immagini</label>
<select id="svg-select"><option value="-1">— Scegli una sagoma… —</option></select>
<button id="btn-svg-load" class="wide">📂 Carica un SVG/immagine…</button>
<input type="file" id="svg-input" accept="image/svg+xml,image/*" hidden />
<div class="hint-mini">Scegli una sagoma → diventa un effetto reattivo. Per cambiare colori/stile filtra la libreria su “SVG/Immagine”.</div>
</section>
<section class="span2" id="effect-seq-section">
<label>Sequenza effetti <span id="seq-count">0 in sequenza</span></label>
<div class="row">
<button id="btn-seq-add">➕ Aggiungi corrente</button>
<button id="btn-seq-clear">🗑 Svuota</button>
</div>
<div class="row">
<label class="chk"><input type="checkbox" id="seq-auto" /> Auto-cambio</label>
<label class="chk"><input type="checkbox" id="seq-beat" /> Sul beat</label>
<label class="chk"><input type="checkbox" id="seq-shuffle" /> Shuffle</label>
</div>
<label>Intervallo auto-cambio <span id="seq-interval-val">8.0s</span></label>
<input type="range" id="seq-interval" min="1" max="60" step="0.5" value="8" />
<ol id="effect-seq" class="playlist"></ol>
<div class="hint-mini">Clic in libreria = applica · ➕ = in sequenza · ⌨ tasto rapido · 1-9 = primi 9</div>
</section>
</div>
<!-- ============ PAD ============ -->
<!-- ============ ANIMAZIONI 3D ============ -->
<div class="tab-panel" data-tab="anim3d">
<section class="span2">
<label>Effetto 3D</label>
<div class="row">
<select id="glb-effect-fam"></select>
<select id="glb-effect-preset"></select>
</div>
<button id="glb-effect-go" class="wide">▶ Attiva sull'output</button>
<div class="hint-mini">Le famiglie 3D vivono qui (non più nella libreria Effetti):
“Modello 3D” balla da solo a tempo; le “Interattivo” usano la camera.</div>
</section>
<section class="span2">
<label>Modello 3D (GLB)</label>
<button id="btn-glb-load" class="wide">🧊 Carica un modello (.glb / .fbx)…</button>
<input type="file" id="glb-input" accept=".glb,.fbx" hidden />
<div class="hint-mini" id="glb-name">Il modello ruota e balla a tempo di musica nella famiglia “Modello 3D”;
con la camera lo comandi in “Interattivo Avatar”.</div>
<label>I miei modelli</label>
<div id="glb-models" class="glb-models"></div>
<label>Apertura braccia <span id="glb-spread-val">0°</span></label>
<input type="range" id="glb-spread" min="0" max="70" step="1" value="0" />
<div class="hint-mini">Se durante i balli le braccia trapassano il corpo (modelli più larghi
del manichino standard), aumenta finché sono libere. Salvato per ogni modello.</div>
<label>Sfondo</label>
<select id="glb-bg">
<option value="gradient">Gradiente</option>
<option value="ink">Fluido</option>
<option value="fire">Fluido Fuoco</option>
<option value="ring">Fluido Anello</option>
<option value="vortex">Fluido Vortici</option>
<option value="wave">Fluido Onda</option>
<!-- tutte le altre famiglie vengono aggiunte da control.js -->
</select>
</section>
<section class="span2">
<label>Repertorio del ballerino</label>
<div id="glb-anims" class="av-fams"></div>
<button id="btn-anim-load" class="wide">💃 Aggiungi animazioni (.fbx / .glb)…</button>
<input type="file" id="anim-input" accept=".fbx,.glb" multiple hidden />
<label>BPM ballerino <span id="glb-bpm-val">auto</span></label>
<div class="row led-row">
<input type="number" class="led-num" id="glb-bpm" min="0" max="220" step="1" value="0"
title="0 = automatico dal beat rilevato" />
<button id="glb-tap">👏 TAP</button>
</div>
<div class="hint-mini">Spunta le mosse che il regista può usare (nessuna spuntata = tutte).
Le animazioni extra arrivano da Mixamo: .glb diretti, .fbx convertiti al volo (serve Blender).
BPM: 0 = segue il beat; oppure scrivilo o battilo col TAP.</div>
</section>
</div>
<div class="tab-panel" data-tab="pad">
<section class="span2">
<label>Pad — 20 brani al volo (5×4, salvati automaticamente)</label>
<div class="row">
<button id="btn-pad-stop">⏹ Stop</button>
<button id="btn-pad-clear">🗑 Svuota tutti</button>
</div>
<label>Crossfade tra pad <span id="pad-xfade-val">0.0s</span></label>
<input type="range" id="pad-xfade" min="0" max="8" step="0.5" value="0" />
<div id="pad-grid" class="pad-grid"></div>
<input type="file" id="pad-input" accept="audio/*" hidden />
<div class="hint-mini">Clic su un pad vuoto (o trascina un brano) per assegnarlo · clic su un pad pieno per avviarlo · premendone un altro ferma quello in corso · <b>tasto destro</b> per ricaricare · ✕ per svuotare.</div>
</section>
</div>
<!-- ============ IMMAGINI ============ -->
<div class="tab-panel" data-tab="immagini">
<section class="span2">
<label>Video (loop VJ)</label>
<div class="row">
<button id="btn-video">🎞 Carica video…</button>
<button id="btn-video-toggle">⏯</button>
<button id="btn-video-clear">Rimuovi</button>
</div>
<input type="file" id="video-input" accept="video/*" hidden />
<label>Opacità <span id="video-op-val">100%</span></label>
<input type="range" id="video-op" min="0" max="100" step="1" value="100" />
<div class="row">
<select id="video-fit">
<option value="cover">Riempie schermo</option>
<option value="contain">Adatta (bordi)</option>
</select>
<select id="video-blend">
<option value="normal">normale</option>
<option value="screen">screen</option>
<option value="overlay">overlay</option>
<option value="multiply">multiply</option>
<option value="lighten">lighten</option>
</select>
</div>
<div class="hint-mini">Il video (muto, in loop) si fonde con i visual generati sotto. Per mostrarlo da solo: opacità 100% + fusione "normale".</div>
</section>
<section>
<label>Immagini overlay</label>
<div class="row">
<button id="btn-images">🖼 Carica…</button>
<button id="btn-images-clear">Pulisci</button>
</div>
<input type="file" id="image-input" accept="image/*" multiple hidden />
<div class="row">
<label class="chk"><input type="checkbox" id="img-slideshow" /> Slideshow</label>
<label class="chk"><input type="checkbox" id="img-beat" checked /> Pulsa beat</label>
</div>
<label>Dimensione <span id="img-size-val">70%</span></label>
<input type="range" id="img-size" min="10" max="100" step="1" value="70" />
<label>Durata slideshow <span id="img-interval-val">5.0s</span></label>
<input type="range" id="img-interval" min="1" max="20" step="0.5" value="5" />
<label>Fusione</label>
<select id="img-blend">
<option value="screen">screen (luminoso)</option>
<option value="normal">normale</option>
<option value="overlay">overlay</option>
<option value="multiply">multiply</option>
<option value="lighten">lighten</option>
</select>
</section>
<section class="logo-ctl" data-logo="0">
<label>Logo 1</label>
<div class="row">
<button class="logo-load" data-logo="0">🖼 Carica</button>
<button class="logo-clear" data-logo="0">Rimuovi</button>
</div>
<input type="file" class="logo-input" data-logo="0" accept="image/*" hidden />
<label>Orizzontale <span class="logo-x-val">50%</span></label>
<input type="range" class="logo-x" data-logo="0" min="0" max="100" step="0.5" value="50" />
<label>Verticale <span class="logo-y-val">50%</span></label>
<input type="range" class="logo-y" data-logo="0" min="0" max="100" step="0.5" value="50" />
<label>Dimensione <span class="logo-size-val">20%</span></label>
<input type="range" class="logo-size" data-logo="0" min="2" max="80" step="0.5" value="20" />
<label>Opacità <span class="logo-op-val">100%</span></label>
<input type="range" class="logo-op" data-logo="0" min="0" max="100" step="1" value="100" />
</section>
<section class="logo-ctl" data-logo="1">
<label>Logo 2</label>
<div class="row">
<button class="logo-load" data-logo="1">🖼 Carica</button>
<button class="logo-clear" data-logo="1">Rimuovi</button>
</div>
<input type="file" class="logo-input" data-logo="1" accept="image/*" hidden />
<label>Orizzontale <span class="logo-x-val">50%</span></label>
<input type="range" class="logo-x" data-logo="1" min="0" max="100" step="0.5" value="50" />
<label>Verticale <span class="logo-y-val">50%</span></label>
<input type="range" class="logo-y" data-logo="1" min="0" max="100" step="0.5" value="50" />
<label>Dimensione <span class="logo-size-val">20%</span></label>
<input type="range" class="logo-size" data-logo="1" min="2" max="80" step="0.5" value="20" />
<label>Opacità <span class="logo-op-val">100%</span></label>
<input type="range" class="logo-op" data-logo="1" min="0" max="100" step="1" value="100" />
</section>
</div>
<!-- ============ TESTO ============ -->
<div class="tab-panel" data-tab="testo">
<section class="span2">
<label>Testo scorrevole</label>
<textarea id="ticker-text" class="textfield textarea-lg" rows="4" placeholder="Scrivi qui il testo da far scorrere…&#10;Es. NOME DJ · TITOLO EVENTO · DATA"></textarea>
<div class="row">
<label class="chk"><input type="checkbox" id="ticker-on" /> Mostra</label>
<select id="ticker-pos">
<option value="bottom">Basso</option>
<option value="top">Alto</option>
<option value="middle">Centro</option>
</select>
</div>
<label>Direzione</label>
<select id="ticker-dir">
<option value="h">➡️ Orizzontale</option>
<option value="vup">⬆️ Verticale (su)</option>
<option value="vdown">⬇️ Verticale (giù)</option>
<option value="sides">↕️ Bordi laterali (caratteri impilati)</option>
</select>
<label>Velocità testo <span id="ticker-speed-val">1.0×</span></label>
<input type="range" id="ticker-speed" min="0.2" max="4" step="0.1" value="1" />
<label>Carattere</label>
<select id="ticker-font">
<option value="-apple-system, BlinkMacSystemFont, sans-serif">Sistema</option>
<option value="'Arial Black', Impact, sans-serif">Arial Black</option>
<option value="Impact, sans-serif">Impact</option>
<option value="Georgia, 'Times New Roman', serif">Georgia</option>
<option value="'Courier New', monospace">Monospace</option>
<option value="'Brush Script MT', 'Snell Roundhand', cursive">Corsivo</option>
<option value="Verdana, Geneva, sans-serif">Verdana</option>
<option value="'Trebuchet MS', sans-serif">Trebuchet</option>
</select>
<label>Grandezza <span id="ticker-size-val">6.0</span></label>
<input type="range" id="ticker-size" min="2" max="18" step="0.5" value="6" />
<div class="row">
<label class="chk"><input type="checkbox" id="ticker-bold" checked /> Grassetto</label>
<label class="chk">Colore <input type="color" id="ticker-color" value="#ffffff" /></label>
</div>
<label>Effetto movimento</label>
<select id="ticker-fx">
<option value="none">Nessuno</option>
<option value="updown">Su e giù</option>
<option value="wave">Onda</option>
<option value="zoom">Zoom / pulse</option>
<option value="flash">Flash</option>
<option value="rotate">Rotazione</option>
</select>
</section>
</div>
<!-- ============ REGISTRA ============ -->
<div class="tab-panel" data-tab="registra">
<section class="span2">
<label>Registrazione output → MP4</label>
<div class="rec-status">
<span id="rec-dot" class="off"></span>
<span id="rec-label">Pronto</span>
<span id="rec-time">00:00</span>
</div>
<label>Formato</label>
<select id="rec-aspect">
<option value="1920x1080">16:9 orizzontale (1920×1080)</option>
<option value="1080x1920">9:16 verticale (1080×1920)</option>
<option value="1080x1080">1:1 quadrato (1080×1080)</option>
<option value="1440x1080">4:3 (1440×1080)</option>
<option value="1080x1440">3:4 verticale (1080×1440)</option>
<option value="2560x1080">21:9 cinema (2560×1080)</option>
</select>
<label>Durata (Reel: si ferma da solo)</label>
<select id="rec-dur">
<option value="0">∞ manuale</option>
<option value="15000">⏱ 15 secondi</option>
<option value="30000">⏱ 30 secondi</option>
<option value="60000">⏱ 60 secondi</option>
<option value="90000">⏱ 90 secondi</option>
</select>
<button id="btn-rec" class="wide">🔴 Avvia registrazione</button>
<button id="btn-rec-folder" class="wide">📁 Apri cartella registrazioni</button>
<div class="hint-mini">Registra ciò che vedi sull'output + l'audio (file o input live). Salvataggio in <b>Filmati ▸ DJ Visualizer</b>. La conversione in MP4 avviene allo stop.</div>
</section>
</div>
<!-- ============ SCHERMO ============ -->
<div class="tab-panel" data-tab="schermo">
<section class="span2">
<label>Schermo di output</label>
<select id="display-select"></select>
<div class="row">
<button id="btn-display">📺 Manda al monitor</button>
<button id="btn-fullscreen">⛶ Fullscreen</button>
</div>
</section>
<section class="span2">
<label>Area LED (pixel mapping)</label>
<div class="row led-row">
<label class="led-chk"><input type="checkbox" id="led-on" /> Attiva</label>
<input type="number" class="led-num" id="led-w" min="16" max="8192" step="1" title="Larghezza in px" /> ×
<input type="number" class="led-num" id="led-h" min="16" max="8192" step="1" title="Altezza in px" /> px @
<input type="number" class="led-num" id="led-x" min="0" max="8192" step="1" title="Posizione X in px" /> ,
<input type="number" class="led-num" id="led-y" min="0" max="8192" step="1" title="Posizione Y in px" />
</div>
<div class="hint-mini">Per processori LED con mapping 1:1: confina tutto l'output in un rettangolo
Larghezza×Altezza alla posizione X,Y sullo schermo (nero attorno). Es. wall 896×512 → 896 × 512 @ 0 , 0.</div>
</section>
</div>
</main>
<footer id="hints">
<kbd>1</kbd>–<kbd>9</kbd> effetti in sequenza · <kbd>F</kbd> fullscreen · <kbd>O</kbd> aggiungi brani ·
<kbd>← →</kbd> immagini · <kbd>Spazio</kbd> play/pausa · tasti rapidi personalizzabili
</footer>
<script src="effects.js"></script>
<script src="control.js"></script>
</body>
</html>