# !/usr/bin/env python # -*- coding: UTF-8 -*- import numpy as np import torch import os from comfy_api.latest import io import folder_paths from pathlib import Path from datetime import datetime from .node_utils import clear_comfyui_cache,audio2path,load_yue2_pipeline,yue2_generate,loadsheetsage device = torch.device( "cuda:0") if torch.cuda.is_available() else torch.device( "mps") if torch.backends.mps.is_available() else torch.device( "cpu") MAX_SEED = np.iinfo(np.int32).max node_yue_cr_path = os.path.dirname(os.path.abspath(__file__)) weigths_gguf_current_path = os.path.join(folder_paths.models_dir, "gguf") if not os.path.exists(weigths_gguf_current_path): os.makedirs(weigths_gguf_current_path) folder_paths.add_model_folder_path("gguf", weigths_gguf_current_path) # gguf dir class YUE_SM_Model(io.ComfyNode): @classmethod def define_schema(cls): return io.Schema( node_id="YUE_SM_Model", display_name="YUE_SM_Model", category="YUE_SM", inputs=[ io.Combo.Input("diffusion_models",["none"] +folder_paths.get_filename_list("diffusion_models")), ], outputs=[ io.Model.Output(display_name="model"), ], ) @classmethod def execute(cls, diffusion_models) -> io.NodeOutput: clear_comfyui_cache() dit_path=folder_paths.get_full_path("diffusion_models",diffusion_models) if diffusion_models != "none" else None return io.NodeOutput(load_yue2_pipeline(os.path.join(node_yue_cr_path, "YuE2-3B"),yue_ckpt=dit_path, vae=None, device=device)) class YUE_SM_Vae(io.ComfyNode): @classmethod def define_schema(cls): return io.Schema( node_id="YUE_SM_Vae", display_name="YUE_SM_Vae", category="YUE_SM", inputs=[ io.Combo.Input("vae",options=folder_paths.get_filename_list("vae")), ], outputs=[ io.Vae.Output(display_name="vae"), ], ) @classmethod def execute(cls,vae) -> io.NodeOutput: clear_comfyui_cache() from .src.yue2.modeling_vae import YuE2VAE return io.NodeOutput(YuE2VAE.from_pretrained(os.path.join(node_yue_cr_path, "vae"), decoder_only=True, device="cpu",local_files_only=True,ckpt_path=folder_paths.get_full_path("vae",vae))) class YUE_SM_Clip(io.ComfyNode): @classmethod def define_schema(cls): return io.Schema( node_id="YUE_SM_Clip", display_name="YUE_SM_Clip", category="YUE_SM", inputs=[ io.Combo.Input("clip",options= folder_paths.get_filename_list("clip")), io.Combo.Input("mert2",options= folder_paths.get_filename_list("clip")), ], outputs=[ io.Clip.Output(display_name="clip"), ], ) @classmethod def execute(cls,clip,mert2) -> io.NodeOutput: clear_comfyui_cache() return io.NodeOutput(loadsheetsage(folder_paths.get_full_path("clip",clip),folder_paths.get_full_path("clip",mert2))) class YUE_SM_Cond(io.ComfyNode): @classmethod def define_schema(cls): return io.Schema( node_id="YUE_SM_Cond", display_name="YUE_SM_Cond", category="YUE_SM", inputs=[ io.Clip.Input("clip"), io.Audio.Input("audio"), ], outputs=[ io.String.Output(display_name="abc_file"), ], ) @classmethod def execute(cls,clip,audio,) -> io.NodeOutput: clear_comfyui_cache() clip.to(device) prefix=datetime.now().strftime("%Y%m%d_%H%M%S_%f") output_dir = os.path.join(folder_paths.get_output_directory(),"audio", f"cover_score_{prefix}") result = clip.transcribe(audio2path(audio), output_dir=output_dir, melody_only=True,) clip.to("cpu") if not result.get("abc") or result.get("abc_error"): raise RuntimeError("Transcription did not produce a usable melody score") abc_file= os.path.join(output_dir,"score.abc") return io.NodeOutput(abc_file) class YUE_SM_Sampler(io.ComfyNode): @classmethod def define_schema(cls): return io.Schema( node_id="YUE_SM_Sampler", display_name="YUE_SM_Sampler", category="YUE_SM", inputs=[ io.Model.Input("model"), io.Vae.Input("vae"), io.String.Input("style", default="English, warm piano pop, expressive female voice, acoustic piano, rounded bass and light drums, lyrical memorable melody, unhurried phrasing, 88 BPM",multiline=True), io.String.Input("lyrics", default="[Verse]\nNeon fades along the lane\nFootsteps keep the time of rain\nFold the night and leave it here\nMorning has a sky to clear\n\n[Chorus]\nLet the day come into view\nEvery road begins with you\nHold a little room for light\nWe will sing beyond the night",multiline=True), io.Combo.Input("cot", options=["full", "melody", "off"], default="full"), io.Int.Input("seed", default=0, min=0, max=MAX_SEED), io.Boolean.Input("only_plan", default=False,tooltip="If True, just return the plan abc files, not the audio."), io.String.Input("abc_file", default="E:\\ComfyUI313\\ComfyUI\\custom_nodes\\ComfyUI_YuE\\examples\\melody.abc",multiline=False), ], outputs=[ io.Audio.Output(display_name="audio"), ], ) @classmethod def execute(cls, model,vae,style,lyrics,cot,seed,only_plan,abc_file) -> io.NodeOutput: clear_comfyui_cache() request = {"lyrics": lyrics,"style": style,"seed": seed,"cot": cot,"id": "comfyui_node",} print(abc_file) if abc_file: request["abc"] = Path(abc_file).read_text(encoding="utf-8") if request.get("abc") is not None and request.get("cot", "full") == "off": raise ValueError("A supplied score requires full or melody mode. 使用乐谱时,cot参数必须为full或melody。") prefix=datetime.now().strftime("%Y%m%d_%H%M%S_%f") destination=os.path.join(folder_paths.get_output_directory(),"audio", f"destination_{prefix}") if not only_plan: result=yue2_generate(model, request,vae_model=vae) result.save_artifacts(destination) waveform = torch.from_numpy(result.audio).permute(1, 0).to("cpu").float() print(f"Generated audio shape: {waveform.shape}, sample rate: {result.sample_rate}") # torch.Size([2,3836096]), sample rate: 48000 return io.NodeOutput({"waveform": waveform.unsqueeze(0), "sample_rate": result.sample_rate}) else: print("only_plan,no audio generated") from .src.yue2.protocol import SongRequest request=SongRequest(**request) plan = model.plan(request=request, vae_model=vae) plan.save(destination) result = {"mode": request.cot, "truncated": {"abc": plan.truncated}} return io.NodeOutput({"waveform": torch.zeros(1, 1, 1), "sample_rate": 48000}) from aiohttp import web from server import PromptServer import base64 @PromptServer.instance.routes.post("/yue_sm/get_file_path") async def get_file_path(request): try: data = await request.json() filename = data.get('filename', 'temp.abc') content_base64 = data.get('content', '') if not content_base64: return web.json_response({"error": "No file content provided"}, status=400) file_content = base64.b64decode(content_base64) temp_dir = os.path.join(folder_paths.get_output_directory(), "yue_sm_temp") os.makedirs(temp_dir, exist_ok=True) file_path = os.path.join(temp_dir, filename) with open(file_path, 'wb') as f: f.write(file_content) print(f"File saved to: {file_path}") return web.json_response({"path": file_path}) except Exception as e: print(f"Error in get_file_path: {str(e)}") return web.json_response({"error": str(e)}, status=500)