SongGenerationz / app.py
Novix's picture
Update app.py
acfc75a verified
Raw
History Blame Contribute Delete
9.43 kB
import gradio as gr
import json
from datetime import datetime
import yaml
import time
import re
import os
import os.path as op
import torch
import soundfile as sf
import numpy as np
import tempfile
from download import download_model
# 🎯 خطوة السيادة الأولى: توجيه المحرك لتحميل أوزانكِ الخاصة بدلاً من المستودع العام
APP_DIR = op.dirname(op.abspath(__file__))
MODEL_ID = "Novix/SongGenerationtwo" # مستودع أوزانكِ السيادية
print(f"⏳ [Novix Core] جاري سحب الأوزان السيادية من المستودع: {MODEL_ID}...")
try:
# تحميل الأوزان مباشرة إلى البيئة المحلية للـ Space
download_model(APP_DIR, repo_id=MODEL_ID, revision="main")
print("✅ تم تحميل الأوزان السيادية لـ Novix بنجاح.")
except Exception as e:
print(f"⚠️ تنبيه أثناء تحميل الأوزان: {e}. سيتم الاعتماد على الأوزان المحلية إن وجدت.")
# تهيئة وإقلاع المحرك من الفئة الأصلية المستقرة
from levo_inference import LeVoInference
MODEL = None
EXAMPLE_LYRICS = """
[intro-medium]
[verse]
随风去流浪
我不想停留原地
原地只有无尽循环
不再规划人生
不再遵循地图
[chorus]
让我随风去流浪
邂逅未知的自己
生命最绚烂的章节
""".strip()
# قراءة قاموس التوكنز الصوتي الأصلي للموديل
vocab_path = op.join(APP_DIR, 'conf/vocab.yaml')
if op.exists(vocab_path):
with open(vocab_path, 'r', encoding='utf-8') as file:
STRUCTS = yaml.safe_load(file)
else:
STRUCTS = ['[intro]', '[intro-short]', '[intro-medium]', '[verse]', '[chorus]', '[bridge]', '[inst]', '[inst-short]', '[inst-medium]', '[outro]', '[outro-short]', '[outro-medium]']
def save_as_flac(sample_rate, audio_data):
if isinstance(audio_data, tuple):
sample_rate, audio_data = audio_data
if audio_data.dtype == np.float64:
audio_data = audio_data.astype(np.float32)
temp_file = tempfile.NamedTemporaryFile(delete=False, suffix=".flac")
sf.write(temp_file, audio_data, sample_rate, format='FLAC')
return temp_file.name
# دالة التوليد الأصلية بعد ربطها بـ Novix Core
def generate_song(lyric, description=None, prompt_audio=None, genre=None, cfg_coef=None, temperature=0.1, top_k=-1, gen_type="mixed", progress=gr.Progress(track_tqdm=True)):
global MODEL
global STRUCTS
if MODEL is None:
return None, json.dumps({"error": "المحرك لم يتم تحميله في الذاكرة بعد. يرجى إعادة تحديث الصفحة أو مراجعة السجلات."})
params = {'cfg_coef': cfg_coef, 'temperature': temperature, 'top_k': top_k}
params = {k: v for k, v in params.items() if v is not None}
vocal_structs = ['[verse]', '[chorus]', '[bridge]']
sample_rate = MODEL.cfg.sample_rate
# تنسيق وتطهير الكلمات والمقاطع الهيكلية
lyric = lyric.replace("[intro]", "[intro-short]").replace("[inst]", "[inst-short]").replace("[outro]", "[outro-short]")
paragraphs = [p.strip() for p in lyric.strip().split('\n\n') if p.strip()]
if len(paragraphs) < 1:
return None, json.dumps("Lyrics can not be left blank")
paragraphs_norm = []
vocal_flag = False
for para in paragraphs:
lines = para.splitlines()
struct_tag = lines[0].strip().lower()
if struct_tag not in STRUCTS:
return None, json.dumps(f"Segments should start with a structure tag in {STRUCTS}")
if struct_tag in vocal_structs:
vocal_flag = True
if len(lines) < 2 or not [line.strip() for line in lines[1:] if line.strip()]:
return None, json.dumps("The following segments require lyrics: [verse], [chorus], [bridge]")
else:
new_para_list = []
for line in lines[1:]:
new_para_list.append(re.sub(r"[^\w\s\[\]\-\u4e00-\u9fff\u3040-\u309f\u30a0-\u30ff\uac00-\ud7af\u00c0-\u017f]", "", line))
new_para_str = f"{struct_tag} {'.'.join(new_para_list)}"
else:
if len(lines) > 1:
return None, json.dumps("The following segments should not contain lyrics: [intro], [intro-short], [intro-medium], [inst], [inst-short], [inst-medium], [outro], [outro-short], [outro-medium]")
else:
new_para_str = struct_tag
paragraphs_norm.append(new_para_str)
if not vocal_flag:
return None, json.dumps(f"The lyric must contain at least one of the following structures: {vocal_structs}")
lyric_norm = " ; ".join(paragraphs_norm)
if prompt_audio is not None:
genre = None
description = None
elif description is not None and description != "":
genre = None
if description[-1] != ".":
description = description + "."
progress(0.0, "⚡ [Novix Core] التوليد مستمر الآن...")
start = time.time()
# تشغيل المصفوفات الحقيقية للأوزان المستقلة
prompt_path = op.join(APP_DIR, "tools/new_prompt.pt")
audio_data = MODEL(lyric_norm, description, prompt_audio, genre, prompt_path, gen_type, params).cpu().permute(1, 0).float().numpy()
end = time.time()
input_config = {
"lyric": lyric_norm,
"genre": genre,
"prompt_audio": prompt_audio,
"description": description,
"params": params,
"inference_duration": end - start,
"timestamp": datetime.now().isoformat(),
"engine": "Novix Sovereign Studio (Independent Mode)"
}
filepath = save_as_flac(sample_rate, audio_data)
return filepath, json.dumps(input_config, indent=2)
# بناء الواجهة الاحترافية الكبرى لـ Gradio
with gr.Blocks(title="Novix Sovereign Studio Pro") as demo:
gr.Markdown("# 🎵 استوديو Novix المستقل والمملوك لك بالكامل 100%")
gr.Markdown("🛡️ تم فك الارتباط من خوادم الشركات وتوجيه النواة لأوزانكِ الخاصة للإنتاج والربح الحر.")
with gr.Row():
with gr.Column():
lyric = gr.Textbox(
label="Lyrics",
lines=5,
max_lines=15,
value=EXAMPLE_LYRICS,
info="قوالب المقاطع الصوتية المدعومة: [intro], [verse], [chorus], [bridge], [inst], [outro]"
)
with gr.Tabs(elem_id="extra-tabs"):
with gr.Tab("Genre Select"):
genre = gr.Radio(
choices=["Auto", "Pop", "Latin", "Rock", "Electronic", "Metal", "Country", "R&B/Soul", "Ballad", "Jazz", "World", "Hip-Hop", "Funk", "Soundtrack"],
label="Genre Select (Optional)",
value="Auto",
interactive=True
)
with gr.Tab("Text Prompt"):
description = gr.Textbox(
label="Song Description (Optional)",
info="اكتبي مواصفات الصوت بالأرقام أو الإنجليزية (مثال: female, sad pop, piano).",
placeholder="female, rock, motivational, electric guitar, bass guitar, drum kit.",
lines=1,
max_lines=2
)
with gr.Tab("Audio Prompt"):
prompt_audio = gr.Audio(
label="Prompt Audio (Optional)",
type="filepath"
)
with gr.Accordion("Advanced Config", open=False):
cfg_coef = gr.Slider(label="CFG Coefficient", minimum=0.1, maximum=3.0, step=0.1, value=1.8, interactive=True)
temperature = gr.Slider(label="Temperature", minimum=0.1, maximum=2.0, step=0.1, value=0.8, interactive=True)
with gr.Row():
generate_btn = gr.Button("Generate Song (Sovereign Mode)", variant="primary")
with gr.Column():
output_audio = gr.Audio(label="Generated Song", type="filepath")
output_json = gr.JSON(label="Generated Info")
generate_btn.click(
fn=generate_song,
inputs=[lyric, description, prompt_audio, genre, cfg_coef, temperature, gr.State(5000)],
outputs=[output_audio, output_json]
)
# تشغيل الإقلاع المستقل للنواة
if __name__ == "__main__":
torch.set_num_threads(1)
ckpt_path = op.join(APP_DIR, "ckpt")
# التأكد من وجود مجلد الأوزان وإقلاع النموذج فوراً
if not op.exists(ckpt_path):
os.makedirs(ckpt_path, exist_ok=True)
print("🧠 جاري صهر وبناء بيئة الاستدلال الصوتي المستقل...")
MODEL = LeVoInference(ckpt_path)
print("✅ النصر الكلي! الاستوديو شغال أوفلاين وجاهز لاستقبال ضربة زر التوليد.")
demo.launch(server_name="0.0.0.0", server_port=7860)