lushapp 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +104 -0
- package/bin/lush +62 -0
- package/bin/lush.js +48 -0
- package/package.json +61 -0
- package/pyproject.toml +52 -0
- package/scripts/postinstall.js +58 -0
- package/scripts/sync-version.js +38 -0
- package/setup.py +23 -0
- package/src/lush/__init__.py +30 -0
- package/src/lush/__main__.py +42 -0
- package/src/lush/audio.py +222 -0
- package/src/lush/cava.py +665 -0
- package/src/lush/constants.py +686 -0
- package/src/lush/data/cava.conf +19 -0
- package/src/lush/data/stations.json +3586 -0
- package/src/lush/modals.py +1382 -0
- package/src/lush/net.py +27 -0
- package/src/lush/notify.py +154 -0
- package/src/lush/search.py +185 -0
- package/src/lush/state.py +550 -0
- package/src/lush/stats.py +555 -0
- package/src/lush/ui.py +690 -0
- package/src/lush/ui_helpers.py +569 -0
|
@@ -0,0 +1,569 @@
|
|
|
1
|
+
# LUSH - UI Helpers and Theme Color Initializer
|
|
2
|
+
import curses
|
|
3
|
+
from .constants import SHIMMER_PALETTES
|
|
4
|
+
|
|
5
|
+
def get_source_tag(url: str) -> str:
|
|
6
|
+
u = url.lower()
|
|
7
|
+
if 'youtube.com' in u or 'youtu.be' in u:
|
|
8
|
+
return 'YT'
|
|
9
|
+
elif 'somafm.com' in u:
|
|
10
|
+
return 'SOMA'
|
|
11
|
+
elif 'nightride.fm' in u:
|
|
12
|
+
return 'NR'
|
|
13
|
+
elif 'radioparadise.com' in u:
|
|
14
|
+
return 'RP'
|
|
15
|
+
elif 'ice' in u or 'streamafrica' in u or 'zeno.fm' in u or 'radioart' in u:
|
|
16
|
+
return 'ICE'
|
|
17
|
+
elif 'aac' in u:
|
|
18
|
+
return 'AAC'
|
|
19
|
+
return 'STREAM'
|
|
20
|
+
|
|
21
|
+
def trim(text: str, width: int) -> str:
|
|
22
|
+
if len(text) <= width: return text
|
|
23
|
+
if width <= 3: return text[:width]
|
|
24
|
+
return text[:width-3] + "..."
|
|
25
|
+
|
|
26
|
+
def apply_theme(theme_name: str, themes: dict):
|
|
27
|
+
if not curses.has_colors():
|
|
28
|
+
return
|
|
29
|
+
t = themes.get(theme_name, themes.get("Plur1bus", list(themes.values())[0]))
|
|
30
|
+
is_256 = curses.COLORS >= 256
|
|
31
|
+
|
|
32
|
+
fallback_map = {
|
|
33
|
+
"Plur1bus": {"header": (curses.COLOR_BLACK, curses.COLOR_CYAN), "accent": curses.COLOR_CYAN, "footer": (curses.COLOR_BLACK, curses.COLOR_CYAN), "track": curses.COLOR_CYAN, "playing": curses.COLOR_GREEN, "highlight": (curses.COLOR_BLACK, curses.COLOR_CYAN), "genre": curses.COLOR_CYAN, "border": curses.COLOR_CYAN, "sub": curses.COLOR_WHITE},
|
|
34
|
+
"Tokyo Night": {"header": (curses.COLOR_BLACK, curses.COLOR_BLUE), "accent": curses.COLOR_MAGENTA, "footer": (curses.COLOR_BLACK, curses.COLOR_BLUE), "track": curses.COLOR_CYAN, "playing": curses.COLOR_GREEN, "highlight": (curses.COLOR_BLACK, curses.COLOR_MAGENTA), "genre": curses.COLOR_BLUE, "border": curses.COLOR_BLUE, "sub": curses.COLOR_WHITE},
|
|
35
|
+
"Synthwave": {"header": (curses.COLOR_BLACK, curses.COLOR_MAGENTA), "accent": curses.COLOR_MAGENTA, "footer": (curses.COLOR_BLACK, curses.COLOR_MAGENTA), "track": curses.COLOR_YELLOW, "playing": curses.COLOR_CYAN, "highlight": (curses.COLOR_BLACK, curses.COLOR_MAGENTA), "genre": curses.COLOR_YELLOW, "border": curses.COLOR_MAGENTA, "sub": curses.COLOR_WHITE},
|
|
36
|
+
"Matrix": {"header": (curses.COLOR_BLACK, curses.COLOR_GREEN), "accent": curses.COLOR_GREEN, "footer": (curses.COLOR_BLACK, curses.COLOR_GREEN), "track": curses.COLOR_GREEN, "playing": curses.COLOR_GREEN, "highlight": (curses.COLOR_BLACK, curses.COLOR_GREEN), "genre": curses.COLOR_GREEN, "border": curses.COLOR_GREEN, "sub": curses.COLOR_WHITE},
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
if is_256:
|
|
40
|
+
h_fg, h_bg = t.get("header", [0, 45])
|
|
41
|
+
accent = t.get("accent", 45)
|
|
42
|
+
f_fg, f_bg = t.get("footer", [0, 45])
|
|
43
|
+
track = t.get("track", 51)
|
|
44
|
+
playing = t.get("playing", 48)
|
|
45
|
+
hl_fg, hl_bg = t.get("highlight", [0, 45])
|
|
46
|
+
genre = t.get("genre", 38)
|
|
47
|
+
border = t.get("border", 45)
|
|
48
|
+
sub = t.get("sub", 245)
|
|
49
|
+
else:
|
|
50
|
+
fb = fallback_map.get(theme_name, fallback_map["Plur1bus"])
|
|
51
|
+
h_fg, h_bg = fb["header"]
|
|
52
|
+
accent = fb["accent"]
|
|
53
|
+
f_fg, f_bg = fb["footer"]
|
|
54
|
+
track = fb["track"]
|
|
55
|
+
playing = fb["playing"]
|
|
56
|
+
hl_fg, hl_bg = fb["highlight"]
|
|
57
|
+
genre = fb["genre"]
|
|
58
|
+
border = fb["border"]
|
|
59
|
+
sub = fb["sub"]
|
|
60
|
+
|
|
61
|
+
curses.init_pair(1, h_fg, h_bg) # Header / Solid Box
|
|
62
|
+
curses.init_pair(2, accent, -1) # Neon Accent text (Cyan)
|
|
63
|
+
curses.init_pair(3, f_fg, f_bg) # Hotkey Bar (Reverse)
|
|
64
|
+
curses.init_pair(4, track, -1) # Track text / Light Cyan
|
|
65
|
+
curses.init_pair(5, playing, -1) # Playing status (Bright Green)
|
|
66
|
+
curses.init_pair(6, hl_fg, hl_bg) # Active selection highlight
|
|
67
|
+
curses.init_pair(7, genre, -1) # Genre / Muted Info
|
|
68
|
+
curses.init_pair(8, border, -1) # Cyan Borders & Frames
|
|
69
|
+
curses.init_pair(9, sub, -1) # Subtitle & count details
|
|
70
|
+
|
|
71
|
+
# Shimmer gradient color pairs 10 - 14
|
|
72
|
+
if is_256:
|
|
73
|
+
shimmer_cols = SHIMMER_PALETTES.get(theme_name, SHIMMER_PALETTES["Plur1bus"])
|
|
74
|
+
curses.init_pair(10, shimmer_cols[0], -1) # Peak White Hot Sparkle
|
|
75
|
+
curses.init_pair(11, shimmer_cols[1], -1) # Sheen Highlight
|
|
76
|
+
curses.init_pair(12, shimmer_cols[2], -1) # Neon Glow
|
|
77
|
+
curses.init_pair(13, shimmer_cols[3], -1) # Mid Glow
|
|
78
|
+
curses.init_pair(14, shimmer_cols[4], -1) # Base Tone
|
|
79
|
+
curses.init_pair(15, 196, -1) # Glowing Red (REC indicator)
|
|
80
|
+
else:
|
|
81
|
+
curses.init_pair(10, curses.COLOR_WHITE, -1)
|
|
82
|
+
curses.init_pair(11, curses.COLOR_WHITE, -1)
|
|
83
|
+
curses.init_pair(12, curses.COLOR_CYAN, -1)
|
|
84
|
+
curses.init_pair(13, curses.COLOR_CYAN, -1)
|
|
85
|
+
curses.init_pair(14, curses.COLOR_CYAN, -1)
|
|
86
|
+
curses.init_pair(15, curses.COLOR_RED, -1)
|
|
87
|
+
|
|
88
|
+
import math
|
|
89
|
+
|
|
90
|
+
def get_shimmer_color_pair(effect: str, x_norm: float, c_idx: int, row_i: int, t_now: float, cava_bars=None, is_playing=True) -> int:
|
|
91
|
+
if effect == "off":
|
|
92
|
+
return 14 # Base Theme Tone (Static)
|
|
93
|
+
|
|
94
|
+
if not is_playing or not cava_bars or all(b == 0 for b in cava_bars):
|
|
95
|
+
# Subtle ambient breathing glow when paused or silent
|
|
96
|
+
idle_phase = math.sin(t_now * 1.5 + x_norm * 3.14)
|
|
97
|
+
if idle_phase > 0.8: return 12
|
|
98
|
+
elif idle_phase > 0.4: return 13
|
|
99
|
+
return 14
|
|
100
|
+
|
|
101
|
+
# 1. CAVA Beat Reactive Flare
|
|
102
|
+
if effect == "cava_sync":
|
|
103
|
+
energy = sum(cava_bars) / max(1, len(cava_bars) * 8.0)
|
|
104
|
+
bass = max(cava_bars[:4]) if len(cava_bars) >= 4 else 0
|
|
105
|
+
speed = 0.8 + energy * 2.4
|
|
106
|
+
wave_pos = ((t_now * speed) % 2.2) - 0.4
|
|
107
|
+
dist = abs(x_norm - wave_pos)
|
|
108
|
+
|
|
109
|
+
bin_idx = min(len(cava_bars)-1, int(x_norm * len(cava_bars)))
|
|
110
|
+
bin_val = cava_bars[bin_idx]
|
|
111
|
+
|
|
112
|
+
wave_glow = max(0.0, 0.55 - dist * 2.2)
|
|
113
|
+
bass_kick = 0.40 if (bass >= 7 and dist < 0.28) else 0.0
|
|
114
|
+
total_intensity = (bin_val / 8.0) * 0.5 + wave_glow * 0.4 + bass_kick
|
|
115
|
+
|
|
116
|
+
if total_intensity > 0.80 or (bass >= 7 and dist < 0.15):
|
|
117
|
+
return 10
|
|
118
|
+
elif total_intensity > 0.60:
|
|
119
|
+
return 11
|
|
120
|
+
elif total_intensity > 0.40:
|
|
121
|
+
return 12
|
|
122
|
+
elif total_intensity > 0.20:
|
|
123
|
+
return 13
|
|
124
|
+
return 14
|
|
125
|
+
|
|
126
|
+
# NEW: CAVA Hyper-Fluid Plasma (Sub-MS Chroma Flow)
|
|
127
|
+
elif effect == "cava_hyper_fluid_plasma":
|
|
128
|
+
bass_e = sum(cava_bars[:5]) / 40.0 if len(cava_bars) >= 5 else 0
|
|
129
|
+
mid_e = sum(cava_bars[5:15]) / 80.0 if len(cava_bars) >= 15 else 0
|
|
130
|
+
high_e = sum(cava_bars[15:]) / max(1, len(cava_bars[15:]) * 8.0) if len(cava_bars) >= 16 else 0
|
|
131
|
+
v1 = math.sin(x_norm * 8.0 + t_now * (2.5 + bass_e * 4.0) + row_i * 2.0)
|
|
132
|
+
v2 = math.cos(x_norm * 12.0 - t_now * (3.0 + mid_e * 5.0) + c_idx * 0.3)
|
|
133
|
+
plasma = (v1 + v2) * 0.5
|
|
134
|
+
intensity = (plasma + 1.0) * 0.4 + (bass_e * 0.35) + (high_e * 0.25)
|
|
135
|
+
if intensity > 0.82: return 10
|
|
136
|
+
elif intensity > 0.62: return 11
|
|
137
|
+
elif intensity > 0.42: return 12
|
|
138
|
+
elif intensity > 0.20: return 13
|
|
139
|
+
return 14
|
|
140
|
+
|
|
141
|
+
# NEW: CAVA Ballistic Shockwave (Transient Impact Rings)
|
|
142
|
+
elif effect == "cava_ballistic_shockwave":
|
|
143
|
+
kick = max(cava_bars[:3]) / 8.0 if cava_bars else 0
|
|
144
|
+
speed = 2.0 + kick * 4.0
|
|
145
|
+
center_dist = abs(x_norm - 0.5) * 2.0
|
|
146
|
+
wave_phase = (t_now * speed) % 1.3
|
|
147
|
+
ring = max(0.0, 1.0 - abs(center_dist - wave_phase) * 5.0) * (0.4 + kick * 0.6)
|
|
148
|
+
if ring > 0.78: return 10
|
|
149
|
+
elif ring > 0.55: return 11
|
|
150
|
+
elif ring > 0.32: return 12
|
|
151
|
+
elif ring > 0.15: return 13
|
|
152
|
+
return 14
|
|
153
|
+
|
|
154
|
+
# NEW: CAVA Quantum Flux (Sub-Atomic Wavefunction)
|
|
155
|
+
elif effect == "cava_quantum_flux":
|
|
156
|
+
vocal = sum(cava_bars[4:12]) / 64.0 if len(cava_bars) >= 12 else 0
|
|
157
|
+
k1 = 14.0 + vocal * 10.0
|
|
158
|
+
psi = math.sin(x_norm * k1 - t_now * 6.0) * math.cos(row_i * 3.14 + t_now * (4.0 + vocal * 8.0))
|
|
159
|
+
prob = (psi * psi) * (0.5 + vocal * 0.5)
|
|
160
|
+
if prob > 0.75: return 10
|
|
161
|
+
elif prob > 0.50: return 11
|
|
162
|
+
elif prob > 0.28: return 12
|
|
163
|
+
elif prob > 0.12: return 13
|
|
164
|
+
return 14
|
|
165
|
+
|
|
166
|
+
# NEW: CAVA Ballistic VU Sweep (Studio Dual-Needle)
|
|
167
|
+
elif effect == "cava_analog_vu_sweep":
|
|
168
|
+
rms = sum(cava_bars) / max(1, len(cava_bars) * 8.0)
|
|
169
|
+
needle_pos = min(1.0, rms * 1.35)
|
|
170
|
+
dist = abs(x_norm - needle_pos)
|
|
171
|
+
needle_glow = max(0.0, 1.0 - dist * 4.5)
|
|
172
|
+
if needle_glow > 0.80: return 10
|
|
173
|
+
elif needle_glow > 0.55: return 11
|
|
174
|
+
elif needle_glow > 0.30: return 12
|
|
175
|
+
elif needle_glow > 0.12: return 13
|
|
176
|
+
return 14
|
|
177
|
+
|
|
178
|
+
# NEW: CAVA Solar Corona Flare (Sub-Bass Eruption)
|
|
179
|
+
elif effect == "cava_solar_flare":
|
|
180
|
+
sub = max(cava_bars[:3]) if cava_bars else 0
|
|
181
|
+
coronal = math.sin(x_norm * 6.28 + t_now * 3.0) * (sub / 8.0)
|
|
182
|
+
row_bias = 0.3 if row_i == 0 else 0.0
|
|
183
|
+
flare = (sub / 8.0) * 0.7 + coronal * 0.3 + row_bias
|
|
184
|
+
if flare > 0.76: return 10
|
|
185
|
+
elif flare > 0.52: return 11
|
|
186
|
+
elif flare > 0.32: return 12
|
|
187
|
+
elif flare > 0.15: return 13
|
|
188
|
+
return 14
|
|
189
|
+
|
|
190
|
+
# NEW: CAVA Neural Synapse (Cerebral Pulse Network)
|
|
191
|
+
elif effect == "cava_neon_synapse":
|
|
192
|
+
highs = max(cava_bars[12:]) if len(cava_bars) >= 13 else 0
|
|
193
|
+
fire = math.sin(c_idx * 9.1 + t_now * (18.0 + highs * 6.0))
|
|
194
|
+
pulse = (fire > 0.6) * (highs / 8.0)
|
|
195
|
+
if pulse > 0.70: return 10
|
|
196
|
+
elif pulse > 0.45: return 11
|
|
197
|
+
elif pulse > 0.25: return 12
|
|
198
|
+
elif pulse > 0.10: return 13
|
|
199
|
+
return 14
|
|
200
|
+
|
|
201
|
+
# NEW: CAVA Crystalline Prism (Acoustic Refraction)
|
|
202
|
+
elif effect == "cava_crystal_shatter":
|
|
203
|
+
lows = sum(cava_bars[:6]) / 48.0 if len(cava_bars) >= 6 else 0
|
|
204
|
+
highs = sum(cava_bars[14:]) / 48.0 if len(cava_bars) >= 15 else 0
|
|
205
|
+
facet = math.sin((x_norm + lows) * 16.0 + t_now * (3.0 + highs * 5.0))
|
|
206
|
+
refract = (facet + 1.0) * 0.5 * (0.4 + highs * 0.6)
|
|
207
|
+
if refract > 0.80: return 10
|
|
208
|
+
elif refract > 0.58: return 11
|
|
209
|
+
elif refract > 0.38: return 12
|
|
210
|
+
elif refract > 0.18: return 13
|
|
211
|
+
return 14
|
|
212
|
+
|
|
213
|
+
# NEW: CAVA Cyber Glitch (Matrix Digital Rain)
|
|
214
|
+
elif effect == "cava_cyber_matrix_glitch":
|
|
215
|
+
bass = max(cava_bars[:4]) if len(cava_bars) >= 4 else 0
|
|
216
|
+
treble = max(cava_bars[12:]) if len(cava_bars) >= 13 else 0
|
|
217
|
+
glitch_seed = math.sin(c_idx * 13.37 + math.floor(t_now * (12.0 + treble * 4.0)))
|
|
218
|
+
drop_pos = (t_now * (4.0 + (bass / 8.0) * 8.0) + (c_idx * 0.15)) % 1.2
|
|
219
|
+
dist_drop = abs((row_i / 6.0) - drop_pos)
|
|
220
|
+
is_glitch = (glitch_seed > 0.65 and treble >= 4) or (bass >= 7 and dist_drop < 0.25)
|
|
221
|
+
if is_glitch: return 10
|
|
222
|
+
elif dist_drop < 0.18: return 11
|
|
223
|
+
elif dist_drop < 0.35: return 12
|
|
224
|
+
elif (bass / 8.0) > 0.4: return 13
|
|
225
|
+
return 14
|
|
226
|
+
|
|
227
|
+
# NEW: CAVA Supernova Inferno (Thermal Combustion Flame)
|
|
228
|
+
elif effect == "cava_supernova_inferno":
|
|
229
|
+
bin_idx = min(len(cava_bars)-1, int(x_norm * len(cava_bars)))
|
|
230
|
+
col_pow = cava_bars[bin_idx] / 8.0
|
|
231
|
+
height_norm = 1.0 - (row_i / 5.0) if row_i <= 5 else 0.0
|
|
232
|
+
heat_flicker = math.sin(c_idx * 5.0 + t_now * 15.0) * 0.18
|
|
233
|
+
flame = col_pow + heat_flicker
|
|
234
|
+
if flame >= height_norm + 0.35: return 10
|
|
235
|
+
elif flame >= height_norm + 0.15: return 11
|
|
236
|
+
elif flame >= height_norm - 0.10: return 12
|
|
237
|
+
elif flame >= height_norm - 0.30: return 13
|
|
238
|
+
return 14
|
|
239
|
+
|
|
240
|
+
# NEW: CAVA Laser Crosshairs (Tactical Dual Scanner)
|
|
241
|
+
elif effect == "cava_laser_crosshair_scan":
|
|
242
|
+
mids = sum(cava_bars[4:14]) / 80.0 if len(cava_bars) >= 14 else 0
|
|
243
|
+
speed = 1.5 + mids * 3.5
|
|
244
|
+
laser_l = (t_now * speed) % 1.4 - 0.2
|
|
245
|
+
laser_r = 1.0 - laser_l
|
|
246
|
+
dist_l = abs(x_norm - laser_l)
|
|
247
|
+
dist_r = abs(x_norm - laser_r)
|
|
248
|
+
collision = (abs(laser_l - 0.5) < 0.1) and (mids > 0.5)
|
|
249
|
+
if collision or min(dist_l, dist_r) < 0.04: return 10
|
|
250
|
+
elif min(dist_l, dist_r) < 0.10: return 11
|
|
251
|
+
elif min(dist_l, dist_r) < 0.20: return 12
|
|
252
|
+
elif min(dist_l, dist_r) < 0.32: return 13
|
|
253
|
+
return 14
|
|
254
|
+
|
|
255
|
+
# NEW: CAVA Liquid Mercury (Viscous Chrome Flow)
|
|
256
|
+
elif effect == "cava_liquid_mercury_flow":
|
|
257
|
+
energy = sum(cava_bars) / max(1, len(cava_bars) * 8.0)
|
|
258
|
+
v1 = math.sin(x_norm * 9.0 + t_now * (2.0 + energy * 4.0))
|
|
259
|
+
v2 = math.cos(row_i * 2.0 - t_now * (3.0 + energy * 3.0))
|
|
260
|
+
wave = (v1 + v2) * 0.5
|
|
261
|
+
specular = max(0.0, math.cos(wave * 3.14 - x_norm * 2.0))
|
|
262
|
+
glow = specular * 0.6 + energy * 0.4
|
|
263
|
+
if glow > 0.78: return 10
|
|
264
|
+
elif glow > 0.55: return 11
|
|
265
|
+
elif glow > 0.35: return 12
|
|
266
|
+
elif glow > 0.15: return 13
|
|
267
|
+
return 14
|
|
268
|
+
|
|
269
|
+
# NEW: CAVA Hyperspace Warp (Interstellar Tunnel)
|
|
270
|
+
elif effect == "cava_hyperspace_warp_tunnel":
|
|
271
|
+
bass = max(cava_bars[:3]) / 8.0 if cava_bars else 0
|
|
272
|
+
rad_dist = math.sqrt((x_norm - 0.5) ** 2 + ((row_i - 2.5) / 5.0) ** 2) * 2.0
|
|
273
|
+
warp_speed = 3.0 + bass * 8.0
|
|
274
|
+
ring_phase = (rad_dist * 4.0 - t_now * warp_speed) % 1.0
|
|
275
|
+
tunnel_glow = max(0.0, 1.0 - abs(ring_phase - 0.5) * 3.0) * (0.3 + bass * 0.7)
|
|
276
|
+
if tunnel_glow > 0.72: return 10
|
|
277
|
+
elif tunnel_glow > 0.48: return 11
|
|
278
|
+
elif tunnel_glow > 0.28: return 12
|
|
279
|
+
elif tunnel_glow > 0.10: return 13
|
|
280
|
+
return 14
|
|
281
|
+
|
|
282
|
+
# NEW: CAVA Tesla Arc Discharge (High-Voltage Lightning)
|
|
283
|
+
elif effect == "cava_tesla_lightning_arc":
|
|
284
|
+
highs = max(cava_bars[10:]) if len(cava_bars) >= 11 else 0
|
|
285
|
+
sub = max(cava_bars[:3]) if cava_bars else 0
|
|
286
|
+
spark_rand = math.sin(c_idx * 17.3 + math.floor(t_now * 24.0))
|
|
287
|
+
is_bolt = (spark_rand > 0.60 and highs >= 5) or (sub >= 7 and spark_rand > 0.40)
|
|
288
|
+
if is_bolt: return 10
|
|
289
|
+
elif spark_rand > 0.35 and highs >= 3: return 11
|
|
290
|
+
elif spark_rand > 0.10: return 12
|
|
291
|
+
elif (highs / 8.0) > 0.3: return 13
|
|
292
|
+
return 14
|
|
293
|
+
|
|
294
|
+
# 2. CAVA Live Frequency Equalizer
|
|
295
|
+
elif effect == "cava_equalizer":
|
|
296
|
+
bin_idx = min(len(cava_bars)-1, int(x_norm * len(cava_bars)))
|
|
297
|
+
bin_val = cava_bars[bin_idx]
|
|
298
|
+
if bin_val >= 7: return 10
|
|
299
|
+
elif bin_val >= 5: return 11
|
|
300
|
+
elif bin_val >= 3: return 12
|
|
301
|
+
elif bin_val >= 1: return 13
|
|
302
|
+
return 14
|
|
303
|
+
|
|
304
|
+
# 3. CAVA Sub-Bass Radial Shockwave Bloom
|
|
305
|
+
elif effect == "cava_bass_bloom":
|
|
306
|
+
bass = max(cava_bars[:4]) if len(cava_bars) >= 4 else 0
|
|
307
|
+
bass_ratio = bass / 8.0
|
|
308
|
+
dist_center = abs(x_norm - 0.5) * 2.0
|
|
309
|
+
shock_pos = (t_now * 2.5) % 1.2
|
|
310
|
+
wave_dist = abs(dist_center - shock_pos)
|
|
311
|
+
flare = max(0.0, 0.6 - wave_dist * 2.5) * bass_ratio
|
|
312
|
+
if flare > 0.45 or (bass >= 7 and dist_center < 0.3):
|
|
313
|
+
return 10
|
|
314
|
+
elif flare > 0.30:
|
|
315
|
+
return 11
|
|
316
|
+
elif flare > 0.15:
|
|
317
|
+
return 12
|
|
318
|
+
elif bass >= 4:
|
|
319
|
+
return 13
|
|
320
|
+
return 14
|
|
321
|
+
|
|
322
|
+
# 4. CAVA Audio Laser Sweep (Transient Burst)
|
|
323
|
+
elif effect == "cava_laser_sweep":
|
|
324
|
+
mids = max(cava_bars[4:14]) if len(cava_bars) >= 14 else 0
|
|
325
|
+
treble = max(cava_bars[14:]) if len(cava_bars) >= 15 else 0
|
|
326
|
+
speed = 1.0 + (mids / 8.0) * 2.5 + (treble / 8.0) * 1.5
|
|
327
|
+
laser_pos = (t_now * speed) % 1.4 - 0.2
|
|
328
|
+
dist = abs(x_norm - laser_pos)
|
|
329
|
+
if dist < 0.05:
|
|
330
|
+
return 10
|
|
331
|
+
elif dist < 0.12:
|
|
332
|
+
return 11
|
|
333
|
+
elif dist < 0.22:
|
|
334
|
+
return 12
|
|
335
|
+
elif dist < 0.35:
|
|
336
|
+
return 13
|
|
337
|
+
return 14
|
|
338
|
+
|
|
339
|
+
# 5. CAVA Treble Sparkle Storm (Vocal & Cymbal Glitter)
|
|
340
|
+
elif effect == "cava_treble_sparkle":
|
|
341
|
+
treble = max(cava_bars[12:]) if len(cava_bars) >= 13 else 0
|
|
342
|
+
if treble < 2: return 14
|
|
343
|
+
spark_phase = math.sin(c_idx * 2.3 + row_i * 4.1 + t_now * (6.0 + treble))
|
|
344
|
+
if spark_phase > 0.75 and treble >= 6:
|
|
345
|
+
return 10
|
|
346
|
+
elif spark_phase > 0.45 and treble >= 4:
|
|
347
|
+
return 11
|
|
348
|
+
elif spark_phase > 0.15 and treble >= 2:
|
|
349
|
+
return 12
|
|
350
|
+
return 14
|
|
351
|
+
|
|
352
|
+
# 6. CAVA Stereo Channel Wave (Left/Right Channel Ping-Pong)
|
|
353
|
+
elif effect == "cava_stereo_pulse":
|
|
354
|
+
half = len(cava_bars) // 2
|
|
355
|
+
l_amp = max(cava_bars[:half]) / 8.0 if half > 0 else 0
|
|
356
|
+
r_amp = max(cava_bars[half:]) / 8.0 if half > 0 else 0
|
|
357
|
+
if x_norm < 0.5:
|
|
358
|
+
col_ratio = l_amp * (1.0 - x_norm * 0.8)
|
|
359
|
+
else:
|
|
360
|
+
col_ratio = r_amp * (0.6 + (x_norm - 0.5) * 0.8)
|
|
361
|
+
if col_ratio > 0.82: return 10
|
|
362
|
+
elif col_ratio > 0.62: return 11
|
|
363
|
+
elif col_ratio > 0.42: return 12
|
|
364
|
+
elif col_ratio > 0.22: return 13
|
|
365
|
+
return 14
|
|
366
|
+
|
|
367
|
+
# 7. CAVA Kick Strobe (808 Bass Flash)
|
|
368
|
+
elif effect == "cava_kick_strobe":
|
|
369
|
+
bass = max(cava_bars[:3]) if len(cava_bars) >= 3 else 0
|
|
370
|
+
if bass >= 7:
|
|
371
|
+
return 10
|
|
372
|
+
elif bass >= 5:
|
|
373
|
+
return 11
|
|
374
|
+
elif bass >= 3:
|
|
375
|
+
return 12
|
|
376
|
+
return 14
|
|
377
|
+
|
|
378
|
+
# 8. CAVA Vocal Aura (Mid-Range Siphon)
|
|
379
|
+
elif effect == "cava_vocal_aura":
|
|
380
|
+
mids = max(cava_bars[4:12]) if len(cava_bars) >= 12 else 0
|
|
381
|
+
dist_c = abs(x_norm - 0.5) * 2.0
|
|
382
|
+
aura = (mids / 8.0) * max(0.0, 1.0 - dist_c * 0.8)
|
|
383
|
+
if aura > 0.8: return 10
|
|
384
|
+
elif aura > 0.55: return 11
|
|
385
|
+
elif aura > 0.35: return 12
|
|
386
|
+
elif aura > 0.15: return 13
|
|
387
|
+
return 14
|
|
388
|
+
|
|
389
|
+
# 9. CAVA Hi-Hat Glitter (Percussion Shimmer)
|
|
390
|
+
elif effect == "cava_hihat_glitter":
|
|
391
|
+
highs = max(cava_bars[14:]) if len(cava_bars) >= 15 else 0
|
|
392
|
+
if highs < 2: return 14
|
|
393
|
+
tick = math.sin(c_idx * 3.7 + row_i * 2.3 + t_now * (12.0 + highs * 2))
|
|
394
|
+
if tick > 0.70 and highs >= 5: return 10
|
|
395
|
+
elif tick > 0.40 and highs >= 3: return 11
|
|
396
|
+
elif tick > 0.10: return 12
|
|
397
|
+
return 14
|
|
398
|
+
|
|
399
|
+
# 10. CAVA Spectral Drift (Chroma Mood Ring)
|
|
400
|
+
elif effect == "cava_frequency_drift":
|
|
401
|
+
bass = sum(cava_bars[:6])
|
|
402
|
+
mids = sum(cava_bars[6:14])
|
|
403
|
+
highs = sum(cava_bars[14:])
|
|
404
|
+
tot = max(1, bass + mids + highs)
|
|
405
|
+
cog = (mids * 1.0 + highs * 2.0) / tot
|
|
406
|
+
drift = (cog + x_norm * 0.5 + t_now * 0.4) % 1.0
|
|
407
|
+
if drift < 0.2: return 10
|
|
408
|
+
elif drift < 0.4: return 11
|
|
409
|
+
elif drift < 0.6: return 12
|
|
410
|
+
elif drift < 0.8: return 13
|
|
411
|
+
return 14
|
|
412
|
+
|
|
413
|
+
# 11. CAVA RMS Heatwave (Loudness Flare)
|
|
414
|
+
elif effect == "cava_rms_heatwave":
|
|
415
|
+
rms = sum(b * b for b in cava_bars) / max(1, len(cava_bars) * 64.0)
|
|
416
|
+
row_factor = 1.0 if row_i == 0 else 0.75
|
|
417
|
+
flare = rms * row_factor + max(0.0, math.sin(x_norm * 3.14) * 0.25)
|
|
418
|
+
if flare > 0.70: return 10
|
|
419
|
+
elif flare > 0.50: return 11
|
|
420
|
+
elif flare > 0.30: return 12
|
|
421
|
+
elif flare > 0.15: return 13
|
|
422
|
+
return 14
|
|
423
|
+
|
|
424
|
+
# 12. CAVA Stereo Cascade (Dual Interference)
|
|
425
|
+
elif effect == "cava_stereo_cascade":
|
|
426
|
+
half = len(cava_bars) // 2
|
|
427
|
+
l_amp = max(cava_bars[:half]) / 8.0 if half > 0 else 0
|
|
428
|
+
r_amp = max(cava_bars[half:]) / 8.0 if half > 0 else 0
|
|
429
|
+
l_wave = max(0.0, l_amp - x_norm)
|
|
430
|
+
r_wave = max(0.0, r_amp - (1.0 - x_norm))
|
|
431
|
+
interference = l_wave + r_wave
|
|
432
|
+
if interference > 1.2: return 10
|
|
433
|
+
elif interference > 0.8: return 11
|
|
434
|
+
elif interference > 0.5: return 12
|
|
435
|
+
elif interference > 0.2: return 13
|
|
436
|
+
return 14
|
|
437
|
+
|
|
438
|
+
|
|
439
|
+
# 13. CAVA Transient Spark (Dynamic Accent)
|
|
440
|
+
elif effect == "cava_transient_spark":
|
|
441
|
+
mids = max(cava_bars[6:12]) if len(cava_bars) >= 12 else 0
|
|
442
|
+
if mids < 3: return 14
|
|
443
|
+
spark = math.sin(c_idx * 5.1 + row_i * 3.7 + t_now * (8.0 + mids * 3))
|
|
444
|
+
if spark > 0.82 and mids >= 6: return 10
|
|
445
|
+
elif spark > 0.50 and mids >= 4: return 11
|
|
446
|
+
elif spark > 0.20: return 12
|
|
447
|
+
return 14
|
|
448
|
+
|
|
449
|
+
# 14. CAVA Harmonic Resonance (Chord Glow)
|
|
450
|
+
elif effect == "cava_harmonic_breath":
|
|
451
|
+
tot_energy = sum(cava_bars) / max(1, len(cava_bars) * 8.0)
|
|
452
|
+
breath = (math.sin(t_now * 2.0) + 1.0) * 0.5 * tot_energy
|
|
453
|
+
if breath > 0.65: return 10
|
|
454
|
+
elif breath > 0.45: return 11
|
|
455
|
+
elif breath > 0.25: return 12
|
|
456
|
+
elif breath > 0.10: return 13
|
|
457
|
+
return 14
|
|
458
|
+
|
|
459
|
+
# 15. CAVA Sub-Bass Tremor (Low-End Rumble)
|
|
460
|
+
elif effect == "cava_sub_earthquake":
|
|
461
|
+
sub = max(cava_bars[:2]) if len(cava_bars) >= 2 else 0
|
|
462
|
+
if sub >= 7:
|
|
463
|
+
tremor = int((t_now * 30.0 + c_idx) % 2)
|
|
464
|
+
return 10 if tremor == 0 else 11
|
|
465
|
+
elif sub >= 5:
|
|
466
|
+
return 11
|
|
467
|
+
elif sub >= 3:
|
|
468
|
+
return 12
|
|
469
|
+
return 14
|
|
470
|
+
|
|
471
|
+
# 16. CAVA Aurora Wave (Spectral Ribbon)
|
|
472
|
+
elif effect == "cava_aurora_wave":
|
|
473
|
+
speed = 1.0 + (sum(cava_bars) / max(1, len(cava_bars) * 8.0)) * 2.0
|
|
474
|
+
ribbon_pos = (t_now * speed) % 1.5 - 0.25
|
|
475
|
+
dist = abs(x_norm - ribbon_pos)
|
|
476
|
+
wave = max(0.0, 0.45 - dist * 2.0)
|
|
477
|
+
if wave > 0.35: return 10
|
|
478
|
+
elif wave > 0.25: return 11
|
|
479
|
+
elif wave > 0.15: return 12
|
|
480
|
+
elif wave > 0.05: return 13
|
|
481
|
+
return 14
|
|
482
|
+
|
|
483
|
+
# 17. CAVA Stereo Panning (Binaural Flow)
|
|
484
|
+
elif effect == "cava_stereo_pan":
|
|
485
|
+
half = len(cava_bars) // 2
|
|
486
|
+
l_pow = sum(cava_bars[:half])
|
|
487
|
+
r_pow = sum(cava_bars[half:])
|
|
488
|
+
tot = max(1, l_pow + r_pow)
|
|
489
|
+
pan_center = r_pow / tot
|
|
490
|
+
dist_pan = abs(x_norm - pan_center)
|
|
491
|
+
glow = max(0.0, 0.5 - dist_pan * 1.5)
|
|
492
|
+
if glow > 0.38: return 10
|
|
493
|
+
elif glow > 0.28: return 11
|
|
494
|
+
elif glow > 0.18: return 12
|
|
495
|
+
elif glow > 0.08: return 13
|
|
496
|
+
return 14
|
|
497
|
+
|
|
498
|
+
# 18. CAVA Vocal Resonance (Liquid Ripple)
|
|
499
|
+
elif effect == "cava_vocal_ripple":
|
|
500
|
+
mids = max(cava_bars[4:12]) if len(cava_bars) >= 12 else 0
|
|
501
|
+
ripple = math.sin(abs(x_norm - 0.5) * 14.0 - t_now * (3.0 + (mids / 8.0) * 5.0))
|
|
502
|
+
intensity = (mids / 8.0) * 0.6 + max(0.0, ripple * 0.4)
|
|
503
|
+
if intensity > 0.78: return 10
|
|
504
|
+
elif intensity > 0.55: return 11
|
|
505
|
+
elif intensity > 0.35: return 12
|
|
506
|
+
elif intensity > 0.15: return 13
|
|
507
|
+
return 14
|
|
508
|
+
|
|
509
|
+
# 19. CAVA Laser Crossfire (Dual Snare Lasers)
|
|
510
|
+
elif effect == "cava_laser_crossfire":
|
|
511
|
+
treble = max(cava_bars[12:]) if len(cava_bars) >= 13 else 0
|
|
512
|
+
speed = 1.5 + (treble / 8.0) * 4.0
|
|
513
|
+
laser_l = (t_now * speed) % 1.6 - 0.3
|
|
514
|
+
laser_r = 1.0 - ((t_now * speed) % 1.6 - 0.3)
|
|
515
|
+
dist_l = abs(x_norm - laser_l)
|
|
516
|
+
dist_r = abs(x_norm - laser_r)
|
|
517
|
+
min_dist = min(dist_l, dist_r)
|
|
518
|
+
if min_dist < 0.06: return 10
|
|
519
|
+
elif min_dist < 0.14: return 11
|
|
520
|
+
elif min_dist < 0.24: return 12
|
|
521
|
+
elif min_dist < 0.36: return 13
|
|
522
|
+
return 14
|
|
523
|
+
|
|
524
|
+
# 20. CAVA Spectrum Cascade (Multi-Band Rain)
|
|
525
|
+
elif effect == "cava_spectrum_cascade":
|
|
526
|
+
bin_idx = min(len(cava_bars)-1, int(x_norm * len(cava_bars)))
|
|
527
|
+
v = cava_bars[bin_idx]
|
|
528
|
+
drop = math.sin(row_i * 3.5 + c_idx * 1.8 + t_now * (4.0 + v))
|
|
529
|
+
flare = (v / 8.0) * 0.65 + max(0.0, drop * 0.35)
|
|
530
|
+
if flare > 0.80: return 10
|
|
531
|
+
elif flare > 0.60: return 11
|
|
532
|
+
elif flare > 0.40: return 12
|
|
533
|
+
elif flare > 0.20: return 13
|
|
534
|
+
return 14
|
|
535
|
+
|
|
536
|
+
# 21. CAVA Cardiac Sub-Pulse (Kick Rhythm)
|
|
537
|
+
elif effect == "cava_heartbeat_sub":
|
|
538
|
+
bass = max(cava_bars[:3]) if len(cava_bars) >= 3 else 0
|
|
539
|
+
pulse = math.sin(t_now * 8.0)
|
|
540
|
+
cardiac = (bass / 8.0) * (1.0 if pulse > 0.3 else 0.4)
|
|
541
|
+
if cardiac > 0.75: return 10
|
|
542
|
+
elif cardiac > 0.50: return 11
|
|
543
|
+
elif cardiac > 0.30: return 12
|
|
544
|
+
elif cardiac > 0.15: return 13
|
|
545
|
+
return 14
|
|
546
|
+
|
|
547
|
+
# 22. CAVA Diamond Glitter (Hi-Hat Storm)
|
|
548
|
+
elif effect == "cava_diamond_glitter":
|
|
549
|
+
highs = max(cava_bars[14:]) if len(cava_bars) >= 15 else 0
|
|
550
|
+
if highs < 2: return 14
|
|
551
|
+
shimmer = math.sin(c_idx * 7.7 + row_i * 5.3 + t_now * (16.0 + highs * 3))
|
|
552
|
+
if shimmer > 0.85 and highs >= 5: return 10
|
|
553
|
+
elif shimmer > 0.50 and highs >= 3: return 11
|
|
554
|
+
elif shimmer > 0.15: return 12
|
|
555
|
+
return 14
|
|
556
|
+
|
|
557
|
+
# 23. CAVA Harmonic Vortex (Acoustic Whirlwind)
|
|
558
|
+
elif effect == "cava_harmonic_vortex":
|
|
559
|
+
energy = sum(cava_bars) / max(1, len(cava_bars) * 8.0)
|
|
560
|
+
angle = math.atan2(row_i - 0.5, x_norm - 0.5)
|
|
561
|
+
vortex = math.sin(angle * 2.0 + t_now * (3.0 + energy * 6.0))
|
|
562
|
+
glow = energy * 0.6 + max(0.0, vortex * 0.4)
|
|
563
|
+
if glow > 0.75: return 10
|
|
564
|
+
elif glow > 0.55: return 11
|
|
565
|
+
elif glow > 0.35: return 12
|
|
566
|
+
elif glow > 0.15: return 13
|
|
567
|
+
return 14
|
|
568
|
+
|
|
569
|
+
return 14
|