"""Hum research Space: record, choose a rendering path, hear the result."""
from __future__ import annotations
import gradio as gr
from logic import INSTRUMENTS
from pipeline import generate_all
SOLO_STYLE = (
"instrumental solo {instrument}, clear single melody, intimate dry recording, "
"natural instrumental tone"
)
ARRANGEMENT_STYLE = (
"instrumental chamber pop, {instrument} melody, acoustic bass, "
"brushed drums, soft strings, warm intimate mood"
)
DRUM_STYLE = (
"instrumental {instrument} lead melody, steady acoustic drum kit, "
"kick and snare, closed hi-hat, dry studio recording"
)
def suggest_prompts(instrument: str) -> tuple[str, str, str]:
name = INSTRUMENTS[instrument][1]
return tuple(template.format(instrument=name)
for template in (SOLO_STYLE, DRUM_STYLE, ARRANGEMENT_STYLE))
def run(audio: str | None, instrument: str, solo_style: str, drum_style: str,
arrangement_style: str, seed: int, include_yue: bool, save_recording: bool,
progress: gr.Progress = gr.Progress(track_tqdm=False)):
try:
styles = {"yue_solo": solo_style, "yue_drums": drum_style,
"yue_arrangement": arrangement_style}
for result in generate_all(audio, instrument, styles, int(seed), include_yue,
progress, save_recording=save_recording):
yield result
if result[-1]["complete"] and "recording_archive" in result[-1]["errors"]:
gr.Warning("Запись не сохранилась в приватном архиве; см. детали запуска.")
except Exception as error:
raise gr.Error(str(error)) from error
CSS = """
.gradio-container {max-width: 1000px !important; margin: auto !important;}
#hero {padding: 20px 4px 10px 4px;}
#hero h1 {font-size: clamp(2.5rem, 6vw, 4.5rem); letter-spacing: -.045em; margin: 0;}
#hero p {font-size: 1.08rem; max-width: 720px; line-height: 1.55; color: #566179;}
.note {border: 1px solid #d9e0ed; border-radius: 16px; padding: 14px 18px;
background: linear-gradient(120deg,#f5f7ff,#f1fbfc); color: #3e4b64;}
"""
with gr.Blocks(title="Hum · напев в инструмент", css=CSS, theme=gr.themes.Soft()) as demo:
gr.HTML("""
Hum ♪
Напойте фразу в микрофон или загрузите короткую запись. Один запуск
создаст все варианты: сэмплерные инструменты с исходным временем и три
эксперимента YuE2. Первые результаты появятся до окончания YuE2.
""")
with gr.Row():
with gr.Column(scale=4):
audio = gr.Audio(label="Ваш напев", sources=["microphone", "upload"],
type="filepath", format=None)
save_recording = gr.Checkbox(
label="Сохранить исходную запись в приватный архив владельца Space",
value=False)
gr.Markdown("Если включить сохранение, исходный файл с микрофона или загрузки "
"останется в приватном архиве Hugging Face после перезапуска Space. "
"Без галочки исходник остаётся лишь во временном хранилище Space "
"до очистки или перезапуска.")
instrument = gr.Dropdown(list(INSTRUMENTS), value="Флейта", label="Инструмент")
include_yue = gr.Checkbox(label="Запустить YuE2 · три генерации (включено по умолчанию)",
value=True)
with gr.Accordion("Настройки YuE2", open=False):
defaults = suggest_prompts("Флейта")
solo_style = gr.Textbox(label="Соло · style", lines=2, value=defaults[0])
drum_style = gr.Textbox(label="Мелодия + барабаны · style", lines=2,
value=defaults[1])
arrangement_style = gr.Textbox(label="Композиция · style", lines=2,
value=defaults[2])
seed = gr.Number(label="Seed", value=4096, precision=0)
go = gr.Button("Создать все варианты", variant="primary", size="lg")
gr.HTML("""Текущая цель: один инструмент и
приблизительно исходное время. Электрогитара и бас используют
записанные сэмплы; регистр баса выбирается автоматически. Обработанные версии
добавляют усиление и лёгкий эффект, но не новые ноты. Барабаны
привязаны к долям SheetSage2. YuE2 может изменить
длительность, добавить человеческие звуки или другие инструменты;
отсутствие вокала не гарантируется. Это публичный Space: записи
обрабатываются на сервере Hugging Face.
""")
with gr.Column(scale=6):
with gr.Row():
with gr.Column():
instrument_only = gr.Audio(label="Инструмент · исходный ритм",
interactive=False)
instrument_drums = gr.Audio(label="Инструмент + синтезаторные барабаны",
interactive=False)
contour = gr.Audio(label="Непрерывная высота · synth",
interactive=False)
guitar_clean = gr.Audio(label="Электрогитара · чистая",
interactive=False)
guitar_amp = gr.Audio(label="Электрогитара · усилитель и эффекты",
interactive=False)
bass_clean = gr.Audio(label="Бас · чистый",
interactive=False)
bass_amp = gr.Audio(label="Бас · тёплый усилитель",
interactive=False)
with gr.Column():
yue_solo = gr.Audio(label="YuE2 · соло", interactive=False)
yue_drums = gr.Audio(label="YuE2 · мелодия + барабаны",
interactive=False)
yue_arrangement = gr.Audio(label="YuE2 · композиция",
interactive=False)
with gr.Row():
midi = gr.File(label="Мелодия · MIDI")
drum_midi = gr.File(label="Барабаны · MIDI")
beat_lab = gr.File(label="Доли · LAB")
abc = gr.File(label="Мелодия для YuE2 · ABC")
details = gr.JSON(label="Детали запуска")
instrument.change(suggest_prompts, inputs=instrument,
outputs=[solo_style, drum_style, arrangement_style])
go.click(run, inputs=[audio, instrument, solo_style, drum_style,
arrangement_style, seed, include_yue, save_recording],
outputs=[instrument_only, instrument_drums, contour,
guitar_clean, guitar_amp, bass_clean, bass_amp,
yue_solo, yue_drums, yue_arrangement,
midi, drum_midi, beat_lab, abc, details], show_progress="full")
if __name__ == "__main__":
demo.queue(max_size=8, default_concurrency_limit=1)
demo.launch(server_name="0.0.0.0", server_port=7860, show_error=True)