Spaces:
Sleeping
Sleeping
| """ | |
| ุตุงูู โ Hugging Face Space (ZeroGPU) | |
| ูุตู ุงูู ูุณููู ุนู ุงูููุงู ุจู ูุฏูู Mel-Band RoFormer (Kim) ุนูู GPU ู ุฌุงูู. | |
| โข ูุงุฌูุฉ Gradio (ุจูุณุชุฎุฏู ูุง ุงููุงุณ ู ุจุงุดุฑุฉ) | |
| โข ููู ุงู API ุชููุงุฆู (ูุงุฌูุฉ Cloudflare ุชูุฏุฑ ุชูุงุฏูู) | |
| ู ูุงุญุธุงุช ZeroGPU ุงูู ุชูุจุนุฉ ููุง: | |
| - import spaces + @spaces.GPU ุนูู ุฏุงูุฉ ุงููุตู ุจุณ | |
| - ุงูู ูุฏูู ุจูุชุญู ูู ู ุฑุฉ ูุงุญุฏุฉ ูู ุงูู global (read-only) โ ู ุด ุฌููู ุงูุฏุงูุฉ | |
| - ู ููุด ุญุงูุฉ ุนุงู ุฉ ูุงุจูุฉ ููุชุนุฏููุ ูู ุณุงุฑุงุช ุงูุฅุฎุฑุงุฌ ูุฑูุฏุฉ (tempfile) ูุฃู | |
| ุงูุทูุจุงุช ุจุชุดุชุบู ุจุงูุชูุงุฒู ุงูุชุฑุงุถูุงู | |
| - ู ููุด torch.compile (ู ู ููุน ุนูู ZeroGPU) | |
| """ | |
| import os | |
| import sys | |
| import glob | |
| import tempfile | |
| import subprocess | |
| import gradio as gr | |
| import spaces | |
| # ---------- ุฑูุนุฉ ูู bug ู ุนุฑูู ูู gradio_client (additionalProperties=bool) ---------- | |
| # ุจููุณุฑ ุจูุงุก ุงูู API schema ุจุฑุณุงูุฉ: argument of type 'bool' is not iterable | |
| import gradio_client.utils as _gcu | |
| _orig_json = _gcu._json_schema_to_python_type | |
| _orig_get_type = _gcu.get_type | |
| def _safe_get_type(schema): | |
| if not isinstance(schema, dict): | |
| return "Any" | |
| return _orig_get_type(schema) | |
| def _safe_json(schema, defs=None): | |
| if isinstance(schema, bool): | |
| return "Any" | |
| return _orig_json(schema, defs) | |
| _gcu.get_type = _safe_get_type | |
| _gcu._json_schema_to_python_type = _safe_json | |
| # ---------- ุชุญู ูู ุงูู ูุฏูู ู ุฑุฉ ูุงุญุฏุฉ (read-only global) ---------- | |
| # ูุฎููู ุญุฒู ุฉ melband ุชูุฒูู ุงูู ูุฏูู + ุงูุฅุนุฏุงุฏุงุช ุจููุณูุง (ุจุตูุบุฉ ู ุชูุงููุฉ ู ุน safe_load) | |
| import glob as _glob | |
| MODEL_SLUG = "melband-roformer-kim-vocals" | |
| MODELS_DIR = os.path.join(tempfile.gettempdir(), "safi_models") | |
| os.makedirs(MODELS_DIR, exist_ok=True) | |
| # ููุฒูู ุงูู ูุฏูู ุจุฃู ุฑ ุงูุญุฒู ุฉ ุงูู ุจุงุดุฑ (ู ุซุจุช ุฅูู ุนูู ุงูู PATH ููุช ุงูุชุดุบูู) | |
| _model_dir = os.path.join(MODELS_DIR, MODEL_SLUG) | |
| if not os.path.isdir(_model_dir) or not _glob.glob(os.path.join(_model_dir, "*.ckpt")): | |
| subprocess.run( | |
| ["melband-roformer-download", | |
| "--model", MODEL_SLUG, "--output-dir", MODELS_DIR], | |
| check=True, | |
| ) | |
| _cfgs = _glob.glob(os.path.join(_model_dir, "*.yaml")) | |
| _ckpts = _glob.glob(os.path.join(_model_dir, "*.ckpt")) | |
| if not _cfgs or not _ckpts: | |
| raise RuntimeError(f"ุชุญู ูู ุงูู ูุฏูู ูุดูุ ู ููุด ู ููุงุช ูู {_model_dir}") | |
| CONFIG_PATH = _cfgs[0] | |
| CKPT_PATH = _ckpts[0] | |
| def _to_wav(src, dst): | |
| """ุชุญููู ุฃู ุตูุบุฉ ูู WAV ุณุชูุฑูู 44.1kHz.""" | |
| subprocess.run( | |
| ["ffmpeg", "-y", "-i", src, "-vn", "-acodec", "pcm_s16le", | |
| "-ar", "44100", "-ac", "2", dst], | |
| check=True, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL, | |
| ) | |
| def _separate_gpu(in_dir, out_dir): | |
| """ุงูุฌุฒุก ุงููู ู ุญุชุงุฌ GPU: ุชุดุบูู ุงูู ูุฏูู ุนูู ุฌุฒุก ูุงุญุฏ. (ZeroGPU)""" | |
| subprocess.run( | |
| ["melband-roformer-infer", | |
| "--config_path", CONFIG_PATH, | |
| "--model_path", CKPT_PATH, | |
| "--input_folder", in_dir, | |
| "--store_dir", out_dir], | |
| check=True, | |
| ) | |
| # ---------- ุงูุชูุณูู ูุฃุฌุฒุงุก + ุงูุชุฌู ูุน ู ุน cross-fade ---------- | |
| import numpy as _np | |
| import soundfile as _sf | |
| CHUNK_SEC = 20.0 # ุทูู ูู ุฌุฒุก (ุซูุงูู) โ ูู ุฌุฒุก = ุงุณุชุฏุนุงุก GPU ูุตูุฑ | |
| OVERLAP_SEC = 1.0 # ุชุฏุงุฎู ุจูู ุงูุฃุฌุฒุงุก (ุนุดุงู ู ููุด ูุทุน ู ุณู ูุน) | |
| def _separate_in_chunks(wav_path, work, progress=None): | |
| """ | |
| ุจููุณูู ุงูู ูู ูุฃุฌุฒุงุกุ ููุตู ูู ุฌุฒุก ูู ุงุณุชุฏุนุงุก GPU ูุตูุฑุ | |
| ููุฌู ูุนูู ุจูุธุงู cross-fade. ุจูุฑุฌูุน (vocals_path, music_path). | |
| """ | |
| audio, sr = _sf.read(wav_path, always_2d=True) # (ุนููุงุช, ูููุงุช) | |
| total = audio.shape[0] | |
| ch = audio.shape[1] | |
| chunk = int(CHUNK_SEC * sr) | |
| ov = int(OVERLAP_SEC * sr) | |
| step = chunk - ov | |
| # ุญุฏูุฏ ุงูุฃุฌุฒุงุก | |
| spans = [] | |
| s = 0 | |
| while s < total: | |
| e = min(s + chunk, total) | |
| spans.append((s, e)) | |
| if e >= total: | |
| break | |
| s += step | |
| voc = _np.zeros((total, ch), dtype=_np.float64) | |
| mus = _np.zeros((total, ch), dtype=_np.float64) | |
| wsum = _np.zeros((total, 1), dtype=_np.float64) | |
| for i, (s, e) in enumerate(spans): | |
| if progress is not None: | |
| progress((i, len(spans)), desc=f"ุจููุตู ุงูุฌุฒุก {i+1}/{len(spans)}") | |
| cdir = os.path.join(work, f"c{i}") | |
| cin = os.path.join(cdir, "in") | |
| cout = os.path.join(cdir, "out") | |
| os.makedirs(cin, exist_ok=True) | |
| os.makedirs(cout, exist_ok=True) | |
| _sf.write(os.path.join(cin, "track.wav"), audio[s:e], sr) | |
| _separate_gpu(cin, cout) # ุงุณุชุฏุนุงุก GPU ูุตูุฑ ููู ุฌุฒุก | |
| v = glob.glob(os.path.join(cout, "*vocals*.wav")) | |
| m = glob.glob(os.path.join(cout, "*instrumental*.wav")) | |
| if not v: | |
| raise gr.Error(f"ุงููุตู ูุดู ูู ุงูุฌุฒุก {i+1}") | |
| vd, _ = _sf.read(v[0], always_2d=True) | |
| md = _sf.read(m[0], always_2d=True)[0] if m else _np.zeros_like(vd) | |
| n = e - s | |
| vd = vd[:n]; md = md[:n] | |
| w = _np.ones((n, 1)) | |
| if ov > 0: | |
| ramp = _np.linspace(0, 1, min(ov, n)).reshape(-1, 1) | |
| if s > 0: | |
| w[:len(ramp)] *= ramp | |
| if e < total: | |
| w[-len(ramp):] *= ramp[::-1] | |
| voc[s:e] += vd * w | |
| mus[s:e] += md * w | |
| wsum[s:e] += w | |
| wsum[wsum < 1e-8] = 1.0 | |
| voc = (voc / wsum).astype(_np.float32) | |
| mus = (mus / wsum).astype(_np.float32) | |
| vpath = os.path.join(work, "vocals.wav") | |
| mpath = os.path.join(work, "music.wav") | |
| _sf.write(vpath, voc, sr) | |
| _sf.write(mpath, mus, sr) | |
| return vpath, mpath | |
| def _download_youtube(url, out_path): | |
| """ููุฒูู ุฃูุถู ุตูุช ู ู ูููู ููุชููุจ ุจู yt-dlp ุฅูู out_path (wav).""" | |
| # ู ูุงุญุธุฉ: ููุชุฌุฑุจุฉ ุงูุดุฎุตูุฉ ููุท โ ุชูุฒูู ููุชููุจ ูุฎุงูู ุดุฑูุทู. | |
| # ููุงุฏู yt-dlp ูู ูุฏููู ุจุงูุซูู (ุฃุฃู ู ู ู ุงูุงุนุชู ุงุฏ ุนูู ูุฌูุฏู ูุฃู ุฑ ูู PATH) | |
| base = out_path.rsplit(".", 1)[0] | |
| subprocess.run( | |
| [sys.executable, "-m", "yt_dlp", | |
| "-x", "--audio-format", "wav", "--audio-quality", "0", | |
| "-o", base + ".%(ext)s", url], | |
| check=True, | |
| ) | |
| def separate_link(url): | |
| """ุชุฌุฑูุจู: ุจูุงุฎุฏ ูููู ููุชููุจุ ููุฒูู ุงูุตูุชุ ููุตููุ ููุฑุฌูุน (ุงูููุงู , ุงูู ูุณููู).""" | |
| if not url or not url.strip(): | |
| return None, None | |
| work = tempfile.mkdtemp() | |
| dl_dir = os.path.join(work, "dl") # ุงูุชูุฒูู ููุง | |
| os.makedirs(dl_dir, exist_ok=True) | |
| raw = os.path.join(dl_dir, "yt.wav") | |
| _download_youtube(url.strip(), raw) | |
| got = glob.glob(os.path.join(dl_dir, "*.wav")) | |
| if not got: | |
| raise gr.Error("ู ุด ูุงุฏุฑ ุฃูุฒูู ุงูุตูุช ู ู ุงููููู ุฏู.") | |
| wav = os.path.join(work, "track.wav") | |
| _to_wav(got[0], wav) | |
| return _separate_in_chunks(wav, work) | |
| def separate(audio_file, progress=gr.Progress()): | |
| """ุจุชุงุฎุฏ ู ููุ ุชุญููููุ ุชูุตูู ุฌุฒุก ุฌุฒุกุ ูุชุฑุฌูุน (ุงูููุงู , ุงูู ูุณููู).""" | |
| if audio_file is None: | |
| return None, None | |
| work = tempfile.mkdtemp() # ูุฑูุฏ ููู ุทูุจ | |
| wav = os.path.join(work, "track.wav") | |
| _to_wav(audio_file, wav) # CPU | |
| return _separate_in_chunks(wav, work, progress) # GPU ุฌุฒุก ุฌุฒุก | |
| # ---------- ูุงุฌูุฉ Gradio (ุจูููุฉ ุตุงูู) ---------- | |
| CSS = """ | |
| .gradio-container{max-width:760px!important;margin:auto} | |
| #safi-title{font-weight:900;font-size:30px;text-align:center;color:#3dd7b8} | |
| #safi-sub{text-align:center;color:#8a97a3;margin-bottom:8px} | |
| footer{visibility:hidden} | |
| """ | |
| with gr.Blocks(css=CSS, title="ุตุงูู โ ุฅุฒุงูุฉ ุงูู ูุณููู", theme=gr.themes.Soft()) as demo: | |
| gr.Markdown("# โ ุตุงูู", elem_id="safi-title") | |
| gr.Markdown("ุงุณู ุน ุงูููุงู ุ ุณูุจู ู ู ุงูู ูุณููู โ ูุตู ุจู ูุฏูู Mel-Band RoFormer", elem_id="safi-sub") | |
| with gr.Tab("ุงุฑูุน ู ูู"): | |
| inp = gr.Audio(label="ุงุฑูุน ู ูู ุตูุช", type="filepath") | |
| btn = gr.Button("ุงูุตู ุงูู ูุณููู", variant="primary") | |
| with gr.Tab("ูููู ููุชููุจ (ุชุฌุฑูุจู)"): | |
| gr.Markdown( | |
| "โ ๏ธ ููุชุฌุฑุจุฉ ุงูุดุฎุตูุฉ ููุท โ ุชูุฒูู ููุชููุจ ูุฎุงูู ุดุฑูุทู ูู ุด ู ูุงุณุจ ูููุดุฑ ุงูุนุงู .", | |
| elem_id="safi-sub", | |
| ) | |
| link = gr.Textbox(label="ูููู ููุชููุจ", placeholder="https://www.youtube.com/watch?v=...") | |
| btn_link = gr.Button("ูุฒูู ูุงูุตู", variant="primary") | |
| with gr.Row(): | |
| out_vocals = gr.Audio(label="ุงูููุงู (ุตุงูู)") | |
| out_music = gr.Audio(label="ุงูู ูุณููู") | |
| btn.click(fn=separate, inputs=inp, outputs=[out_vocals, out_music], api_name="separate") | |
| btn_link.click(fn=separate_link, inputs=link, outputs=[out_vocals, out_music], api_name="separate_link") | |
| gr.Markdown( | |
| "ุงูุชุฑุฎูุต MIT โ ุชุฌุงุฑู. ุฃูุช ู ุณุคูู ุนู ุญููู ุงูู ุญุชูู ุงููู ุชุฑูุนู.", | |
| elem_id="safi-sub", | |
| ) | |
| # api_name ุจูุฎููู ุงูุฏุงูุฉ ู ุชุงุญุฉ ูู API ููุงุฌูุฉ Cloudflare | |
| demo.queue() # ู ูู ูู ZeroGPU | |
| if __name__ == "__main__": | |
| # show_api=False ุจูุชุฌููุจ bug ุชูููุฏ ุงูู schema ูู gradio_client | |
| demo.launch() | |