|
import gradio as gr
|
|
import os
|
|
from predict import predict_healing_music
|
|
import train_model
|
|
import logging
|
|
import tempfile
|
|
import time
|
|
import shutil
|
|
import socket
|
|
import joblib
|
|
|
|
|
|
logging.basicConfig(
|
|
level=logging.INFO,
|
|
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
|
|
)
|
|
logger = logging.getLogger(__name__)
|
|
|
|
def find_free_port(start_port=7860, max_port=7960):
|
|
"""Find a free port in the given range."""
|
|
for port in range(start_port, max_port + 1):
|
|
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
|
|
try:
|
|
s.bind(('', port))
|
|
return port
|
|
except OSError:
|
|
continue
|
|
return None
|
|
|
|
|
|
model_dir = os.path.join(os.path.dirname(__file__), "models")
|
|
os.makedirs(model_dir, exist_ok=True)
|
|
|
|
|
|
model_path = os.path.join(model_dir, "model.joblib")
|
|
scaler_path = os.path.join(model_dir, "scaler.joblib")
|
|
|
|
|
|
if not os.path.exists(model_path) or not os.path.exists(scaler_path):
|
|
print('First run: Training the model...')
|
|
try:
|
|
train_model.train_and_evaluate_model()
|
|
print('Model training completed!')
|
|
except Exception as e:
|
|
print(f'Model training failed: {str(e)}')
|
|
raise e
|
|
|
|
def process_audio(audio_path):
|
|
"""
|
|
Process and analyze the audio file
|
|
"""
|
|
if audio_path is None:
|
|
return None, None, None, "Please upload an audio file"
|
|
|
|
model_dir = os.path.join(os.path.dirname(__file__), "models")
|
|
model_path = os.path.join(model_dir, "model.joblib")
|
|
scaler_path = os.path.join(model_dir, "scaler.joblib")
|
|
|
|
try:
|
|
|
|
model = joblib.load(model_path)
|
|
scaler = joblib.load(scaler_path)
|
|
|
|
tmp_file = None
|
|
try:
|
|
|
|
suffix = os.path.splitext(audio_path)[1]
|
|
tmp_file = tempfile.NamedTemporaryFile(delete=False, suffix=suffix)
|
|
shutil.copy2(audio_path, tmp_file.name)
|
|
|
|
|
|
healing_probability = predict_healing_music(tmp_file.name)
|
|
|
|
if healing_probability is not None:
|
|
|
|
healing_percentage = healing_probability * 100
|
|
|
|
|
|
if healing_percentage >= 75:
|
|
description = "This music has strong healing properties! ✨"
|
|
color = "#15803d"
|
|
elif healing_percentage >= 50:
|
|
description = "This music has moderate healing effects. 🌟"
|
|
color = "#0369a1"
|
|
else:
|
|
description = "This music has limited healing potential. 🎵"
|
|
color = "#b91c1c"
|
|
|
|
return f"{healing_percentage:.1f}%", f'<div style="background-color: {color}; color: white; padding: 1rem; border-radius: 8px; text-align: center;">{description}</div>', None, None
|
|
else:
|
|
return "Error", "Error analyzing file. Please ensure it's a valid MP3 or WAV file.", None, None
|
|
|
|
except Exception as e:
|
|
logger.error(f"Error during analysis: {str(e)}")
|
|
return "Error", f"An unexpected error occurred: {str(e)}", None, None
|
|
|
|
finally:
|
|
|
|
if tmp_file is not None:
|
|
try:
|
|
tmp_file.close()
|
|
os.unlink(tmp_file.name)
|
|
except Exception as e:
|
|
logger.error(f"Failed to clean up temporary file: {str(e)}")
|
|
|
|
except Exception as e:
|
|
logger.error(f"Error during model loading: {str(e)}")
|
|
return "Error", f"An unexpected error occurred: {str(e)}", None, None
|
|
|
|
def analyze_audio(audio):
|
|
"""Analyze the audio file"""
|
|
try:
|
|
if audio is None:
|
|
return [
|
|
gr.update(visible=False),
|
|
gr.update(visible=False),
|
|
"",
|
|
""
|
|
]
|
|
|
|
|
|
yield [
|
|
gr.update(visible=False),
|
|
gr.update(visible=True),
|
|
"",
|
|
""
|
|
]
|
|
|
|
|
|
index, desc, _, _ = process_audio(audio)
|
|
desc_with_hint = f'{desc}<div style="margin-top: 1rem; color: #9ca3af; font-size: 0.9rem;">To analyze another file, please refresh the page</div>'
|
|
|
|
|
|
yield [
|
|
gr.update(visible=True),
|
|
gr.update(visible=False),
|
|
index,
|
|
desc_with_hint
|
|
]
|
|
|
|
except Exception as e:
|
|
logger.error(f"Error in analyze_audio: {str(e)}")
|
|
yield [
|
|
gr.update(visible=True),
|
|
gr.update(visible=False),
|
|
"Error",
|
|
f"An error occurred: {str(e)}"
|
|
]
|
|
|
|
|
|
custom_css = """
|
|
.gradio-container {
|
|
font-family: 'Inter', -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
|
|
max-width: 800px !important;
|
|
margin: auto;
|
|
padding: 0 1rem;
|
|
background-color: #0f1117;
|
|
}
|
|
.container {
|
|
max-width: 700px;
|
|
margin: 0 auto;
|
|
padding-top: 2rem;
|
|
}
|
|
.header {
|
|
text-align: center;
|
|
margin-bottom: 1rem;
|
|
width: 100%;
|
|
display: flex;
|
|
justify-content: center;
|
|
align-items: center;
|
|
}
|
|
.title {
|
|
font-size: 2.5rem !important;
|
|
font-weight: 700 !important;
|
|
color: white !important;
|
|
margin: 0 !important;
|
|
line-height: 1.2 !important;
|
|
text-align: center !important;
|
|
white-space: nowrap !important;
|
|
}
|
|
.subtitle {
|
|
font-size: 1.2rem !important;
|
|
text-align: center;
|
|
color: #9ca3af !important;
|
|
margin-top: 0.5rem !important;
|
|
max-width: 800px;
|
|
margin-left: auto;
|
|
margin-right: auto;
|
|
white-space: nowrap !important;
|
|
}
|
|
.upload-box {
|
|
background-color: #1f2937;
|
|
border-radius: 12px;
|
|
padding: 2rem;
|
|
margin-bottom: 1rem;
|
|
border: 2px dashed #374151;
|
|
transition: all 0.3s ease;
|
|
}
|
|
.upload-box:hover {
|
|
border-color: #3b82f6;
|
|
box-shadow: 0 0 10px rgba(59, 130, 246, 0.2);
|
|
}
|
|
.upload-area {
|
|
display: flex;
|
|
flex-direction: column;
|
|
align-items: center;
|
|
justify-content: center;
|
|
gap: 1rem;
|
|
padding: 1.5rem 0;
|
|
}
|
|
.icon-text-container {
|
|
display: inline-flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
gap: 0.2rem;
|
|
white-space: nowrap;
|
|
}
|
|
.upload-icon {
|
|
color: #9ca3af;
|
|
font-size: 2rem;
|
|
line-height: 1;
|
|
margin-right: 0.1rem;
|
|
}
|
|
.upload-text {
|
|
color: white;
|
|
font-size: 1.1rem;
|
|
font-weight: 500;
|
|
line-height: 1;
|
|
}
|
|
.upload-hint {
|
|
color: #6b7280 !important;
|
|
font-size: 0.85rem !important;
|
|
margin-top: 0.5rem !important;
|
|
font-style: italic !important;
|
|
}
|
|
.progress-area {
|
|
margin: 1rem 0;
|
|
background-color: #1f2937;
|
|
border-radius: 12px;
|
|
padding: 1.5rem;
|
|
text-align: center;
|
|
}
|
|
.progress-text {
|
|
color: #60a5fa !important;
|
|
font-size: 1.2rem !important;
|
|
font-weight: 500 !important;
|
|
margin: 0 !important;
|
|
}
|
|
.results-container {
|
|
background-color: #1f2937;
|
|
border-radius: 12px;
|
|
padding: 1.25rem;
|
|
margin-top: 1rem;
|
|
animation: fadeIn 0.5s ease;
|
|
}
|
|
.result-title {
|
|
color: white !important;
|
|
font-size: 1.25rem !important;
|
|
font-weight: 600 !important;
|
|
margin-bottom: 0.5rem !important;
|
|
}
|
|
.healing-index {
|
|
font-size: 2.5rem !important;
|
|
font-weight: 700 !important;
|
|
text-align: center;
|
|
color: white !important;
|
|
margin: 0.5rem 0 !important;
|
|
animation: scaleIn 0.5s ease;
|
|
}
|
|
.result-text {
|
|
animation: slideIn 0.5s ease;
|
|
}
|
|
@keyframes fadeIn {
|
|
from { opacity: 0; transform: translateY(20px); }
|
|
to { opacity: 1; transform: translateY(0); }
|
|
}
|
|
@keyframes scaleIn {
|
|
from { transform: scale(0.8); opacity: 0; }
|
|
to { transform: scale(1); opacity: 1; }
|
|
}
|
|
@keyframes slideIn {
|
|
from { transform: translateY(10px); opacity: 0; }
|
|
to { transform: translateY(0); opacity: 1; }
|
|
}
|
|
"""
|
|
|
|
|
|
with gr.Blocks(
|
|
title="Healing Music Classifier",
|
|
css=custom_css,
|
|
theme=gr.themes.Default()
|
|
) as demo:
|
|
with gr.Column(elem_classes="container"):
|
|
with gr.Row(elem_classes="header"):
|
|
gr.Markdown("🎵 Healing Music Classifier", elem_classes="title")
|
|
|
|
gr.Markdown(
|
|
"Upload your music file, and our model will analyze its healing potential!",
|
|
elem_classes="subtitle"
|
|
)
|
|
|
|
with gr.Column(elem_classes="upload-box"):
|
|
with gr.Column(elem_classes="upload-area"):
|
|
gr.Markdown("☁️ Drop your audio file here", elem_classes="icon-text-container")
|
|
audio_input = gr.Audio(
|
|
label="Audio Input",
|
|
sources=["upload"],
|
|
type="filepath",
|
|
elem_classes="audio-input",
|
|
interactive=True,
|
|
label_visible=False
|
|
)
|
|
gr.Markdown("Limit 200MB per file • MP3, WAV", elem_classes="upload-hint")
|
|
|
|
with gr.Column(elem_classes="analyzing-status", visible=False) as analyzing:
|
|
gr.Markdown(
|
|
"""<div style="display: flex; align-items: center; justify-content: center; gap: 0.5rem;">
|
|
<div class="loading-spinner"></div>
|
|
<span style="color: #60a5fa;">Analyzing your music...</span>
|
|
</div>""",
|
|
elem_classes="analyzing-text"
|
|
)
|
|
|
|
with gr.Column(elem_classes="results-container", visible=False) as results:
|
|
gr.Markdown("Analysis Results", elem_classes="result-title")
|
|
healing_index = gr.Markdown("", elem_classes="healing-index")
|
|
result_text = gr.Markdown("", elem_classes="result-text")
|
|
|
|
|
|
audio_input.upload(
|
|
fn=analyze_audio,
|
|
inputs=[audio_input],
|
|
outputs=[
|
|
results,
|
|
analyzing,
|
|
healing_index,
|
|
result_text
|
|
],
|
|
queue=True
|
|
)
|
|
|
|
|
|
demo.queue()
|
|
|
|
|
|
if __name__ == "__main__":
|
|
demo.launch()
|
|
|