Upload publicapi.py
Browse files- public/publicapi.py +105 -62
public/publicapi.py
CHANGED
|
@@ -2,7 +2,7 @@
|
|
| 2 |
#!/usr/bin/env python3
|
| 3 |
# -*- coding: utf-8 -*-
|
| 4 |
|
| 5 |
-
# GhostAI Music Generator — Release v1.3.
|
| 6 |
# Gradio UI + FastAPI server, externalized styles (CSS), prompts (INI), and examples (MD).
|
| 7 |
# Saves MP3s to ./mp3, single rotating log (max 5MB) in ./logs, colorized console.
|
| 8 |
|
|
@@ -40,7 +40,7 @@ import uvicorn
|
|
| 40 |
|
| 41 |
from colorama import init as colorama_init, Fore, Style
|
| 42 |
|
| 43 |
-
RELEASE = "v1.3.
|
| 44 |
|
| 45 |
# ======================================================================================
|
| 46 |
# PATCHES & RUNTIME
|
|
@@ -263,7 +263,7 @@ def balance_stereo(seg: AudioSegment, noise_threshold=-40, sample_rate=48000) ->
|
|
| 263 |
stereo = stereo * mask
|
| 264 |
left, right = stereo[:, 0], stereo[:, 1]
|
| 265 |
l_rms = np.sqrt(np.mean(left[left != 0] ** 2)) if np.any(left != 0) else 0
|
| 266 |
-
r_rms = np.sqrt(np.mean(
|
| 267 |
if l_rms > 0 and r_rms > 0:
|
| 268 |
avg = (l_rms + r_rms) / 2
|
| 269 |
stereo[:, 0] *= (avg / l_rms)
|
|
@@ -316,12 +316,11 @@ def apply_fade(seg: AudioSegment, fade_in=500, fade_out=800) -> AudioSegment:
|
|
| 316 |
return seg
|
| 317 |
|
| 318 |
# ======================================================================================
|
| 319 |
-
# PROMPTS (FROM INI) — SAFE FORMAT
|
| 320 |
# ======================================================================================
|
| 321 |
|
| 322 |
class SafeFormatDict(dict):
|
| 323 |
def __missing__(self, key):
|
| 324 |
-
# Gracefully handle missing placeholders in templates (e.g., {mood}, {genre})
|
| 325 |
return ""
|
| 326 |
|
| 327 |
class StylesConfig:
|
|
@@ -343,11 +342,12 @@ class StylesConfig:
|
|
| 343 |
self.styles = {}
|
| 344 |
for sec in self.cfg.sections():
|
| 345 |
d: Dict[str, Any] = {k: v for k, v in self.cfg.items(sec)}
|
| 346 |
-
#
|
| 347 |
listish = {
|
| 348 |
"drum_beat", "synthesizer", "rhythmic_steps", "bass_style", "guitar_style",
|
| 349 |
"variations", "mood", "genre", "key", "scale", "feel", "instrument",
|
| 350 |
-
"lead", "pad", "arp", "drums", "bass", "guitar", "strings", "brass", "woodwinds"
|
|
|
|
| 351 |
}
|
| 352 |
for key in listish:
|
| 353 |
if key in d and isinstance(d[key], str):
|
|
@@ -366,13 +366,10 @@ class StylesConfig:
|
|
| 366 |
self.maybe_reload()
|
| 367 |
return list(self.styles.keys())
|
| 368 |
|
| 369 |
-
def
|
| 370 |
-
if
|
| 371 |
-
return
|
| 372 |
-
|
| 373 |
-
if isinstance(val, list):
|
| 374 |
-
return random.choice(val) if val else "none"
|
| 375 |
-
return str(val) if val else "none"
|
| 376 |
|
| 377 |
def build_prompt(
|
| 378 |
self,
|
|
@@ -390,19 +387,21 @@ class StylesConfig:
|
|
| 390 |
return ""
|
| 391 |
s = self.styles[style]
|
| 392 |
|
| 393 |
-
# BPM handling
|
| 394 |
bpm_min = int(s.get("bpm_min", "100"))
|
| 395 |
bpm_max = int(s.get("bpm_max", "140"))
|
| 396 |
final_bpm = bpm if bpm != 120 else random.randint(bpm_min, bpm_max)
|
| 397 |
|
| 398 |
-
|
| 399 |
-
|
| 400 |
-
|
| 401 |
-
|
| 402 |
-
|
| 403 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 404 |
|
| 405 |
-
# Variation logic per chunk
|
| 406 |
var_list = s.get("variations", [])
|
| 407 |
variation = ""
|
| 408 |
if isinstance(var_list, list) and var_list:
|
|
@@ -411,37 +410,54 @@ class StylesConfig:
|
|
| 411 |
else:
|
| 412 |
variation = random.choice(var_list)
|
| 413 |
|
| 414 |
-
# Start
|
| 415 |
fields: Dict[str, Any] = {}
|
| 416 |
for k, v in s.items():
|
| 417 |
-
if isinstance(v, list)
|
| 418 |
-
|
| 419 |
-
|
| 420 |
-
|
|
|
|
| 421 |
|
| 422 |
-
# Overlay computed/required fields
|
| 423 |
fields.update({
|
| 424 |
"bpm": final_bpm,
|
| 425 |
"chunk": chunk_num,
|
| 426 |
-
"drum": d if d
|
| 427 |
-
"synth": syn if syn
|
| 428 |
-
"rhythm": r if r
|
| 429 |
-
"bass": b if b
|
| 430 |
-
"guitar": g if g
|
| 431 |
"variation": variation
|
| 432 |
})
|
| 433 |
|
| 434 |
-
# Default template if none in INI
|
| 435 |
tpl = s.get(
|
| 436 |
"prompt_template",
|
| 437 |
-
"Instrumental track at {bpm} BPM {variation}. {mood} {
|
| 438 |
)
|
| 439 |
|
| 440 |
-
# Safe formatting (prevents KeyError for undefined placeholders like {mood})
|
| 441 |
prompt = tpl.format_map(SafeFormatDict(fields))
|
| 442 |
prompt = re.sub(r"\s{2,}", " ", prompt).strip()
|
| 443 |
return prompt
|
| 444 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 445 |
STYLES = StylesConfig(PROMPTS_INI)
|
| 446 |
|
| 447 |
# ======================================================================================
|
|
@@ -572,7 +588,6 @@ def generate_music(
|
|
| 572 |
if not instrumental_prompt.strip():
|
| 573 |
return None, "⚠️ Enter a prompt.", vram_status_text
|
| 574 |
|
| 575 |
-
# Validate I/O
|
| 576 |
try:
|
| 577 |
out_sr = int(output_sample_rate)
|
| 578 |
except:
|
|
@@ -818,7 +833,6 @@ def prompt(style: str, bpm: int = 120, chunk: int = 1,
|
|
| 818 |
return {"style": style, "prompt": txt}
|
| 819 |
|
| 820 |
# Back-compat endpoints declared in prompts.ini (e.g., /set_classical_star_wars_prompt)
|
| 821 |
-
# Fix closure capture by binding route path explicitly.
|
| 822 |
for sec, cfg in list(STYLES.styles.items()):
|
| 823 |
api_name = cfg.get("api_name")
|
| 824 |
if api_name:
|
|
@@ -903,19 +917,13 @@ def read_css() -> str:
|
|
| 903 |
try:
|
| 904 |
if CSS_FILE.exists():
|
| 905 |
return CSS_FILE.read_text(encoding="utf-8")
|
| 906 |
-
# High-contrast ADA-compliant fallback (white text, dark bg)
|
| 907 |
return """
|
| 908 |
:root { color-scheme: dark; }
|
| 909 |
-
body, .gradio-container {
|
| 910 |
-
background: #0E1014 !important;
|
| 911 |
-
color: #FFFFFF !important;
|
| 912 |
-
}
|
| 913 |
* { color: #FFFFFF !important; }
|
| 914 |
input, textarea, select {
|
| 915 |
-
background: #151922 !important;
|
| 916 |
-
|
| 917 |
-
border: 1px solid #2A3142 !important;
|
| 918 |
-
border-radius: 10px !important;
|
| 919 |
}
|
| 920 |
.ga-header { display:flex; gap:12px; align-items:center; }
|
| 921 |
.ga-header .logo { font-size: 28px; }
|
|
@@ -956,7 +964,6 @@ with gr.Blocks(css=read_css(), analytics_enabled=False, title=f"GhostAI Music Ge
|
|
| 956 |
# BAND GRID (fixed rows of 4 per row)
|
| 957 |
with gr.Group(elem_classes="ga-section"):
|
| 958 |
gr.Markdown("### Band / Style (grid 4 per row)")
|
| 959 |
-
# helper to create a row of 4 buttons
|
| 960 |
def row_of_buttons(entries):
|
| 961 |
with gr.Row(equal_height=True):
|
| 962 |
buttons = []
|
|
@@ -965,7 +972,6 @@ with gr.Blocks(css=read_css(), analytics_enabled=False, title=f"GhostAI Music Ge
|
|
| 965 |
buttons.append((key, btn))
|
| 966 |
return buttons
|
| 967 |
|
| 968 |
-
# rows
|
| 969 |
row1 = row_of_buttons([
|
| 970 |
("metallica", "Metallica (Thrash) 🎸"),
|
| 971 |
("nirvana", "Nirvana (Grunge) 🎤"),
|
|
@@ -974,7 +980,7 @@ with gr.Blocks(css=read_css(), analytics_enabled=False, title=f"GhostAI Music Ge
|
|
| 974 |
])
|
| 975 |
row2 = row_of_buttons([
|
| 976 |
("foo_fighters", "Foo Fighters (Alt Rock) 🤘"),
|
| 977 |
-
("
|
| 978 |
("smashing_pumpkins", "Smashing Pumpkins (Alt) 🎃"),
|
| 979 |
("radiohead", "Radiohead (Experimental) 🧠"),
|
| 980 |
])
|
|
@@ -1006,12 +1012,12 @@ with gr.Blocks(css=read_css(), analytics_enabled=False, title=f"GhostAI Music Ge
|
|
| 1006 |
target_volume = gr.Slider(-30.0, -20.0, step=0.5, value=float(loaded.get("target_volume", -23.0)), label="Target Loudness (dBFS RMS)")
|
| 1007 |
preset = gr.Dropdown(choices=["default", "rock", "techno", "grunge", "indie", "funk_rock"], value=str(loaded.get("preset", "default")), label="Preset")
|
| 1008 |
with gr.Row():
|
| 1009 |
-
drum_beat = gr.Dropdown(choices=["none", "standard rock", "funk groove", "techno kick", "jazz swing"], value=str(loaded.get("drum_beat", "none")), label="Drum Beat")
|
| 1010 |
-
synthesizer = gr.Dropdown(choices=["none", "analog synth", "digital pad", "arpeggiated synth"], value=str(loaded.get("synthesizer", "none")), label="Synthesizer")
|
| 1011 |
-
rhythmic_steps = gr.Dropdown(choices=["none", "syncopated steps", "steady steps", "complex steps"], value=str(loaded.get("rhythmic_steps", "none")), label="Rhythmic Steps")
|
| 1012 |
with gr.Row():
|
| 1013 |
-
bass_style = gr.Dropdown(choices=["none", "slap bass", "deep bass", "melodic bass"], value=str(loaded.get("bass_style", "none")), label="Bass Style")
|
| 1014 |
-
guitar_style = gr.Dropdown(choices=["none", "distorted", "clean", "jangle"], value=str(loaded.get("guitar_style", "none")), label="Guitar Style")
|
| 1015 |
max_steps = gr.Dropdown(choices=[1000, 1200, 1300, 1500], value=int(loaded.get("max_steps", 1500)), label="Max Steps (hint)")
|
| 1016 |
|
| 1017 |
bitrate_state = gr.State(value=str(loaded.get("bitrate", "192k")))
|
|
@@ -1053,18 +1059,52 @@ with gr.Blocks(css=read_css(), analytics_enabled=False, title=f"GhostAI Music Ge
|
|
| 1053 |
refresh_md = gr.Button("Refresh Examples.md", variant="secondary")
|
| 1054 |
refresh_md.click(lambda: read_examples(), outputs=md_box)
|
| 1055 |
|
| 1056 |
-
#
|
| 1057 |
-
|
| 1058 |
-
|
| 1059 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1060 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1061 |
for key, btn in row1 + row2 + row3 + row4:
|
| 1062 |
if key == "foo_pad":
|
| 1063 |
continue
|
| 1064 |
btn.click(
|
| 1065 |
-
|
| 1066 |
inputs=[gr.State(key), bpm, drum_beat, synthesizer, rhythmic_steps, bass_style, guitar_style],
|
| 1067 |
-
outputs=instrumental_prompt
|
| 1068 |
)
|
| 1069 |
|
| 1070 |
# Quick-sets
|
|
@@ -1165,6 +1205,10 @@ with gr.Blocks(css=read_css(), analytics_enabled=False, title=f"GhostAI Music Ge
|
|
| 1165 |
load_btn.click(
|
| 1166 |
_load_action,
|
| 1167 |
outputs=[
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1168 |
instrumental_prompt, cfg_scale, top_k, top_p, temperature, total_duration, bpm,
|
| 1169 |
drum_beat, synthesizer, rhythmic_steps, bass_style, guitar_style, target_volume,
|
| 1170 |
preset, max_steps, bitrate_state, sample_rate_state, bit_depth_state, status_box
|
|
@@ -1180,7 +1224,6 @@ with gr.Blocks(css=read_css(), analytics_enabled=False, title=f"GhostAI Music Ge
|
|
| 1180 |
]
|
| 1181 |
)
|
| 1182 |
|
| 1183 |
-
# Logs
|
| 1184 |
def _get_log():
|
| 1185 |
try:
|
| 1186 |
return LOG_FILE.read_text(encoding="utf-8")[-40000:]
|
|
|
|
| 2 |
#!/usr/bin/env python3
|
| 3 |
# -*- coding: utf-8 -*-
|
| 4 |
|
| 5 |
+
# GhostAI Music Generator — Release v1.3.2
|
| 6 |
# Gradio UI + FastAPI server, externalized styles (CSS), prompts (INI), and examples (MD).
|
| 7 |
# Saves MP3s to ./mp3, single rotating log (max 5MB) in ./logs, colorized console.
|
| 8 |
|
|
|
|
| 40 |
|
| 41 |
from colorama import init as colorama_init, Fore, Style
|
| 42 |
|
| 43 |
+
RELEASE = "v1.3.2"
|
| 44 |
|
| 45 |
# ======================================================================================
|
| 46 |
# PATCHES & RUNTIME
|
|
|
|
| 263 |
stereo = stereo * mask
|
| 264 |
left, right = stereo[:, 0], stereo[:, 1]
|
| 265 |
l_rms = np.sqrt(np.mean(left[left != 0] ** 2)) if np.any(left != 0) else 0
|
| 266 |
+
r_rms = np.sqrt(np.mean(right[right != 0] ** 2)) if np.any(right != 0) else 0
|
| 267 |
if l_rms > 0 and r_rms > 0:
|
| 268 |
avg = (l_rms + r_rms) / 2
|
| 269 |
stereo[:, 0] *= (avg / l_rms)
|
|
|
|
| 316 |
return seg
|
| 317 |
|
| 318 |
# ======================================================================================
|
| 319 |
+
# PROMPTS (FROM INI) — SAFE FORMAT + STYLE-DRIVEN UI CHANGES
|
| 320 |
# ======================================================================================
|
| 321 |
|
| 322 |
class SafeFormatDict(dict):
|
| 323 |
def __missing__(self, key):
|
|
|
|
| 324 |
return ""
|
| 325 |
|
| 326 |
class StylesConfig:
|
|
|
|
| 342 |
self.styles = {}
|
| 343 |
for sec in self.cfg.sections():
|
| 344 |
d: Dict[str, Any] = {k: v for k, v in self.cfg.items(sec)}
|
| 345 |
+
# Normalize list-like fields
|
| 346 |
listish = {
|
| 347 |
"drum_beat", "synthesizer", "rhythmic_steps", "bass_style", "guitar_style",
|
| 348 |
"variations", "mood", "genre", "key", "scale", "feel", "instrument",
|
| 349 |
+
"lead", "pad", "arp", "drums", "bass", "guitar", "strings", "brass", "woodwinds",
|
| 350 |
+
"structure"
|
| 351 |
}
|
| 352 |
for key in listish:
|
| 353 |
if key in d and isinstance(d[key], str):
|
|
|
|
| 366 |
self.maybe_reload()
|
| 367 |
return list(self.styles.keys())
|
| 368 |
|
| 369 |
+
def _pick_from_list(self, vals: Any) -> str:
|
| 370 |
+
if isinstance(vals, list):
|
| 371 |
+
return random.choice(vals) if vals else ""
|
| 372 |
+
return str(vals or "")
|
|
|
|
|
|
|
|
|
|
| 373 |
|
| 374 |
def build_prompt(
|
| 375 |
self,
|
|
|
|
| 387 |
return ""
|
| 388 |
s = self.styles[style]
|
| 389 |
|
|
|
|
| 390 |
bpm_min = int(s.get("bpm_min", "100"))
|
| 391 |
bpm_max = int(s.get("bpm_max", "140"))
|
| 392 |
final_bpm = bpm if bpm != 120 else random.randint(bpm_min, bpm_max)
|
| 393 |
|
| 394 |
+
def choose(field_name: str, incoming: str) -> str:
|
| 395 |
+
if incoming and incoming != "none":
|
| 396 |
+
return incoming
|
| 397 |
+
return self._pick_from_list(s.get(field_name, [])) or ""
|
| 398 |
+
|
| 399 |
+
d = choose("drum_beat", drum_beat)
|
| 400 |
+
syn = choose("synthesizer", synthesizer)
|
| 401 |
+
r = choose("rhythmic_steps", rhythmic_steps)
|
| 402 |
+
b = choose("bass_style", bass_style)
|
| 403 |
+
g = choose("guitar_style", guitar_style)
|
| 404 |
|
|
|
|
| 405 |
var_list = s.get("variations", [])
|
| 406 |
variation = ""
|
| 407 |
if isinstance(var_list, list) and var_list:
|
|
|
|
| 410 |
else:
|
| 411 |
variation = random.choice(var_list)
|
| 412 |
|
| 413 |
+
# Start from style fields (pick one value from lists)
|
| 414 |
fields: Dict[str, Any] = {}
|
| 415 |
for k, v in s.items():
|
| 416 |
+
fields[k] = self._pick_from_list(v) if isinstance(v, list) else v
|
| 417 |
+
|
| 418 |
+
# Map structure -> section for templates that use {section}
|
| 419 |
+
if "structure" in s:
|
| 420 |
+
fields["section"] = self._pick_from_list(s["structure"])
|
| 421 |
|
|
|
|
| 422 |
fields.update({
|
| 423 |
"bpm": final_bpm,
|
| 424 |
"chunk": chunk_num,
|
| 425 |
+
"drum": f" {d}" if d else "",
|
| 426 |
+
"synth": f" {syn}" if syn else "",
|
| 427 |
+
"rhythm": f" {r}" if r else "",
|
| 428 |
+
"bass": f" {b}" if b else "",
|
| 429 |
+
"guitar": f" {g}" if g else "",
|
| 430 |
"variation": variation
|
| 431 |
})
|
| 432 |
|
|
|
|
| 433 |
tpl = s.get(
|
| 434 |
"prompt_template",
|
| 435 |
+
"Instrumental track at {bpm} BPM {variation}. {mood} {section} {drum}{bass}{guitar}{synth}{rhythm}"
|
| 436 |
)
|
| 437 |
|
|
|
|
| 438 |
prompt = tpl.format_map(SafeFormatDict(fields))
|
| 439 |
prompt = re.sub(r"\s{2,}", " ", prompt).strip()
|
| 440 |
return prompt
|
| 441 |
|
| 442 |
+
def style_defaults_for_ui(self, style: str) -> Dict[str, Any]:
|
| 443 |
+
self.maybe_reload()
|
| 444 |
+
s = self.styles.get(style, {})
|
| 445 |
+
bpm_min = int(s.get("bpm_min", "100"))
|
| 446 |
+
bpm_max = int(s.get("bpm_max", "140"))
|
| 447 |
+
chosen = {
|
| 448 |
+
"bpm": random.randint(bpm_min, bpm_max),
|
| 449 |
+
"drum_beat": self._pick_from_list(s.get("drum_beat", [])) or "none",
|
| 450 |
+
"synthesizer": self._pick_from_list(s.get("synthesizer", [])) or "none",
|
| 451 |
+
"rhythmic_steps": self._pick_from_list(s.get("rhythmic_steps", [])) or "none",
|
| 452 |
+
"bass_style": self._pick_from_list(s.get("bass_style", [])) or "none",
|
| 453 |
+
"guitar_style": self._pick_from_list(s.get("guitar_style", [])) or "none",
|
| 454 |
+
}
|
| 455 |
+
# Normalize any "none" literal that might sneak in as empty
|
| 456 |
+
for k, v in chosen.items():
|
| 457 |
+
if v == "":
|
| 458 |
+
chosen[k] = "none"
|
| 459 |
+
return chosen
|
| 460 |
+
|
| 461 |
STYLES = StylesConfig(PROMPTS_INI)
|
| 462 |
|
| 463 |
# ======================================================================================
|
|
|
|
| 588 |
if not instrumental_prompt.strip():
|
| 589 |
return None, "⚠️ Enter a prompt.", vram_status_text
|
| 590 |
|
|
|
|
| 591 |
try:
|
| 592 |
out_sr = int(output_sample_rate)
|
| 593 |
except:
|
|
|
|
| 833 |
return {"style": style, "prompt": txt}
|
| 834 |
|
| 835 |
# Back-compat endpoints declared in prompts.ini (e.g., /set_classical_star_wars_prompt)
|
|
|
|
| 836 |
for sec, cfg in list(STYLES.styles.items()):
|
| 837 |
api_name = cfg.get("api_name")
|
| 838 |
if api_name:
|
|
|
|
| 917 |
try:
|
| 918 |
if CSS_FILE.exists():
|
| 919 |
return CSS_FILE.read_text(encoding="utf-8")
|
|
|
|
| 920 |
return """
|
| 921 |
:root { color-scheme: dark; }
|
| 922 |
+
body, .gradio-container { background: #0E1014 !important; color: #FFFFFF !important; }
|
|
|
|
|
|
|
|
|
|
| 923 |
* { color: #FFFFFF !important; }
|
| 924 |
input, textarea, select {
|
| 925 |
+
background: #151922 !important; color: #FFFFFF !important;
|
| 926 |
+
border: 1px solid #2A3142 !important; border-radius: 10px !important;
|
|
|
|
|
|
|
| 927 |
}
|
| 928 |
.ga-header { display:flex; gap:12px; align-items:center; }
|
| 929 |
.ga-header .logo { font-size: 28px; }
|
|
|
|
| 964 |
# BAND GRID (fixed rows of 4 per row)
|
| 965 |
with gr.Group(elem_classes="ga-section"):
|
| 966 |
gr.Markdown("### Band / Style (grid 4 per row)")
|
|
|
|
| 967 |
def row_of_buttons(entries):
|
| 968 |
with gr.Row(equal_height=True):
|
| 969 |
buttons = []
|
|
|
|
| 972 |
buttons.append((key, btn))
|
| 973 |
return buttons
|
| 974 |
|
|
|
|
| 975 |
row1 = row_of_buttons([
|
| 976 |
("metallica", "Metallica (Thrash) 🎸"),
|
| 977 |
("nirvana", "Nirvana (Grunge) 🎤"),
|
|
|
|
| 980 |
])
|
| 981 |
row2 = row_of_buttons([
|
| 982 |
("foo_fighters", "Foo Fighters (Alt Rock) 🤘"),
|
| 983 |
+
("red_hot_chili_peppers", "Red Hot Chili Peppers (Funk Rock) 🌶️"),
|
| 984 |
("smashing_pumpkins", "Smashing Pumpkins (Alt) 🎃"),
|
| 985 |
("radiohead", "Radiohead (Experimental) 🧠"),
|
| 986 |
])
|
|
|
|
| 1012 |
target_volume = gr.Slider(-30.0, -20.0, step=0.5, value=float(loaded.get("target_volume", -23.0)), label="Target Loudness (dBFS RMS)")
|
| 1013 |
preset = gr.Dropdown(choices=["default", "rock", "techno", "grunge", "indie", "funk_rock"], value=str(loaded.get("preset", "default")), label="Preset")
|
| 1014 |
with gr.Row():
|
| 1015 |
+
drum_beat = gr.Dropdown(choices=["none", "standard rock", "funk groove", "techno kick", "jazz swing", "four-on-the-floor", "steady kick", "orchestral percussion", "precise drums", "heavy drums"], value=str(loaded.get("drum_beat", "none")), label="Drum Beat")
|
| 1016 |
+
synthesizer = gr.Dropdown(choices=["none", "analog synth", "digital pad", "arpeggiated synth", "lush synths", "atmospheric synths", "pulsing synths", "analog pad", "warm synths"], value=str(loaded.get("synthesizer", "none")), label="Synthesizer")
|
| 1017 |
+
rhythmic_steps = gr.Dropdown(choices=["none", "syncopated steps", "steady steps", "complex steps", "martial march", "staccato ostinato", "triplet swells"], value=str(loaded.get("rhythmic_steps", "none")), label="Rhythmic Steps")
|
| 1018 |
with gr.Row():
|
| 1019 |
+
bass_style = gr.Dropdown(choices=["none", "slap bass", "deep bass", "melodic bass", "groovy bass", "hypnotic bass", "driving bass", "low brass", "cellos", "double basses", "subby bass"], value=str(loaded.get("bass_style", "none")), label="Bass Style")
|
| 1020 |
+
guitar_style = gr.Dropdown(choices=["none", "distorted", "clean", "jangle", "downpicked", "thrash riffing", "dreamy", "experimental", "funky"], value=str(loaded.get("guitar_style", "none")), label="Guitar Style")
|
| 1021 |
max_steps = gr.Dropdown(choices=[1000, 1200, 1300, 1500], value=int(loaded.get("max_steps", 1500)), label="Max Steps (hint)")
|
| 1022 |
|
| 1023 |
bitrate_state = gr.State(value=str(loaded.get("bitrate", "192k")))
|
|
|
|
| 1059 |
refresh_md = gr.Button("Refresh Examples.md", variant="secondary")
|
| 1060 |
refresh_md.click(lambda: read_examples(), outputs=md_box)
|
| 1061 |
|
| 1062 |
+
# =========================
|
| 1063 |
+
# STYLE -> UI SYNC HANDLER
|
| 1064 |
+
# =========================
|
| 1065 |
+
def set_prompt_and_settings_from_style(style_key, current_bpm, current_drum, current_synth, current_steps, current_bass, current_guitar):
|
| 1066 |
+
# Always derive fresh settings based on the selected style
|
| 1067 |
+
defaults = STYLES.style_defaults_for_ui(style_key)
|
| 1068 |
+
# Ensure fields exist even if style key missing
|
| 1069 |
+
new_bpm = int(defaults.get("bpm", current_bpm or 120))
|
| 1070 |
+
new_drum = str(defaults.get("drum_beat", "none"))
|
| 1071 |
+
new_synth = str(defaults.get("synthesizer", "none"))
|
| 1072 |
+
new_steps = str(defaults.get("rhythmic_steps", "none"))
|
| 1073 |
+
new_bass = str(defaults.get("bass_style", "none"))
|
| 1074 |
+
new_guitar = str(defaults.get("guitar_style", "none"))
|
| 1075 |
+
|
| 1076 |
+
# Build a prompt using these chosen defaults so text matches controls
|
| 1077 |
+
prompt_txt = STYLES.build_prompt(
|
| 1078 |
+
style_key,
|
| 1079 |
+
new_bpm,
|
| 1080 |
+
1,
|
| 1081 |
+
new_drum,
|
| 1082 |
+
new_synth,
|
| 1083 |
+
new_steps,
|
| 1084 |
+
new_bass,
|
| 1085 |
+
new_guitar
|
| 1086 |
+
)
|
| 1087 |
+
if not prompt_txt:
|
| 1088 |
+
prompt_txt = f"{style_key}: update prompts.ini"
|
| 1089 |
|
| 1090 |
+
return (
|
| 1091 |
+
prompt_txt, # Instrumental Prompt
|
| 1092 |
+
new_bpm, # BPM slider
|
| 1093 |
+
new_drum, # Drum Beat dropdown
|
| 1094 |
+
new_synth, # Synth dropdown
|
| 1095 |
+
new_steps, # Rhythmic Steps dropdown
|
| 1096 |
+
new_bass, # Bass Style dropdown
|
| 1097 |
+
new_guitar # Guitar Style dropdown
|
| 1098 |
+
)
|
| 1099 |
+
|
| 1100 |
+
# Wire all style buttons to update BOTH the prompt and the key UI settings
|
| 1101 |
for key, btn in row1 + row2 + row3 + row4:
|
| 1102 |
if key == "foo_pad":
|
| 1103 |
continue
|
| 1104 |
btn.click(
|
| 1105 |
+
set_prompt_and_settings_from_style,
|
| 1106 |
inputs=[gr.State(key), bpm, drum_beat, synthesizer, rhythmic_steps, bass_style, guitar_style],
|
| 1107 |
+
outputs=[instrumental_prompt, bpm, drum_beat, synthesizer, rhythmic_steps, bass_style, guitar_style]
|
| 1108 |
)
|
| 1109 |
|
| 1110 |
# Quick-sets
|
|
|
|
| 1205 |
load_btn.click(
|
| 1206 |
_load_action,
|
| 1207 |
outputs=[
|
| 1208 |
+
instrumental_prompt, cfg_scale, top_k, top_p, temperature, total_duration, bpm,
|
| 1209 |
+
drum_eat, synthesizer, rhythmic_steps, bass_style, guitar_style, target_volume,
|
| 1210 |
+
preset, max_steps, bitrate_state, sample_rate_state, bit_depth_state, status_box
|
| 1211 |
+
] if False else [
|
| 1212 |
instrumental_prompt, cfg_scale, top_k, top_p, temperature, total_duration, bpm,
|
| 1213 |
drum_beat, synthesizer, rhythmic_steps, bass_style, guitar_style, target_volume,
|
| 1214 |
preset, max_steps, bitrate_state, sample_rate_state, bit_depth_state, status_box
|
|
|
|
| 1224 |
]
|
| 1225 |
)
|
| 1226 |
|
|
|
|
| 1227 |
def _get_log():
|
| 1228 |
try:
|
| 1229 |
return LOG_FILE.read_text(encoding="utf-8")[-40000:]
|