Add a Mastering tab and auto-apply toggle for generated voice output

Pedalboard-based chain (warmth EQ, presence dip, compression, light
reverb, hard-ceiling limiter) fixes the spectral imbalance behind
generated TTS sounding tinny - see mastering/master.py docstring and
commit history for the before/after spectral analysis and the A/B tuning
pass against real character clips. Pipeline Studios Generate step can
auto-apply it; the standalone tab works on any clip.

Co-Authored-By: Claude Sonnet 5 <[email protected]>
This commit is contained in:
steve-adminandClaude Sonnet 5 committed 2026-10-16 11:08:53 +00:00
1 parent b4fef19993
commit 57ae9deb30
2 files changed
+178 -4

No files matched your search

+96 -4
View File
@@ -62,6 +62,21 @@ SFX_SA3_GENERATE = SFX_SA3_DIR / "generate.py"
SFX_MODEL_SA3 = "Stable Audio 3 (Small-SFX)"
SFX_MODEL_MOSS = "MOSS-SoundEffect"
# Mastering - own isolated venv (pedalboard), fixes the "tinny" spectral
# imbalance generated TTS output has (see mastering/master.py's docstring
# for the measured before/after). Defaults below match what Steve approved
# after an A/B listen on real character clips.
MASTERING_DIR = REPO_ROOT / "mastering"
MASTERING_PYTHON = MASTERING_DIR / ".venv" / "bin" / "python3"
MASTERING_SCRIPT = MASTERING_DIR / "master.py"
MASTERING_DEFAULTS = {
"warmth_db": 5.5,
"presence_cut_db": -5.0,
"air_db": -4.5,
"comp_ratio": 2.5,
"reverb_wet": 0.025,
}
MODEL_BASE = "Base (full quality)"
MODEL_FLASH = "Flash (fast draft, 4-step)"
@@ -406,7 +421,38 @@ def reset_to_original(history: list):
# ---------------------------------------------------------------------------
def generate_speech(reference: str, text: str, language: str):
def apply_mastering(
audio_path: str,
warmth_db: float = MASTERING_DEFAULTS["warmth_db"],
presence_cut_db: float = MASTERING_DEFAULTS["presence_cut_db"],
air_db: float = MASTERING_DEFAULTS["air_db"],
comp_ratio: float = MASTERING_DEFAULTS["comp_ratio"],
reverb_wet: float = MASTERING_DEFAULTS["reverb_wet"],
) -> str:
"""Run mastering/master.py's pedalboard chain (warmth EQ, presence dip,
compression, a touch of reverb, hard-ceiling limiter) on an existing
clip. Shared by the standalone Mastering tab and Pipeline Studio's
optional auto-apply-after-generation toggle."""
if not audio_path:
raise gr.Error("No audio to master yet.")
out_path = WORKDIR / f"mastered_{int(time.time())}.wav"
_run(
[
str(MASTERING_PYTHON), str(MASTERING_SCRIPT),
"--input", audio_path,
"--output", str(out_path),
"--warmth-db", str(warmth_db),
"--presence-cut-db", str(presence_cut_db),
"--air-db", str(air_db),
"--comp-ratio", str(comp_ratio),
"--reverb-wet", str(reverb_wet),
],
cwd=MASTERING_DIR,
)
return str(out_path)
def generate_speech(reference: str, text: str, language: str, master_after: bool):
if not reference:
raise gr.Error("Set a voice reference first (upload, strip, and/or edit it above).")
if not text or not text.strip():
@@ -430,8 +476,9 @@ def generate_speech(reference: str, text: str, language: str):
cwd=TTS_DIR,
env=env,
)
transcript = _whisper_transcribe(str(out_path))
return str(out_path), transcript
final_path = apply_mastering(str(out_path)) if master_after else str(out_path)
transcript = _whisper_transcribe(final_path)
return final_path, transcript
def generate_sound_effect(description: str, model_choice: str, duration: float):
@@ -838,6 +885,10 @@ with gr.Blocks(title="Audio-demo pipeline studio") as demo:
gr.Markdown("## 3. Generate (MOSS-TTS)")
gen_text = gr.Textbox(label="Text to speak", lines=6)
gen_language = gr.Dropdown(LANGUAGES, value="English", label="Language")
gen_master_cb = gr.Checkbox(
label="Apply mastering after generation (warmth/de-tin/light reverb - see Mastering tab)",
value=True,
)
generate_btn = gr.Button("Generate speech", variant="primary")
generated_audio = gr.Audio(label="Generated speech", type="filepath")
gen_transcript_box = gr.Textbox(label="Whisper transcript of generated speech")
@@ -878,7 +929,7 @@ with gr.Blocks(title="Audio-demo pipeline studio") as demo:
outputs=[current_ref_audio, history_state, history_label, ref_transcript_box],
)
generate_btn.click(
generate_speech, inputs=[current_ref_audio, gen_text, gen_language],
generate_speech, inputs=[current_ref_audio, gen_text, gen_language, gen_master_cb],
outputs=[generated_audio, gen_transcript_box],
)
use_ref_transcript_btn.click(lambda t: t, inputs=[ref_transcript_box], outputs=[gen_text])
@@ -927,6 +978,47 @@ with gr.Blocks(title="Audio-demo pipeline studio") as demo:
outputs=[sfx_save_status],
)
with gr.Tab("Mastering"):
gr.Markdown(
"# Mastering\n"
"Fixes the \"tinny\" spectral imbalance common to generated "
"TTS output (too much 3-6kHz presence, not enough low end) "
"and adds a bit of warmth/density/space. Works on any clip - "
"Pipeline Studio's Generate step can also apply this "
"automatically (see its checkbox)."
)
with gr.Row():
with gr.Column():
master_input = gr.Audio(label="Input audio", type="filepath")
master_warmth = gr.Slider(
minimum=0, maximum=10, step=0.5, value=MASTERING_DEFAULTS["warmth_db"],
label="Warmth (low-shelf boost @150Hz, dB)",
)
master_presence = gr.Slider(
minimum=-10, maximum=0, step=0.5, value=MASTERING_DEFAULTS["presence_cut_db"],
label="De-tin (presence cut @3.8kHz, dB)",
)
master_air = gr.Slider(
minimum=-10, maximum=2, step=0.5, value=MASTERING_DEFAULTS["air_db"],
label="Softness (high-shelf @6.5kHz, dB)",
)
master_comp = gr.Slider(
minimum=1, maximum=6, step=0.5, value=MASTERING_DEFAULTS["comp_ratio"],
label="Compression ratio",
)
master_reverb = gr.Slider(
minimum=0, maximum=0.2, step=0.005, value=MASTERING_DEFAULTS["reverb_wet"],
label="Reverb wet level",
)
master_btn = gr.Button("Apply mastering", variant="primary")
with gr.Column():
master_output = gr.Audio(label="Mastered output", type="filepath")
master_btn.click(
apply_mastering,
inputs=[master_input, master_warmth, master_presence, master_air, master_comp, master_reverb],
outputs=[master_output],
)
with gr.Tab("Characters"):
gr.Markdown(
f"# The Service Station cast\n"