/oscope/fx/<name> and /oscope/glitch handlers deleted from
OscClient.cpp — grep across sound_algo/, data_only_viz/,
launcher/, data_feeds/, web_realart/ (*.scd *.py *.swift
*.js *.html) found zero emitters.
OscClient::fx() now always returns its fallback; consumeGlitchPulse()
always returns false — same observable behaviour since these paths
were never populated at runtime.
Local build not verified: openFrameworks not symlinked on GrosMac
(../../../libs/openFrameworksCompiled missing). Change is a pure
handler removal with no logic side-effects.
Runtime port is 57123 (per bin/data/settings.json) and
all emitters (sound_algo bridge, data_feeds, data_only_viz,
launcher) target 57123. Several internal docs and the
default in ofApp.h still claimed 57122. Align them so the
default matches reality even if settings.json is missing.
Fixes:
- src/ofApp.h:267 default oscListenPort_
- src/OscClient.h:5 header comment
- README.md (3 occurrences, incl. settings.json sample)
- docs/OSC_PROTOCOL.md (5 occurrences incl. diagram)
Root CLAUDE.md (overview, where-to-look, conventions) plus
nested files for data_only_viz, oscope-of, launcher.
sound_algo already had its own nested tree.
Sized for the ~150-instruction budget: root 43 lines,
each nested 43-52 lines, telegraphic style.
- Implemented Hydra visualizations in `web_realart/public/hydra/app.js` and `index.html`.
- Created WebGL visualizer using Three.js and WebGPU in `web_realart/public/webgl/app.js` and `index.html`.
- Developed main landing page in `web_realart/public/index.html` to showcase different visualizations.
- Set up WebSocket server in `web_realart/server.js` to relay OSC messages to web clients.
- Added health check API endpoint for monitoring server status.
- Implemented new data feeds for Blitzortung, Bluesky, GCN, GitHub, Mempool, Netzfrequenz, OpenSky, RTE éCO2mix, SWPC, USGS.
- Introduced utility functions for rate limiting and hashing.
- Established OSC communication for data reception and processing.
- Created SuperCollider definitions for handling incoming data and generating audio output based on real-time data.
- Added configuration management for API credentials and polling intervals.
User : "SphereWave est bien audio reactive avec le hantek ?"
+ "et l'utiliser dans toutes les démos".
Verification + amélioration audio reactivity :
AudioAnalyzer expose désormais publicly :
- monoDown() : ring downsamplé 48 kHz audio (kFftSize floats)
- magDown() : magnitudes FFT 1024 (bin width ~47 Hz)
- monoHead() : index courant dans le ring
- audioSr() : 48000
VisFrame ajout pointeurs optionnels monoDown / magDown +
monoHead populated par ofApp::update.
SphereWaveVis::update : préfère désormais magDown/monoDown du
frame (FFT downsamplée 47Hz/bin → vraie résolution audio
20Hz-16kHz) au lieu de la FFT raw Hantek interne (7800Hz/bin
qui collait toute la bande audio dans 3 bins).
Fallback sur l'ancienne FFT interne si downsamples non dispos.
Ajout SphereWave dans 2 démos supplémentaires :
- Demo 7 AMBIENT VOID : nouvelle scène SPHERE
- Demo 14 GRAND FINAL : nouvelle scène SPHERE
User : "tu m'adapte pour un clavier mac azerty fr".
Sur Mac AZERTY FR, la rangée du haut sans shift donne
& é " ' ( § è ! ç à au lieu de 1234567890. Et les symboles
US ! @ # $ % (shift+1..5 QWERTY) sont injouables.
Adaptations :
- Démos 0-9 : digits 1-0 + alias chars AZERTY FR direct
1 / & → demo 0 AMIGA
2 / é (0xe9) → demo 1 C64
3 / " → demo 2 ACID
4 / ' → demo 3 TUNNEL
5 / ( → demo 4 FREQ
6 / § (0xa7) → demo 5 GLITCH
7 / è (0xe8) → demo 6 AMBIENT
8 / ! → demo 7 RAVE
9 / ç (0xe7) → demo 8 MEMORY
0 / à (0xe0) → demo 9 GREETINGS
- Démos 10-14 : F6-F10 (universels, layout-indépendants)
remplace les anciens ! @ # $ % qui étaient inaccessibles
ou conflictuels sur AZERTY FR Mac.
- Beat-sync toggle : * (US) ou \ (universel).
Hint clavier mis à jour pour refléter le double mapping.
User : "la WAVE faut me faire un version sphérique 3D
spectrogramme et waveforme" + "tu mets aussi windows 1.0
windows 3.11 etc... lotus 123 tout les OS possibles".
SphereWaveVis 3D :
- Mesh sphère paramétrique 64x32 (longitude x latitude)
- Pour chaque vertex : rayon = base + FFT[bin proportionnel
à longitude] + waveform[sample_idx]
- FFT 1024 sur (CH1+CH2)/2 Hantek raw, log mapping
- Camera orbitale autour de la sphère, light point pulsé
sur kick avec couleur audio-modulée
- Render solid + wireframe overlay phosphor
- Couleur cycle hue + saturation modulée par mid+treble
Nouveau BgKind::SphereWave (35 backgrounds total).
13 logos OS/software PNG dans bin/data/sprites/ :
- windows_1_0, windows_311, windows_95
- lotus_123, msdos
- atari_fuji, apple_rainbow
- amiga_wb, nextstep, beos, os2_warp, irix
- zx_spectrum, c64_basic
+ 5 SVG vectoriels source dans le même dossier.
User : "la demo sur la touche 1 et la touche 2 ont toujours
la même tracks audio" + "tu vérifie les chevauchement de texte".
Audit : 3 démos commençaient avec J (6/9/10), 2 avec T (7/11),
2 avec M (0/1). Maintenant 15 albums premiers actes uniques :
M-N-A-P-I-V-J-T-U-W-O-E-R-Q-L
Overlaps UI corrigés :
- Barre progression narrative y=70 chevauchait le titre 2.5x
qui s'étend à y~80. Déplacée à y=92.
- Hint narratif H-48 chevauchait hint FX H-50. Déplacé à H-78.
- Hint narratif élargi pour mentionner ! @ # $ % et q.
User : "azertyuiop = type de scene et qsdfghjklmwxcvbn,;:= pour
les paramètres".
Refonte complete du clavier en layout AZERTY :
[Top row AZERTYUIOP] : 10 backgrounds live override
a = BoingBall z = Tunnel e = Metaballs
r = Voronoi t = Twister y = KIFS
u = Fire i = Mode7 o = Octahedron
p = PlasmaC64
[Middle/bottom rows] : 17 paramètres FX
s = SPD speed d = KCK kick f = ROL roll
g = PAN pan h = CRV curve j = TLX tileX
k = TLZ tileZ l = DIR direction m = BLM bloom
w = CHR chroma x = KAL kaleido c = SCN scanlines
v = PIX pixelate b = HUE hue n = GRN grain
, = VIG vignette ; = SAT saturation
: = reset all (shift+. AZERTY)
[F-keys] : system toggles déplacés
F1 fullscreen F2 GUI F3 postFx
F4 autoglitch F5 reload shaders
[Flèches up/down] : ajuste valeur du paramètre selectionné
[Espace] glitch pulse [q/esc] sortie narrative + bg override
liveBgOverride_ ajouté dans ofApp : permet override du
background scope4 hors mode narratif via touche scène.
drawScope4 priorité : narrative > liveBgOverride > toggles.
User : "le mode live on améliore l'interface UX. les contrôles
s'appellent pour une touche de a à z et flèches up down pour
changer la valeur".
Refonte du contrôle FX live :
Avant : 19 lettres en pairs (a/z s/x d/c e/v t/n u/m i/l o/h
+ j/y combo + w reset)
Après : 1 lettre = sélectionne 1 paramètre, flèches haut/bas
ajustent la valeur du paramètre sélectionné, w reset.
Mappings mnémoniques (évite f g p k r b déjà bindés) :
s : Speed (tunnel) d : pan amplitude (Drift)
c : Crushing kick boost v : curVe amplitude
l : roLL amplitude x : tile X (angulaire)
z : tile Z (profondeur) n : directioN lerp
UP / DOWN : ×1.20 / ×0.83 sur le param sélectionné, clamp [0.05;12].
W : reset tous les multipliers à 1.0.
Overlay FX bas écran : encadré magenta autour du slot actif,
couleur saturée pour le selected, hint clavier ligne au-dessus
"[s c l d v x z n] select [UP DOWN] adjust [w] reset all".
Le pipeline FBO de transition causait un flip Y persistant
des textes (FBO rendu top-left vs gl_FragCoord bottom-left
en GL). Le flip Y dans les shaders corrigeait la transition
mais pas le rendu courant. Solution simple : on bypass le FBO
et on rend directement à l'écran via postfx comme avant. Les
transitions visuelles sont assurées par le flash blanc 0.45s
+ le glitch RGB postfx 0.45s déclenchés sur enterScene().
Les 3 shaders transitions/*.frag restent dans le repo pour
un futur fix architectural propre.
3 nouveaux modèles 3D intégrés aux narrations :
- Demo 1 AMIGA : ajout PYRAMIDE acte 6
- Demo 3 ACID : ajout DNA HELIX + ROSACE après VORTEX
User : "1 et 2 ont la même tracks par exemple" + "ajoute encore
plus de gfx et de modèle 3D wireframe".
Audit album usage : M utilisé 12 fois (très redondant), 8
albums (B C D E K N O W) jamais utilisés.
Rebalance albums dans 5 démos :
- Demo 1 AMIGA : M/M/M/M/G/G → M/R/G/B/C
- Demo 2 C64 : M/M/M/M/V → M/C/D/K/V
- Demo 3 ACID : A/A/A/H/H/J → A/H/D/H/A/J
- Demo 4 TUNL : P/P/P/F → P/S/E/F
- Demo 5 FREQ : I/I/I/Q/Q/Q → I/W/C/Q/B/Q
- Demo 7 VOID : J/J/S/S → J/O/S/S/W/J
Maintenant 23/23 albums utilisés au moins une fois.
6 nouveaux modèles 3D wireframe-friendly :
- gear.ply : pignon 12 dents
- cone.ply : cône simple
- pyramid.ply : pyramide 4 cotés
- rose3d.ply : rosace polaire k=5 tessellation tube
- geosphere.ply : icosphere subdivisée niveau 2
- dna.ply : double helix tube avec 2 brins
35 modèles 3D au total. ModelVis affiche déjà les wireframes
en overlay (ofSetColor 255,230,180,120 + drawWireframe).
Bug critique : tous les textes apparaissaient en miroir
vertical depuis l'introduction des transitions FBO. Cause :
gl_FragCoord est bottom-left en GL alors que les FBO oF
sont rendus top-left. Solution : flip Y dans les 3 shaders
de transition (crossfade/dissolve/wipe).
vec2 px = vec2(gl_FragCoord.x, uRes.y - gl_FragCoord.y);
AMIGA TRIBUTE demo : BOING BALL maintenant en premier acte
au lieu d'INSERT DISK, pour que l'utilisateur voie tout de
suite la boule rouge/blanche emblematique du 1984 Amiga.
Transitions FBO entre scenes des demoparties :
- 2 ofFbo ping-pong (sceneFbo_[0/1]) capturent la scene rendue
complete (post FX inclus) avant affichage
- Sur enterScene() : swap idx + reset transitionT a 0, choisit
une transition aleatoire parmi 3 (crossfade/dissolve/wipe)
- transitionDur 0.7s, progression smoothee
- Composite final via shader transition qui blend sceneFbo[prev]
vers sceneFbo[cur] selon progression uT
3 transition shaders :
- crossfade.frag : mix lineaire prev/cur
- dissolve.frag : noise hashe en blocs 6x6, smoothstep autour
du seuil pour un fade doux
- wipe.frag : wipe radial depuis le centre + anneau lumineux
a la frontiere
Beat-sync mode :
- Touche '*' toggle beatSyncEnabled
- Quand actif, launchDemo queue dans pendingDemoIdx au lieu
de fire immediatement
- update() check kick rising edge (>0.55, prev<=0.55) → fire
la demo en attente
- Permet de synchroniser les transitions au prochain kick
pour fluidifier la performance live
Pour ponctuer les changements d'acte des demoparties, ajout
d'une transition punchy a chaque enterScene() :
- transitionFlash_ = 1.0 au debut, decay lineaire 0.45s
- Flash blanc plein ecran, alpha = 180 * t^2 (ease-out cubique)
- postfx_.triggerGlitch(0.7, 0.45) en parallele = block
displacement RGB sur la meme duree
- Drawn entre la waveform circulaire et l'overlay narratif
pour ne pas masquer le titre d'acte qui suit immediatement
Remplacement des 10 single-presets par 10 démoparties multi-actes
narratives complètes. Chaque touche 1..9 + 0 lance une démo
scénarisée qui boucle sur ses propres scènes.
10 démos (3-4 scènes chacune, 1.5-2.5 min) :
1 AMIGA TRIBUTE INSERT DISK / SCROLLZ / COPPER STORM
2 C64 LOWLIFE BOOT / PIXEL / CLIMAX
3 ACID JOURNEY DROP IN / 303 LOOP / PEAK / COMEDOWN
4 TUNNEL VISION DARK / WALLS / OPENING
5 FREQUENCIES SILENCE / BANDS / FULL
6 GLITCH WORLD PROBE / CORRUPT / CRASH
7 AMBIENT VOID DRIFT / DEEPNESS / RETURN
8 RAVE BUILD / FULL POWER / AFTER
9 MEMORY LANE REWIND / VAPOR / FOG
0 GREETINGS OPENING / ROLL CALL / FINALE
Chaque acte change : toggles visuels, scroller style, narration
texte, track SC associée. Auto-advance à la durée écoulée + loop
sur acte 0 quand la démo finit.
Bindings :
1..9 + 0 lance la démo correspondante
Enter next act
Esc / q sortie en mode live (pas de scénario)
Overlay : ligne 1 "DEMO N / 10 NAME" petit, ligne 2 "ACT N / M
TITLE" gros + barre de progression.
User : "tu me fait un vrais démo partie avec une narration et
des évolutions".
Mode narratif scripté — touche 'b' lance une démoparty de
~3.5 min en 6 actes qui évoluent visuellement et musicalement :
ACT 1 INTRO 25s starfield + neon scroll + ambient
ACT 2 FIRST PULSE 35s tunnel only + classic + industrial
ACT 3 FREQUENCIES 45s polar+spectro + wavy3D + detroit
ACT 4 FULL POWER 55s everything + rainbow + gabber
ACT 5 GLITCH 40s everything + glitch fx + glitch idm
ACT 6 OUTRO 30s starfield + mirror + vocal trance
Chaque acte :
- Applique un preset toggles (visuels)
- Change le style scroller
- Override le texte du scroller avec une narration scénarisée
- Lance l'album SC associé via OSC /control/playAlbum
- Avance auto au temps écoulé, fade in/out du titre
DemoFx::setText override le texte du scroller avec restauration
auto vers greetings.txt en sortie de mode narratif.
Bindings :
b lance la démo party narrative (ACT 1)
Enter skip à la scène suivante
Esc sortie du mode narratif
Overlay : titre "ACT N / 6 NAME" en gros + barre de progression
en haut, hint clavier en bas.
User : "les touches 0 à 10 lanceront une démo party avec la
track supercollider associée + plus de styles de scroller +
q reste le mode live".
DemoFx::ScrollerStyle (6 variants) :
- Classic sine wave wobble par défaut
- Wavy3D sine + scale per char (effet respiration 3D)
- Rainbow hue cycle large par char
- Mirror classic + reflet vertical en bas (alpha gradient)
- Glitch jitter horizontal + couleurs cassées (rouge/bleu)
- Neon outline cyan large + cœur magenta fin
ofApp::DemoPreset (10 presets) :
1 AMIGA classic -> album A (acid_journey)
2 C64 wavy3d -> album M (chiptune_madness)
3 OLDSKOOL mirror -> album G (detroit_soulful)
4 NEWSKOOL rainbow -> album L (future_garage)
5 TUNNEL classic -> album P (industrial)
6 STARFIELD neon -> album J (ambient_cinematic)
7 SCOPE classic -> album I (liquid_dnb)
8 CYBER glitch -> album V (glitch_idm)
9 VAPOR mirror -> album U (vocal_trance)
0 RAVE rainbow -> album T (hardcore_gabber)
Chaque preset = état toggles ScopeToggles + style scroller +
lettre album SC (envoyée via /control/playAlbum à sclang).
Touches 1..9 + 0 lancent les presets (au lieu de toggles
individuels). 'q' reste libre pour le mode live (contrôle
manuel des éléments via les autres touches a-z).
Overlay bas droite affiche le preset actif en gros (magenta).
User : "scope4 uniquement et les touches 0 à 9 sont différents
effets type demoscene".
- Touches 1..9 toggle chacune un effet du Scope4 :
1 tunnel 3D
2 starfield (remplace tunnel)
3 sine scroller (greetings.txt)
4 copper bars
5 logo bobs
6 HUD aléatoire sub-10Hz
7 spectro circular ring
8 polar central
9 waveform circulaire CRT
0 = reset all toggles aux défauts.
- Suppression des bindings 1-9/0/-/q qui changeaient de mode_,
Scope4 est désormais le seul rendu actif.
- Suppression des toggles symboles redondants ' ; , .
- Synchro DemoFx::*Enabled() avec scope4_ flags dans drawScope4.
Nouveau module DemoFx avec 4 effets demoscene classiques :
- Sine scroller : texte depuis bin/data/greetings.txt, scale 3x,
bobbing sin par char (amp 14px), couleurs phosphor cyclant.
~150 px/s, wrap automatique. Affiché en bas d'écran par-dessus
tout dans Scope4.
- Starfield 3D : 220 étoiles z-projetées, motion blur en streak
radial, peut REMPLACER le tunnel quand toggle actif.
- Copper bars : 6 bandes horizontales en haut, gradient sin
vertical, hue cyclant, blend additif, follow sin de la phase.
- Logo bobs : "AV-LIVE" 4x scale qui rebondit en arc de cercle,
scale wobble, hue cycle. En haut centré.
Toggles clavier (touches symboles hors zone FX a-z) :
' scroller on/off
; copper bars on/off
, bobs on/off
. starfield on/off (remplace le tunnel quand on)
bin/data/greetings.txt fourni avec contenu par défaut, éditable
à la volée — DemoFx::setup le lit au boot.
Overlay phosphor vert toujours visible dans Scope4, sans
dépendre du GUI (touche g). 8 slots horizontaux en bas :
label / touches a→z / valeur multiplicateur / barre log.
Couleurs des slots : vert calme à 1.0, vire ambre quand >1.0
(boost), vire bleu quand <1.0 (atténuation). Barre log
centrée sur 1.0, marqueur central tick. "[w] reset" à droite.
Blend additif pour s'intégrer au look CRT rétro de la wave
circulaire au-dessus.
User : "rendre le bias kick plus violent. ajuster la vitesse du
tunnel en /10 et ajouter des fx qui modifie les multiplicateur
avec le clavier (a→z)".
Approach:
- Vitesse base /10 : (0.6 + bpm*0.012) → (0.06 + bpm*0.0012).
Le flow général est lent, le kick frappe d'autant plus fort.
- Kick punch ×4 (vs ×1.5) sur travel, ×1.4 (vs 0.6) sur panY,
×0.6 (vs 0.25) sur la composante roll. Sensation d'impact.
- TunnelVis::Mults — struct de 8 multiplicateurs (speed,
kickBoost, rollAmp, panAmp, curveAmp, tileX, tileZ, dirLerp).
Appliqués in-place dans update/draw.
- Bindings clavier ofApp::keyPressed : 9 paires down/up
a/z : speed
s/x : kickBoost
d/c : rollAmp
e/v : panAmp
t/n : curveAmp
u/m : tileX
i/l : tileZ
o/h : dirLerp
j/y : speed + kickBoost combinés (tout ralentir/accélérer)
w : reset all to 1.0
Chaque pression × 0.83 (down) ou × 1.20 (up), clamp 0.05..12.
User : "sur les carraud du tunnel tu me fait un HUD aléatoire
sur les fréquence sous les 10Hz".
Approach:
- ofApp::drawTunnelHud — pool jusqu'à 14 items, refresh 1-2 par
frame toutes les 0.15-0.40s, life 0.8-2.5s par item.
- Chaque item = mini-cadre rectangulaire phosphor + label texte
(LFO/DC/DRIFT/SUB/MOD/Δf/PHASE/GAIN/BIAS/TRIG/PWM/ENV) +
valeur Hz aléatoire 0.2..9.9 Hz à 1 décimale.
- Position : périphérique (rad 0.34-0.49 du centre) → tombe sur
les "carreaux" visibles du tunnel, évite le polar central.
- Fade in 0.2s + fade out 0.4s + flicker scintillant phosphor.
- Blend additif → s'intègre au look CRT sans masquer les couches.
- Appelé dans drawScope4 juste après le tunnel.
User : "Waveform circular fullscreen en premier plan et avec un
effet oscilloscope rétro".
Approach:
- WaveformVis::drawCircular passe en blend additif intégral
(phosphor look). 5 passes glow par ring (vs 3) + un trait fin
"beam" central surluminescent par-dessus.
- Vignette CRT interne : 8 cercles concentriques verts phosphor
d'alpha décroissant entre baseR et maxR.
- Timeline ticks : 60 graduations (12 majeures à 30°, 48 mineures
à 6°), comme un cadran d'oscilloscope analogique.
- Couleurs ravivées : CH1 (80,255,160) vert phosphor saturé, CH2
(255,210,80) ambre vintage.
- Scope4 : ordre des couches inversé. Tunnel → polar → spectro
ring → lissajous glow → WAVEFORM circulaire EN PREMIER PLAN.
User: "WAVE fait la circulaire sur la timline et que ça prenne
tout l'écran" + "polar doit rester dans le centre du tunnel".
Approach:
- WaveformVis::drawCircular(cx, cy, baseR, maxR) : timeline mappée
à l'angle (i/N → 0..2π depuis le haut), CH1 et CH2 sont des
anneaux concentriques avec amplitude radiale, slow trace en
arrière-plan plus large. Glow phosphor 3 passes par anneau.
- WaveformVis::draw délègue à drawCircular si circular_ = true.
- Scope4 : waveform circular fullscreen (toute l'écran), polar
centré 55% par-dessus, spectro ring 0.27-0.36 autour du polar,
lissajous additif fullscreen pour le glow. Tunnel intact en
fond — son centre cinématique (uPan/uCurve) bouge l'intérieur
sans déplacer le cadre, le polar reste pile au milieu.
- Reset circular_ à false en sortie de drawScope4 pour ne pas
affecter le mode Waveform standard (touche 5).
User : "avec une curve sur la translation du tunnel pour avoir
un effet de tournant".
Approach:
- Nouveaux uniformes shader uCurveAmp + uCurvePhase.
- Avant le calcul polar (r, a), on estime d0 = 1/length(p) puis
on applique un offset 2D vec2(sin(d0*0.45 + phase), cos(d0*0.35
+ phase*0.7)) * curveAmp. L'offset GRANDIT avec la profondeur
(d0 plus grand au loin), donc le centre du tunnel s'écarte
progressivement → sensation de virage.
- curvePhase_ avance avec dt × (1.2 + bpm × 0.005) × direction —
la phase défile en même temps qu'on progresse, le virage suit.
- curveAmp_ = 0.04 (floor) + |lead-bass|*0.10 + bass*0.12 + sin
lent. Toujours un léger virage, et plus de variations selon
les bandes.
User feedback : "le centre du tunnel doit être vraiment mouvent.
le polar doit petre centré"
Approach:
- TunnelVis : composante kinétique de base toujours active
(Lissajous lente sur le pan + sin sur le roll) qui fonctionne
même sans audio. Amplitudes max ~0.55 pour le pan (vs 0.15
avant) → le vanishing point bouge franchement, pas un drift
imperceptible. Audio s'ajoute par-dessus.
- Roll de base sin(t*0.5)*0.35 + composante (lead-bass)*1.2
+ 0.25*kick. La nef s'incline vraiment.
- Scope4 layout : polar agrandi à 70% minSide (vs 55%), spectro
ring repoussé à 0.32-0.44, satellites à 0.54 minSide pour
être hors du ring. Plus aucun chevauchement.
Les visualizers ne dépendaient que de l'OSC depuis sound_algo
pour les bandes (kick/bass/lead/snare). User veut que ce soit
le signal qui passe physiquement par le scope qui pilote le
rendu.
Approach:
- Nouveau AudioAnalyzer qui downsample le ring Hantek (1-48
MS/s) vers ~48 kHz audio, fenêtre Hann, FFT 1024.
- Bandes en log-power normalisé (-60dB→0, 0dB→1) :
bass 20-200 Hz / lowMid 200-800 / mid 800-3200 /
treble 3200-16000.
- Détecteurs de transitoire (kick = montée bass, snare =
montée mid+treble) avec décay 0.85.
- Exposé via VisFrame::bands. TunnelVis et PolarVis lisent
désormais frame.bands.* au lieu de frame.osc.amp(*).
- bpm + pad restent OSC (timing métadonnées non déductibles).