Update demos/musicgen_app.py
Browse files- demos/musicgen_app.py +112 -8
demos/musicgen_app.py
CHANGED
|
@@ -30,8 +30,8 @@ torch.backends.cuda.matmul.allow_fp16_reduced_precision_reduction = False
|
|
| 30 |
torch.backends.cudnn.allow_tf32 = False
|
| 31 |
torch.backends.cudnn.deterministic = False
|
| 32 |
torch.backends.cudnn.benchmark = False
|
| 33 |
-
|
| 34 |
-
|
| 35 |
torch.set_float32_matmul_precision("highest")
|
| 36 |
|
| 37 |
class FileCleaner:
|
|
@@ -53,6 +53,103 @@ class FileCleaner:
|
|
| 53 |
|
| 54 |
file_cleaner = FileCleaner()
|
| 55 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 56 |
def convert_wav_to_mp4(wav_path, output_path=None):
|
| 57 |
"""Converts a WAV file to a waveform MP4 video using ffmpeg."""
|
| 58 |
if output_path is None:
|
|
@@ -489,9 +586,16 @@ if __name__ == '__main__':
|
|
| 489 |
if args.share:
|
| 490 |
launch_kwargs['share'] = args.share
|
| 491 |
logging.basicConfig(level=logging.INFO, stream=sys.stderr)
|
| 492 |
-
|
| 493 |
-
|
| 494 |
-
|
| 495 |
-
|
| 496 |
-
|
| 497 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 30 |
torch.backends.cudnn.allow_tf32 = False
|
| 31 |
torch.backends.cudnn.deterministic = False
|
| 32 |
torch.backends.cudnn.benchmark = False
|
| 33 |
+
torch.backends.cuda.preferred_blas_library="cublas"
|
| 34 |
+
torch.backends.cuda.preferred_linalg_library="cusolver"
|
| 35 |
torch.set_float32_matmul_precision("highest")
|
| 36 |
|
| 37 |
class FileCleaner:
|
|
|
|
| 53 |
|
| 54 |
file_cleaner = FileCleaner()
|
| 55 |
|
| 56 |
+
|
| 57 |
+
# We'll load a model globally to be shared by our new UI tab
|
| 58 |
+
# This simplifies things by avoiding the Predictor class for this specific feature.
|
| 59 |
+
print("Loading model for segmented generation...")
|
| 60 |
+
MODEL = MusicGen.get_pretrained("facebook/musicgen-melody", device='cuda')
|
| 61 |
+
print("Model loaded.")
|
| 62 |
+
|
| 63 |
+
@spaces.GPU(duration=90)
|
| 64 |
+
def predict_segment(prompt, seed, current_segment, duration_per_segment):
|
| 65 |
+
"""
|
| 66 |
+
This is the core prediction function for our new UI tab.
|
| 67 |
+
It calls the generate_segment method we created earlier.
|
| 68 |
+
"""
|
| 69 |
+
print(f"Requesting to generate segment {current_segment} with seed {seed}.")
|
| 70 |
+
# Convert duration to token length for the model
|
| 71 |
+
# Note: The actual frame rate might differ, but this is a close approximation.
|
| 72 |
+
max_segment_len = int(duration_per_segment * 50)
|
| 73 |
+
|
| 74 |
+
# If the seed is -1 or None, it means we should start with a random seed.
|
| 75 |
+
# The generate_segment function handles the logic of creating a new seed for segment 1.
|
| 76 |
+
seed_to_use = None if seed == -1 else int(seed)
|
| 77 |
+
|
| 78 |
+
# Call the method we added to the LMModel
|
| 79 |
+
# Note the path: MODEL.lm.generate_segment
|
| 80 |
+
full_codes, updated_seed = MODEL.lm.generate_segment(
|
| 81 |
+
segment=int(current_segment),
|
| 82 |
+
seed=seed_to_use,
|
| 83 |
+
prompt_text=prompt,
|
| 84 |
+
max_segment_len=max_segment_len,
|
| 85 |
+
# You can pass other generation params here if you add them to the UI
|
| 86 |
+
temp=1.0,
|
| 87 |
+
top_k=250,
|
| 88 |
+
cfg_coef=3.0,
|
| 89 |
+
)
|
| 90 |
+
|
| 91 |
+
# Decode the *entire* audio generated so far to a waveform
|
| 92 |
+
audio_wav = MODEL.compression_model.decode(full_codes.to('cuda'))
|
| 93 |
+
audio_wav = audio_wav.detach().cpu()
|
| 94 |
+
|
| 95 |
+
# Save the waveform to a temporary file for the Gradio Audio component
|
| 96 |
+
with NamedTemporaryFile("wb", suffix=".wav", delete=False) as file:
|
| 97 |
+
audio_write(
|
| 98 |
+
file.name, audio_wav[0], MODEL.sample_rate, strategy="loudness",
|
| 99 |
+
loudness_headroom_db=16, loudness_compressor=True, add_suffix=False
|
| 100 |
+
)
|
| 101 |
+
file_cleaner.add(file.name)
|
| 102 |
+
|
| 103 |
+
# We must return values for all the outputs of the button's .click() event:
|
| 104 |
+
# 1. The audio file path for the gr.Audio component.
|
| 105 |
+
# 2. The updated seed for the gr.Number component.
|
| 106 |
+
# 3. The next segment number (current + 1) for the gr.Number state.
|
| 107 |
+
# 4. A new label for the button to guide the user.
|
| 108 |
+
return file.name, updated_seed, current_segment + 1, gr.update(value=f"Generate Segment {current_segment + 1}")
|
| 109 |
+
|
| 110 |
+
|
| 111 |
+
def reset_segmented_ui():
|
| 112 |
+
"""Resets the UI state for starting a new song."""
|
| 113 |
+
return None, -1, 1, gr.update(value="Generate Segment 1")
|
| 114 |
+
|
| 115 |
+
|
| 116 |
+
def ui_segmented():
|
| 117 |
+
"""Builds the Gradio UI for the new 'Long / Segmented Generation' tab."""
|
| 118 |
+
with gr.Blocks() as interface:
|
| 119 |
+
gr.Markdown("## Long / Segmented Generation\nGenerate music longer than the time limit by creating it in segments. The state is saved to a file after each segment.")
|
| 120 |
+
|
| 121 |
+
with gr.Row():
|
| 122 |
+
prompt = gr.Text(label="Prompt", placeholder="e.g., An epic cinematic orchestral score")
|
| 123 |
+
# The Seed field is critical for linking segments. We use -1 to signify a random start.
|
| 124 |
+
seed = gr.Number(value=-1, label="Seed (-1 for random start)")
|
| 125 |
+
|
| 126 |
+
duration = gr.Slider(label="Duration PER Segment (seconds)", minimum=10, maximum=90, value=30, step=5)
|
| 127 |
+
|
| 128 |
+
# This hidden Number component will keep track of which segment we're on
|
| 129 |
+
current_segment = gr.Number(value=1, visible=False)
|
| 130 |
+
|
| 131 |
+
with gr.Row():
|
| 132 |
+
run_button = gr.Button("Generate Segment 1")
|
| 133 |
+
reset_button = gr.Button("Reset & Start New Song")
|
| 134 |
+
|
| 135 |
+
audio_output = gr.Audio(label="Full Generated Music So Far", type='filepath')
|
| 136 |
+
|
| 137 |
+
# --- Wire up the buttons ---
|
| 138 |
+
run_button.click(
|
| 139 |
+
fn=predict_segment,
|
| 140 |
+
inputs=[prompt, seed, current_segment, duration],
|
| 141 |
+
# The outputs update the audio player, the seed box, the hidden segment counter, and the button label
|
| 142 |
+
outputs=[audio_output, seed, current_segment, run_button]
|
| 143 |
+
)
|
| 144 |
+
|
| 145 |
+
reset_button.click(
|
| 146 |
+
fn=reset_segmented_ui,
|
| 147 |
+
inputs=[],
|
| 148 |
+
outputs=[audio_output, seed, current_segment, run_button]
|
| 149 |
+
)
|
| 150 |
+
return interface
|
| 151 |
+
|
| 152 |
+
|
| 153 |
def convert_wav_to_mp4(wav_path, output_path=None):
|
| 154 |
"""Converts a WAV file to a waveform MP4 video using ffmpeg."""
|
| 155 |
if output_path is None:
|
|
|
|
| 586 |
if args.share:
|
| 587 |
launch_kwargs['share'] = args.share
|
| 588 |
logging.basicConfig(level=logging.INFO, stream=sys.stderr)
|
| 589 |
+
|
| 590 |
+
# Build the two interfaces
|
| 591 |
+
original_interface = ui_full({})
|
| 592 |
+
segmented_interface = ui_segmented()
|
| 593 |
+
|
| 594 |
+
# Combine them in a tabbed layout
|
| 595 |
+
tabbed_interface = gr.TabbedInterface(
|
| 596 |
+
[original_interface, segmented_interface],
|
| 597 |
+
["Single Generation", "Long / Segmented Generation"]
|
| 598 |
+
)
|
| 599 |
+
|
| 600 |
+
# Launch the final combined app
|
| 601 |
+
tabbed_interface.queue().launch(**launch_kwargs)
|