Spaces:
Running
on
Zero
Running
on
Zero
Update app.py
Browse files
app.py
CHANGED
|
@@ -5,6 +5,12 @@ This app provides a simple interface for generating symbolic music using NotaGen
|
|
| 5 |
It accepts three parameters (period, composer, instrumentation) and returns ABC notation.
|
| 6 |
|
| 7 |
The ABC notation can then be processed by WeaveMuse locally for XML/PDF conversion.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 8 |
"""
|
| 9 |
|
| 10 |
import gradio as gr
|
|
@@ -12,19 +18,145 @@ import spaces
|
|
| 12 |
import torch
|
| 13 |
import logging
|
| 14 |
import traceback
|
| 15 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 16 |
|
| 17 |
-
# Set up logging
|
| 18 |
logging.basicConfig(level=logging.INFO)
|
| 19 |
logger = logging.getLogger(__name__)
|
| 20 |
|
| 21 |
-
#
|
| 22 |
-
#
|
|
|
|
|
|
|
|
|
|
|
|
|
| 23 |
device = "cuda"
|
| 24 |
-
logger.info(f"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 25 |
|
| 26 |
try:
|
| 27 |
-
notagen_tool =
|
| 28 |
logger.info("✅ NotaGen tool initialized successfully!")
|
| 29 |
except Exception as e:
|
| 30 |
logger.error(f"❌ Failed to initialize NotaGen tool: {e}")
|
|
@@ -32,11 +164,20 @@ except Exception as e:
|
|
| 32 |
notagen_tool = None
|
| 33 |
|
| 34 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 35 |
@spaces.GPU(duration=120)
|
| 36 |
def generate_abc(period: str, composer: str, instrumentation: str) -> str:
|
| 37 |
"""
|
| 38 |
Generate ABC notation using NotaGen.
|
| 39 |
|
|
|
|
|
|
|
|
|
|
| 40 |
Args:
|
| 41 |
period: Musical period (e.g., Baroque, Classical, Romantic)
|
| 42 |
composer: Composer style (e.g., Bach, Mozart, Chopin)
|
|
@@ -54,31 +195,14 @@ def generate_abc(period: str, composer: str, instrumentation: str) -> str:
|
|
| 54 |
logger.info(f"Generating ABC for: {period}-{composer}-{instrumentation}")
|
| 55 |
|
| 56 |
# Call the NotaGen tool's forward method
|
| 57 |
-
#
|
| 58 |
result = notagen_tool.forward(
|
| 59 |
period=period,
|
| 60 |
composer=composer,
|
| 61 |
instrumentation=instrumentation
|
| 62 |
-
)
|
| 63 |
|
| 64 |
-
|
| 65 |
-
# The result contains file paths and other info, but we only need the ABC
|
| 66 |
-
if "Error:" in result:
|
| 67 |
-
logger.error(f"Generation error: {result}")
|
| 68 |
-
return result
|
| 69 |
-
|
| 70 |
-
# Parse the result to extract ABC content
|
| 71 |
-
# The result format is: "Successfully generated...\n\nABC notation preview:\n```\n{abc}```"
|
| 72 |
-
if "ABC notation preview:" in result:
|
| 73 |
-
abc_start = result.find("```\n") + 4
|
| 74 |
-
abc_end = result.rfind("\n```")
|
| 75 |
-
if abc_start > 3 and abc_end > abc_start:
|
| 76 |
-
abc_content = result[abc_start:abc_end]
|
| 77 |
-
logger.info(f"✅ Successfully generated ABC notation ({len(abc_content)} chars)")
|
| 78 |
-
return abc_content
|
| 79 |
-
|
| 80 |
-
# If we can't parse it, return the full result
|
| 81 |
-
logger.warning("Could not parse ABC from result, returning full result")
|
| 82 |
return result
|
| 83 |
|
| 84 |
except Exception as e:
|
|
@@ -170,8 +294,4 @@ with gr.Blocks(title="NotaGen - Symbolic Music Generation") as demo:
|
|
| 170 |
|
| 171 |
# Launch the app
|
| 172 |
if __name__ == "__main__":
|
| 173 |
-
demo.launch(
|
| 174 |
-
server_name="0.0.0.0", # Allow external access
|
| 175 |
-
server_port=7860, # Default HF Spaces port
|
| 176 |
-
share=False # No need to share, HF handles this
|
| 177 |
-
)
|
|
|
|
| 5 |
It accepts three parameters (period, composer, instrumentation) and returns ABC notation.
|
| 6 |
|
| 7 |
The ABC notation can then be processed by WeaveMuse locally for XML/PDF conversion.
|
| 8 |
+
|
| 9 |
+
IMPORTANT: Zero GPU Strategy
|
| 10 |
+
----------------------------
|
| 11 |
+
- Model initialization and weight downloading happen OUTSIDE @spaces.GPU decorated functions
|
| 12 |
+
- Only the actual inference happens inside the GPU-allocated function
|
| 13 |
+
- This ensures efficient GPU usage (only during inference, not during setup)
|
| 14 |
"""
|
| 15 |
|
| 16 |
import gradio as gr
|
|
|
|
| 18 |
import torch
|
| 19 |
import logging
|
| 20 |
import traceback
|
| 21 |
+
import os
|
| 22 |
+
from smolagents import Tool
|
| 23 |
+
from typing import Optional
|
| 24 |
+
from weavemuse.models.notagen.inference import inference_patch, download_model_weights
|
| 25 |
+
|
| 26 |
|
|
|
|
| 27 |
logging.basicConfig(level=logging.INFO)
|
| 28 |
logger = logging.getLogger(__name__)
|
| 29 |
|
| 30 |
+
# ============================================================================
|
| 31 |
+
# INITIALIZATION PHASE (Outside GPU allocation)
|
| 32 |
+
# ============================================================================
|
| 33 |
+
# Download model weights and prepare everything BEFORE GPU functions are called
|
| 34 |
+
# This ensures GPU is only used for actual inference, not for setup
|
| 35 |
+
|
| 36 |
device = "cuda"
|
| 37 |
+
logger.info(f"Preparing NotaGen tool on device: {device}")
|
| 38 |
+
|
| 39 |
+
|
| 40 |
+
class SimpleNotaGenTool(Tool):
|
| 41 |
+
"""
|
| 42 |
+
Simple tool for symbolic music generation using NotaGen model.
|
| 43 |
+
|
| 44 |
+
This tool can:
|
| 45 |
+
- Generate ABC notation from text prompts
|
| 46 |
+
- Create music in specific styles and periods
|
| 47 |
+
- Generate compositions for specified instrumentation
|
| 48 |
+
- Handle conditional generation with period-composer-instrumentation prompts
|
| 49 |
+
- Convert ABC to XML, PDF, MIDI, and MP3 formats
|
| 50 |
+
- Generate PDF images for visual display
|
| 51 |
+
|
| 52 |
+
Note: This is a simplified version without VRAM management.
|
| 53 |
+
"""
|
| 54 |
+
|
| 55 |
+
# Class attributes required by smolagents
|
| 56 |
+
name = "notagen"
|
| 57 |
+
description = (
|
| 58 |
+
"Generates symbolic music in ABC notation format with full conversion capabilities. "
|
| 59 |
+
"Can create compositions only accepts three parameters: musical period, composer, and instrumentation (Use Piano for better results). "
|
| 60 |
+
"composers, and instrumentation. Supports conditional generation with format: "
|
| 61 |
+
"'Period-Composer-Instrumentation' (e.g., 'Romantic-Chopin-Piano'). "
|
| 62 |
+
"Automatically converts to various formats including PDF for visual display."
|
| 63 |
+
)
|
| 64 |
+
inputs = {
|
| 65 |
+
"period": {
|
| 66 |
+
"type": "string",
|
| 67 |
+
"description": "Musical period (e.g., Baroque, Classical, Romantic)",
|
| 68 |
+
},
|
| 69 |
+
"composer": {
|
| 70 |
+
"type": "string",
|
| 71 |
+
"description": "Composer style to emulate (e.g., Bach, Mozart, Chopin)",
|
| 72 |
+
},
|
| 73 |
+
"instrumentation": {
|
| 74 |
+
"type": "string",
|
| 75 |
+
"description": "Instruments to use (e.g., Piano, Violin, Orchestra)",
|
| 76 |
+
}
|
| 77 |
+
}
|
| 78 |
+
output_type = "string"
|
| 79 |
+
|
| 80 |
+
def __init__(
|
| 81 |
+
self,
|
| 82 |
+
device: str = "auto",
|
| 83 |
+
model_id: str = "manoskary/NotaGenX-Quantized",
|
| 84 |
+
output_dir: Optional[str] = None,
|
| 85 |
+
**kwargs
|
| 86 |
+
):
|
| 87 |
+
"""
|
| 88 |
+
Initialize NotaGen tool.
|
| 89 |
+
|
| 90 |
+
Args:
|
| 91 |
+
device: Device to run on ("auto", "cuda", "cpu")
|
| 92 |
+
model_id: NotaGen model ID
|
| 93 |
+
output_dir: Directory for output files
|
| 94 |
+
**kwargs: Additional arguments
|
| 95 |
+
"""
|
| 96 |
+
|
| 97 |
+
# NotaGen is smaller model, estimate VRAM usage
|
| 98 |
+
estimated_vram = 2000.0
|
| 99 |
+
|
| 100 |
+
super().__init__()
|
| 101 |
+
|
| 102 |
+
self.output_dir = output_dir or "/tmp/notagen_output"
|
| 103 |
+
self.model_id = model_id
|
| 104 |
+
self.device = device
|
| 105 |
+
|
| 106 |
+
# Download model weights during initialization (outside GPU function)
|
| 107 |
+
self.download_model_weights(repo_id=model_id)
|
| 108 |
+
|
| 109 |
+
os.makedirs(self.output_dir, exist_ok=True)
|
| 110 |
+
logger.info(f"Simple NotaGen tool initialized")
|
| 111 |
+
|
| 112 |
+
def forward(self, period: str, composer: str, instrumentation: str) -> str:
|
| 113 |
+
"""
|
| 114 |
+
Generate symbolic music using NotaGen.
|
| 115 |
+
|
| 116 |
+
Args:
|
| 117 |
+
period: Musical period (e.g., Baroque, Classical, Romantic)
|
| 118 |
+
composer: Composer style to emulate (e.g., Bach, Mozart, Chopin)
|
| 119 |
+
instrumentation: Instruments to use (e.g., Piano, Violin, Orchestra)
|
| 120 |
+
|
| 121 |
+
Returns:
|
| 122 |
+
Path to generated ABC file or error message
|
| 123 |
+
"""
|
| 124 |
+
logger.info(f"Generating music: {period}-{composer}-{instrumentation}")
|
| 125 |
+
|
| 126 |
+
# Create prompt for NotaGen
|
| 127 |
+
prompt = f"{period}-{composer}-{instrumentation}"
|
| 128 |
+
|
| 129 |
+
# Use the inference function
|
| 130 |
+
inference_fn = inference_patch
|
| 131 |
+
if inference_fn is None:
|
| 132 |
+
raise ImportError("inference_patch not available")
|
| 133 |
+
|
| 134 |
+
# Generate ABC notation (placeholder implementation)
|
| 135 |
+
abc_content = inference_fn(period, composer, instrumentation)
|
| 136 |
+
return abc_content
|
| 137 |
+
|
| 138 |
+
def download_model_weights(self, repo_id="manoskary/NotaGenX"):
|
| 139 |
+
"""
|
| 140 |
+
Download model weights from HuggingFace.
|
| 141 |
+
|
| 142 |
+
Args:
|
| 143 |
+
repo_id: Repository ID on HuggingFace
|
| 144 |
+
"""
|
| 145 |
+
download_model_weights(repo_id=repo_id)
|
| 146 |
+
logger.info("✅ NotaGen model weights downloaded successfully!")
|
| 147 |
+
|
| 148 |
+
|
| 149 |
+
# ============================================================================
|
| 150 |
+
# TOOL INITIALIZATION (Outside GPU allocation)
|
| 151 |
+
# ============================================================================
|
| 152 |
+
# Initialize the tool and download model weights BEFORE the GPU function is called
|
| 153 |
+
# This ensures:
|
| 154 |
+
# 1. Model weights are downloaded once at startup (not during every inference)
|
| 155 |
+
# 2. GPU is only allocated for actual inference, not for downloading/setup
|
| 156 |
+
# 3. Zero GPU is used efficiently (shorter GPU allocation times)
|
| 157 |
|
| 158 |
try:
|
| 159 |
+
notagen_tool = SimpleNotaGenTool(device=device)
|
| 160 |
logger.info("✅ NotaGen tool initialized successfully!")
|
| 161 |
except Exception as e:
|
| 162 |
logger.error(f"❌ Failed to initialize NotaGen tool: {e}")
|
|
|
|
| 164 |
notagen_tool = None
|
| 165 |
|
| 166 |
|
| 167 |
+
# ============================================================================
|
| 168 |
+
# GPU-ALLOCATED INFERENCE FUNCTION
|
| 169 |
+
# ============================================================================
|
| 170 |
+
# This function is decorated with @spaces.GPU to request GPU only during execution
|
| 171 |
+
# Model is already loaded, so GPU time is minimal and efficient
|
| 172 |
+
|
| 173 |
@spaces.GPU(duration=120)
|
| 174 |
def generate_abc(period: str, composer: str, instrumentation: str) -> str:
|
| 175 |
"""
|
| 176 |
Generate ABC notation using NotaGen.
|
| 177 |
|
| 178 |
+
This function is decorated with @spaces.GPU to allocate GPU only during inference.
|
| 179 |
+
Model weights are already downloaded and prepared outside this function.
|
| 180 |
+
|
| 181 |
Args:
|
| 182 |
period: Musical period (e.g., Baroque, Classical, Romantic)
|
| 183 |
composer: Composer style (e.g., Bach, Mozart, Chopin)
|
|
|
|
| 195 |
logger.info(f"Generating ABC for: {period}-{composer}-{instrumentation}")
|
| 196 |
|
| 197 |
# Call the NotaGen tool's forward method
|
| 198 |
+
# Model is already loaded, this just does the inference
|
| 199 |
result = notagen_tool.forward(
|
| 200 |
period=period,
|
| 201 |
composer=composer,
|
| 202 |
instrumentation=instrumentation
|
| 203 |
+
)
|
| 204 |
|
| 205 |
+
logger.info(f"✅ Successfully generated ABC notation ({len(result)} chars)")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 206 |
return result
|
| 207 |
|
| 208 |
except Exception as e:
|
|
|
|
| 294 |
|
| 295 |
# Launch the app
|
| 296 |
if __name__ == "__main__":
|
| 297 |
+
demo.launch()
|
|
|
|
|
|
|
|
|
|
|
|