Spaces:
Runtime error
Runtime error
| 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) |