diff --git a/__init__.py b/__init__.py index b57095a..80c83b2 100644 --- a/__init__.py +++ b/__init__.py @@ -7,8 +7,18 @@ Generate complete songs with vocals and instrumentals from lyrics! import sys import os +import warnings import importlib.util +# Suppress cosmetic warnings from transformers about GenerationMixin and checkpointing format +# These need to be set early, before transformers is imported +warnings.filterwarnings("ignore", message=".*GenerationMixin.*") +warnings.filterwarnings("ignore", message=".*old version of the checkpointing format.*") +warnings.filterwarnings("ignore", message=".*doesn't directly inherit from.*") +warnings.filterwarnings("ignore", message=".*will NOT inherit from.*") +warnings.filterwarnings("ignore", message=".*_set_gradient_checkpointing.*") +warnings.filterwarnings("ignore", category=FutureWarning, module="transformers.*") + # Get current directory current_dir = os.path.dirname(os.path.abspath(__file__)) diff --git a/codeclm/models/codeclm.py b/codeclm/models/codeclm.py index d0304f4..53deb46 100644 --- a/codeclm/models/codeclm.py +++ b/codeclm/models/codeclm.py @@ -139,6 +139,14 @@ class CodecLM: bgm_wavs = list(bgm_wavs) texts, audio_qt_embs = self._prepare_tokens_and_attributes(lyrics=lyrics, melody_wavs=melody_wavs, vocal_wavs=vocal_wavs, bgm_wavs=bgm_wavs, melody_is_wav=melody_is_wav) + + # Debug: Log what's being passed to _generate_tokens + print(f"\n[CodecLM DEBUG] ========== _generate_tokens INPUT ==========") + print(f"[CodecLM DEBUG] texts (lyrics): {texts}") + print(f"[CodecLM DEBUG] descriptions: {descriptions}") + print(f"[CodecLM DEBUG] audio_qt_embs shape: {audio_qt_embs.shape if audio_qt_embs is not None else 'None'}") + print(f"[CodecLM DEBUG] ================================================\n") + tokens = self._generate_tokens(texts, descriptions, audio_qt_embs) if (tokens == self.lm.eos_token_id).any(): diff --git a/codeclm/models/lm_levo.py b/codeclm/models/lm_levo.py index c851fb7..d5bba18 100755 --- a/codeclm/models/lm_levo.py +++ b/codeclm/models/lm_levo.py @@ -233,7 +233,13 @@ class LmModel(StreamingModule): if descriptions is not None: attr["text"]["type_info"] = descriptions[i] conditions.append(attr) - print("conditions", conditions) + # Enhanced debug logging for lyrics/descriptions + print(f"\n[LmLevo DEBUG] ========== CONDITION ATTRIBUTES ==========") + print(f"[LmLevo DEBUG] Available conditioners: {list(self.condition_provider.conditioners.keys())}") + print(f"[LmLevo DEBUG] attr['text']['description'] (LYRICS): {repr(attr.text.get('description', 'NOT SET'))[:200]}") + print(f"[LmLevo DEBUG] attr['text']['type_info'] (DESCRIPTION): {repr(attr.text.get('type_info', 'NOT SET'))[:200]}") + print(f"[LmLevo DEBUG] Has prompt_audio: {'prompt_audio' in attr.audio}") + print(f"[LmLevo DEBUG] =============================================\n") if prepare_null_condition: cfg_inference = ClassifierFreeGuidanceDropoutInference() null_conditions = cfg_inference(conditions, condition_types=["audio", "text"], diff --git a/codeclm/modules/conditioners.py b/codeclm/modules/conditioners.py index 7a66fe8..cee65aa 100755 --- a/codeclm/modules/conditioners.py +++ b/codeclm/modules/conditioners.py @@ -133,9 +133,22 @@ class QwTokenizerConditioner(TextConditioner): print("all structure tokens: ", {self.text_tokenizer.convert_ids_to_tokens(i):i for i in self.struct_token_ids}) def tokenize(self, x: tp.List[tp.Optional[str]]) -> tp.Dict[str, torch.Tensor]: + # Debug: Log input before tokenization + print(f"\n[QwTokenizerConditioner DEBUG] ========== TOKENIZE INPUT ==========") + print(f"[QwTokenizerConditioner DEBUG] Input text (first item, first 300 chars): {repr(x[0][:300]) if x and x[0] else 'None/Empty'}") + print(f"[QwTokenizerConditioner DEBUG] Number of inputs: {len(x)}") + x = ['<|im_start|>' + xi if xi is not None else "<|im_start|>" for xi in x] # x = [xi if xi is not None else "" for xi in x] inputs = self.text_tokenizer(x, return_tensors="pt", padding=True) + + # Debug: Log tokenization result + print(f"[QwTokenizerConditioner DEBUG] Tokenized shape: {inputs['input_ids'].shape}") + print(f"[QwTokenizerConditioner DEBUG] First 20 token IDs: {inputs['input_ids'][0][:20].tolist()}") + decoded = self.text_tokenizer.decode(inputs['input_ids'][0][:50]) + print(f"[QwTokenizerConditioner DEBUG] Decoded first 50 tokens: {repr(decoded)}") + print(f"[QwTokenizerConditioner DEBUG] ===========================================\n") + return inputs def forward(self, inputs: tp.Dict[str, torch.Tensor]) -> ConditionType: diff --git a/fl_nodes/lyrics_formatter.py b/fl_nodes/lyrics_formatter.py index ea5211d..7e7869c 100644 --- a/fl_nodes/lyrics_formatter.py +++ b/fl_nodes/lyrics_formatter.py @@ -3,8 +3,21 @@ FL Song Gen Lyrics Formatter Node. Helps users build properly formatted lyrics with section tags. """ +import re from typing import Tuple +# Regex to filter lyrics - keeps letters, numbers, whitespace, brackets, hyphens, and CJK characters +# Removes punctuation like commas, apostrophes, quotes, etc. +LYRICS_FILTER_REGEX = re.compile( + r"[^\w\s\[\]\-\u4e00-\u9fff\u3040-\u309f\u30a0-\u30ff\uac00-\ud7af\u00c0-\u017f]" +) + + +def clean_lyrics_line(line: str) -> str: + """Clean a single lyrics line by removing unsupported punctuation.""" + cleaned = LYRICS_FILTER_REGEX.sub("", line) + return cleaned.strip() + class FL_SongGen_LyricsFormatter: """ @@ -228,9 +241,14 @@ class FL_SongGen_LyricsFormatter: """ Convert multiline text to SongGen format. Newlines become periods to separate phrases. + Punctuation is removed to match model training data format. """ - # Split by newlines, strip each line, filter empty - lines = [line.strip() for line in text.strip().split("\n") if line.strip()] + # Split by newlines, clean each line, filter empty + lines = [] + for line in text.strip().split("\n"): + cleaned = clean_lyrics_line(line) + if cleaned: + lines.append(cleaned) - # Join with period-space (matches original SongGeneration training data format) - return ". ".join(lines) + # Join with period only (matches official SongGeneration format) + return ".".join(lines) diff --git a/fl_utils/model_manager.py b/fl_utils/model_manager.py index 02b203b..b0e7a95 100644 --- a/fl_utils/model_manager.py +++ b/fl_utils/model_manager.py @@ -19,6 +19,10 @@ from omegaconf import OmegaConf # Suppress cosmetic warnings from transformers about GenerationMixin and checkpointing format warnings.filterwarnings("ignore", message=".*GenerationMixin.*") warnings.filterwarnings("ignore", message=".*old version of the checkpointing format.*") +warnings.filterwarnings("ignore", message=".*doesn't directly inherit from.*") +warnings.filterwarnings("ignore", message=".*will NOT inherit from.*") +warnings.filterwarnings("ignore", message=".*_set_gradient_checkpointing.*") +warnings.filterwarnings("ignore", category=FutureWarning, module="transformers.*") # Get the fl_utils directory (same directory as this file) _FL_UTILS_DIR = os.path.dirname(__file__) diff --git a/fl_utils/songgen_wrapper.py b/fl_utils/songgen_wrapper.py index 222b8b9..f74ba5c 100644 --- a/fl_utils/songgen_wrapper.py +++ b/fl_utils/songgen_wrapper.py @@ -5,6 +5,7 @@ Encapsulates the inference logic from SongGeneration's generate.py. import gc import os +import re import sys import tempfile import importlib.util @@ -15,6 +16,12 @@ import torch import torchaudio import soundfile as sf +# Regex to filter lyrics - keeps letters, numbers, whitespace, brackets, hyphens, and CJK characters +# Removes punctuation like commas, apostrophes, quotes, etc. that the model wasn't trained on +LYRICS_FILTER_REGEX = re.compile( + r"[^\w\s\[\]\-;\.\u4e00-\u9fff\u3040-\u309f\u30a0-\u30ff\uac00-\ud7af\u00c0-\u017f]" +) + # Get the fl_utils directory (same directory as this file) _FL_UTILS_DIR = os.path.dirname(__file__) @@ -112,8 +119,9 @@ class SongGenWrapper: print(f"[FL SongGen] Duration {duration}s exceeds max {self.max_duration}s, clamping.") duration = self.max_duration - # Clean lyrics - lyrics = lyrics.replace(" ", " ") + # Clean lyrics - remove unsupported punctuation and normalize spaces + lyrics = LYRICS_FILTER_REGEX.sub("", lyrics) + lyrics = re.sub(r"\s+", " ", lyrics) # Normalize multiple spaces to single space if self.low_mem: return self._generate_lowmem( @@ -193,6 +201,16 @@ class SongGenWrapper: 'melody_is_wav': melody_is_wav, } + # Debug: Log what we're passing to the model + print(f"\n[FL SongGen DEBUG] ========== GENERATION INPUT ==========") + print(f"[FL SongGen DEBUG] Lyrics (first 200 chars): {repr(lyrics[:200]) if lyrics else 'None'}") + print(f"[FL SongGen DEBUG] Description: {repr(description) if description else 'None'}") + print(f"[FL SongGen DEBUG] melody_wavs shape: {pmt_wav.shape if pmt_wav is not None else 'None'}") + print(f"[FL SongGen DEBUG] vocal_wavs shape: {vocal_wav.shape if vocal_wav is not None else 'None'}") + print(f"[FL SongGen DEBUG] bgm_wavs shape: {bgm_wav.shape if bgm_wav is not None else 'None'}") + print(f"[FL SongGen DEBUG] melody_is_wav: {melody_is_wav}") + print(f"[FL SongGen DEBUG] ======================================\n") + print(f"[FL SongGen] Generating tokens for {duration}s song...") with torch.autocast(device_type="cuda", dtype=torch.float16, enabled=self.device == "cuda"): @@ -359,6 +377,16 @@ class SongGenWrapper: 'melody_is_wav': melody_is_wav, } + # Debug: Log what we're passing to the model (low mem path) + print(f"\n[FL SongGen LowMem DEBUG] ========== GENERATION INPUT ==========") + print(f"[FL SongGen LowMem DEBUG] Lyrics (first 200 chars): {repr(lyrics[:200]) if lyrics else 'None'}") + print(f"[FL SongGen LowMem DEBUG] Description: {repr(description) if description else 'None'}") + print(f"[FL SongGen LowMem DEBUG] melody_wavs shape: {pmt_wav.shape if pmt_wav is not None else 'None'}") + print(f"[FL SongGen LowMem DEBUG] vocal_wavs shape: {vocal_wav.shape if vocal_wav is not None else 'None'}") + print(f"[FL SongGen LowMem DEBUG] bgm_wavs shape: {bgm_wav.shape if bgm_wav is not None else 'None'}") + print(f"[FL SongGen LowMem DEBUG] melody_is_wav: {melody_is_wav}") + print(f"[FL SongGen LowMem DEBUG] ======================================\n") + print(f"[FL SongGen LowMem] Generating tokens...") with torch.autocast(device_type="cuda", dtype=torch.float16): with torch.no_grad(): diff --git a/pyproject.toml b/pyproject.toml index 6328a32..8ff7a91 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,7 +1,7 @@ [project] name = "comfyui_fl-songgen" description = "FL Song Gen - AI-powered song generation nodes for ComfyUI. Generate complete songs with vocals and instrumentals from lyrics using Tencent's SongGeneration (LeVo) model. Features style transfer, auto style presets, dual-track output, and up to 4m30s song generation." -version = "1.0.8" +version = "1.0.9" license = "Apache-2.0" dependencies = [ "torch>=2.0.0",