Spaces:
Sleeping
Sleeping
File size: 7,701 Bytes
05c8865 41089ff f8bb6ec 05c8865 41089ff 05c8865 41089ff f8bb6ec 05c8865 f8bb6ec 05c8865 41089ff f8bb6ec 41089ff f8bb6ec 05c8865 41089ff f8bb6ec 41089ff f8bb6ec 41089ff f8bb6ec 41089ff f8bb6ec 41089ff f8bb6ec 41089ff f8bb6ec 41089ff f8bb6ec 41089ff 9d5b0ac 05c8865 f8bb6ec 9d5b0ac 05c8865 f8bb6ec 9d5b0ac f8bb6ec 9d5b0ac f8bb6ec 05c8865 f8bb6ec 05c8865 f8bb6ec 05c8865 f8bb6ec 05c8865 f8bb6ec 41089ff 05c8865 f8bb6ec 41089ff 05c8865 f8bb6ec 05c8865 f8bb6ec 05c8865 f8bb6ec 05c8865 f8bb6ec 05c8865 f8bb6ec 9d5b0ac 05c8865 f8bb6ec 05c8865 f8bb6ec 05c8865 41089ff 05c8865 f8bb6ec 41089ff 05c8865 f8bb6ec |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 |
import gradio as gr
from midi2audio import FluidSynth
import os
import tempfile
import zipfile
import glob
import shutil
import logging
# --- Setup pretty logging ---
# This will make console logs much cleaner and more informative
logging.basicConfig(
level=logging.INFO,
format="%(asctime)s - [%(levelname)s] - %(message)s",
datefmt="%Y-%m-%d %H:%M:%S"
)
logger = logging.getLogger(__name__)
# --- End logging setup ---
# --- Configuration ---
SOUNDFONT_FILE = "timbres_of_heaven.sf2"
EXAMPLES_ZIP = "examples.zip"
EXAMPLES_DIR = "midi_examples"
# --- End Configuration ---
def check_fluidsynth_dependency():
"""
Checks if FluidSynth is installed and returns a status message for the UI.
"""
if shutil.which("fluidsynth") is None:
logger.warning("'fluidsynth' command not found in system PATH.")
logger.warning("This app will fail if not run in the provided Docker container.")
logger.warning("If running locally, please install FluidSynth.")
return ("⚠️ **Warning:** 'fluidsynth' command not found. "
"The app may not work unless run via Docker.")
else:
logger.info("'fluidsynth' dependency check passed.")
return "✅ FluidSynth dependency check passed."
def extract_examples():
"""
Extracts examples.zip and returns a list of files and a status message.
"""
if not os.path.exists(EXAMPLES_ZIP):
logger.warning(f"'{EXAMPLES_ZIP}' not found. No examples will be loaded.")
return [], f"⚠️ **Warning:** `{EXAMPLES_ZIP}` not found. No examples will be loaded."
if not os.path.exists(EXAMPLES_DIR):
os.makedirs(EXAMPLES_DIR)
logger.info(f"Created examples directory: {EXAMPLES_DIR}")
try:
with zipfile.ZipFile(EXAMPLES_ZIP, 'r') as zip_ref:
zip_ref.extractall(EXAMPLES_DIR)
midi_files = glob.glob(os.path.join(EXAMPLES_DIR, "**/*.midi"), recursive=True)
kar_files = glob.glob(os.path.join(EXAMPLES_DIR, "**/*.kar"), recursive=True)
mid_files = glob.glob(os.path.join(EXAMPLES_DIR, "**/*.mid"), recursive=True) # <-- Added this line
example_list = midi_files + kar_files + mid_files # <-- Updated this line
example_paths = [[example] for example in example_list]
if not example_paths:
logger.warning(f"No .midi, .kar, or .mid files found in '{EXAMPLES_ZIP}'.") # <-- Updated this line
return [], f"⚠️ **Warning:** No `.midi`, `.kar`, or `.mid` files found in `{EXAMPLES_ZIP}`." # <-- Updated this line
logger.info(f"Loaded {len(example_paths)} examples from {EXAMPLES_ZIP}.")
return example_paths, f"✅ Loaded {len(example_paths)} examples from `{EXAMPLES_ZIP}`."
except zipfile.BadZipFile:
logger.error(f"'{EXAMPLES_ZIP}' is not a valid zip file.")
return [], f"⛔ **Error:** `{EXAMPLES_ZIP}` is not a valid zip file."
except Exception as e:
logger.error(f"Error extracting examples: {e}", exc_info=True)
return [], f"⛔ **Error:** Could not extract examples. See console logs."
def render_midi(midi_file_path, progress=gr.Progress(track_tqdm=True)):
"""
Renders a MIDI/KAR file to audio, now with progress updates and feedback.
The input `midi_file_path` is always a string filepath.
"""
try:
# --- 1. Validation and Setup (Progress: 0% -> 20%) ---
progress(0, desc="Starting render...")
if not os.path.exists(SOUNDFONT_FILE):
logger.error(f"SoundFont file not found at '{SOUNDFONT_FILE}'")
raise gr.Error(
f"SoundFont file not found! The app is looking for '{SOUNDFONT_FILE}'. "
f"Please add it and restart."
)
progress(0.1, desc="SoundFont found. Checking input...")
# Simplified logic: Input is always a filepath string
if isinstance(midi_file_path, str):
input_midi_path = midi_file_path
logger.info(f"Processing input file: {input_midi_path}")
else:
logger.error(f"Invalid input file type: {type(midi_file_path)}")
raise gr.Error(f"Invalid input file type: {type(midi_file_path)}")
progress(0.2, desc="Input file OK. Preparing output...")
# --- 2. Create Temp File (Progress: 20% -> 30%) ---
with tempfile.NamedTemporaryFile(suffix=".wav", delete=False) as tmp_out:
output_wav_path = tmp_out.name
progress(0.3, desc="Temporary output file created.")
# --- 3. Run FluidSynth (Progress: 30% -> 90%) ---
logger.info(f"Initializing FluidSynth with {SOUNDFONT_FILE}")
fs = FluidSynth(SOUNDFONT_FILE)
progress(0.5, desc="Rendering audio... (This may take a moment)")
# This is the main rendering step
fs.midi_to_audio(input_midi_path, output_wav_path)
logger.info(f"Audio rendered successfully to {output_wav_path}")
progress(0.9, desc="Audio rendered. Finalizing...")
# --- 4. Success and Cleanup (Progress: 90% -> 100%) ---
gr.Info("Render complete!") # Show a success pop-up
progress(1.0, desc="Done!")
return output_wav_path
except Exception as e:
# Log the full error to the console for debugging
logger.error(f"Failed to render audio: {str(e)}", exc_info=True)
# Show a user-friendly error in the UI
raise gr.Error(f"Failed to render audio. Error: {str(e)}. "
"This can happen if FluidSynth is not installed "
"or if the MIDI file is corrupt.")
# --- Run Startup Checks ---
fluidsynth_status = check_fluidsynth_dependency()
example_list, example_status = extract_examples()
initial_status_markdown = f"""
**System Status:**
- {fluidsynth_status}
- {example_status}
"""
# --- End Startup Checks ---
# --- Create the Gradio Interface ---
with gr.Blocks() as app: # <-- I've removed theme=gr.themes.Soft()
gr.Markdown(
"""
# 🎵 MIDI & KAR Audio Renderer
Upload a `.midi` or `.kar` file to synthesize it into audio
using the **Timbres of Heaven** SoundFont.
"""
)
# New Status Display
with gr.Accordion("Show System Status", open=False):
status_display = gr.Markdown(initial_status_markdown, elem_id="status-display")
with gr.Row():
with gr.Column(scale=1):
file_input = gr.File(
label="Upload .midi, .kar, or .mid file", # <-- Updated this line
file_types=[".midi", ".kar", ".mid"], # <-- Updated this line
type="filepath" # <-- Changed from "file" to "filepath"
)
submit_btn = gr.Button("Render Audio", variant="primary")
with gr.Column(scale=2):
audio_output = gr.Audio(
label="Rendered Audio",
type="filepath"
)
# Connect the button click to the render function
# Gradio automatically sees the 'progress' argument in render_midi
# and will provide the progress bar component to it.
submit_btn.click(
fn=render_midi,
inputs=file_input,
outputs=audio_output
)
gr.Examples(
examples=example_list,
inputs=file_input,
outputs=audio_output,
fn=render_midi,
cache_examples=True,
label="Click an example to render it!" if example_list else "No examples found."
)
# --- Launch the App ---
if __name__ == "__main__":
logger.info("Starting Gradio application...")
app.launch(server_name="0.0.0.0", server_port=7860) |