diff --git a/README-CN.md b/README-CN.md index 21d1d7e..b92fe2c 100644 --- a/README-CN.md +++ b/README-CN.md @@ -4,6 +4,20 @@ 快速, 高质量音乐生成, "重绘", Remix, 编辑, 扩展等, Windows, Linux, Mac 应该都支持(未做完整测试). +增加了多语言转换节点, 为 [ComfyUI](URL_ADDRESS.comfy.org/tutorials/audio/ace-step/ace-step-v1) 官方版 ACE-Step 工作流提供多语言支持. 目前,ACE Step 支持 19 种语言,但以下 10 种语言有更好的支持: +- 英语:[en] +- 中文:[zh] +- 俄文:[ru] +- 西班牙文:[es] +- 日文:[ja] +- 德文:[de] +- 法文:[fr] +- 葡萄牙文:[pt] +- 意大利文:[it] +- 韩文:[ko] + +![](https://github.com/billwuhao/ComfyUI_ACE-Step/blob/main/images/2025-05-10_19-26-46.png) + 示例: - 生成: @@ -24,6 +38,8 @@ ## 📣 更新 +[2025-05-10]⚒️: 增加歌词语言转换节点, 为 [ComfyUI](https://docs.comfy.org/tutorials/audio/ace-step/ace-step-v1) 官方版 ACE-Step 工作流提供多语言支持. + [2025-05-07]⚒️: 发布版本 v1.0.0. ## 安装 diff --git a/README.md b/README.md index fb1e62c..dfc7897 100644 --- a/README.md +++ b/README.md @@ -4,6 +4,20 @@ Fast, high-quality music generation, "repainting", remixing, editing, extending, and more. Windows, Linux, and Mac should all be supported (not fully tested). +Added a node for lyrics language conversion to provide multilingual support for the official [ComfyUI](https://docs.comfy.org/tutorials/audio/ace-step/ace-step-v1) ACE-Step workflow. Currently, ACE-Step supports 19 languages, but the following ten languages have better support: +- English +- Chinese: [zh] +- Russian: [ru] +- Spanish: [es] +- Japanese: [ja] +- German: [de] +- French: [fr] +- Portuguese: [pt] +- Italian: [it] +- Korean: [ko] + +![](https://github.com/billwuhao/ComfyUI_ACE-Step/blob/main/images/2025-05-10_19-26-46.png) + Examples: - Generation: @@ -24,6 +38,8 @@ Examples: ## 📣 Updates +[2025-05-10]⚒️: Added a node for lyrics language conversion to provide multilingual support for the official [ComfyUI](https://docs.comfy.org/tutorials/audio/ace-step/ace-step-v1) ACE-Step workflow. + [2025-05-07]⚒️: Released version v1.0.0. ## Installation diff --git a/ace_step_nodes.py b/ace_step_nodes.py index 3de2f91..80d0d5d 100644 --- a/ace_step_nodes.py +++ b/ace_step_nodes.py @@ -514,7 +514,10 @@ class ACEStepExtend: return ({"waveform": audio, "sample_rate": sr},) +from .text2lyric import LyricsLangSwitch + NODE_CLASS_MAPPINGS = { + "LyricsLangSwitch": LyricsLangSwitch, "ACEStepGen": ACEStepGen, "GenerationParameters": GenerationParameters, "MultiLinePromptACES": MultiLinePromptACES, @@ -525,6 +528,7 @@ NODE_CLASS_MAPPINGS = { } NODE_DISPLAY_NAME_MAPPINGS = { + "LyricsLangSwitch": "ACE-Step Lyrics Language Switch", "ACEStepGen": "ACE-Step", "GenerationParameters": "ACE-Step Parameters", "MultiLinePromptACES": "ACE-Step Prompt", diff --git a/images/2025-05-10_19-26-46.png b/images/2025-05-10_19-26-46.png new file mode 100644 index 0000000..f33fb83 Binary files /dev/null and b/images/2025-05-10_19-26-46.png differ diff --git a/pyproject.toml b/pyproject.toml index 3f29fe5..e46c8e5 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,7 +1,7 @@ [project] name = "ace-step" description = "ACE-Step: A Step Towards Music Generation Foundation Model" -version = "1.0.5" +version = "1.0.6" license = {file = "LICENSE"} dependencies = ["diffusers", "librosa", "pypinyin", "pytorch_lightning", "soundfile", "hangul-romanize", "num2words", "spacy"] diff --git a/requirements.txt b/requirements.txt index 24faffc..7bcc895 100644 --- a/requirements.txt +++ b/requirements.txt @@ -7,4 +7,5 @@ hangul-romanize num2words spacy loguru -py3langid \ No newline at end of file +py3langid +cutlet \ No newline at end of file diff --git a/text2lyric.py b/text2lyric.py new file mode 100644 index 0000000..e62cf2a --- /dev/null +++ b/text2lyric.py @@ -0,0 +1,95 @@ +import re +from loguru import logger +import os +import sys + +current_dir = os.path.dirname(os.path.abspath(__file__)) +if current_dir not in sys.path: + sys.path.append(current_dir) + +from ace_step.language_segmentation import LangSegment +from ace_step.ace_models.lyrics_utils.lyric_tokenizer import VoiceBpeTokenizer + +lyric_tokenizer = VoiceBpeTokenizer() +lang_segment = LangSegment() + +SUPPORT_LANGUAGES = { + "en": 259, "de": 260, "fr": 262, "es": 284, "it": 285, + "pt": 286, "pl": 294, "tr": 295, "ru": 267, "cs": 293, + "nl": 297, "ar": 5022, "zh": 5023, "ja": 5412, "hu": 5753, + "ko": 6152, "hi": 6680 +} + +lang_segment.setfilters([ + 'af', 'am', 'an', 'ar', 'as', 'az', 'be', 'bg', 'bn', 'br', 'bs', 'ca', 'cs', 'cy', 'da', 'de', 'dz', 'el', + 'en', 'eo', 'es', 'et', 'eu', 'fa', 'fi', 'fo', 'fr', 'ga', 'gl', 'gu', 'he', 'hi', 'hr', 'ht', 'hu', 'hy', + 'id', 'is', 'it', 'ja', 'jv', 'ka', 'kk', 'km', 'kn', 'ko', 'ku', 'ky', 'la', 'lb', 'lo', 'lt', 'lv', 'mg', + 'mk', 'ml', 'mn', 'mr', 'ms', 'mt', 'nb', 'ne', 'nl', 'nn', 'no', 'oc', 'or', 'pa', 'pl', 'ps', 'pt', 'qu', + 'ro', 'ru', 'rw', 'se', 'si', 'sk', 'sl', 'sq', 'sr', 'sv', 'sw', 'ta', 'te', 'th', 'tl', 'tr', 'ug', 'uk', + 'ur', 'vi', 'vo', 'wa', 'xh', 'zh', 'zu' + ]) + + +def get_lang(text): + language = "en" + try: + _ = lang_segment.getTexts(text) + langCounts = lang_segment.getCounts() + language = langCounts[0][0] + if len(langCounts) > 1 and language == "en": + language = langCounts[1][0] + except Exception as err: + language = "en" + return language + +def tokenize_lyrics(lyrics): + lines = lyrics.split("\n") + lyric_token_idx = [] + for line in lines: + line = line.strip() + if not line: + lyric_token_idx += ["\n"] + continue + + lang = get_lang(line) + + if lang not in SUPPORT_LANGUAGES: + lang = "en" + if "zh" in lang: + lang = "zh" + if "spa" in lang: + lang = "es" + + structure_pattern = re.compile(r"\[.*?\]") + try: + if structure_pattern.match(line): + token_idx = lyric_tokenizer.preprocess_text(line, "en") + lyric_token_idx.append(token_idx + "\n") + else: + token_idx = lyric_tokenizer.preprocess_text(line, lang) + lyric_token_idx.append(f"[{lang}]" + token_idx + "\n") + except Exception as e: + print("tokenize error", e, "for line", line, "major_language", lang) + + return "".join(lyric_token_idx) + + +class LyricsLangSwitch: + @classmethod + def INPUT_TYPES(cls): + + return { + "required": { + "lyrics": ("STRING", { + "multiline": True, + "default": "[verse]\n我走过深夜的街道\n冷风吹乱思念的漂亮外套\n你的微笑像星光很炫耀\n照亮了我孤独的每分每秒\n\n[chorus]\n愿你是风吹过我的脸\n带我飞过最远最遥远的山间\n愿你是风轻触我的梦\n停在心头不再飘散无迹无踪\n\n[verse]\n一起在喧哗避开世俗的骚动\n独自在天台探望月色的朦胧\n你说爱像音乐带点重节奏\n一拍一跳让我忘了心的温度多空洞\n\n[bridge]\n唱起对你的想念不隐藏\n像诗又像画写满藏不了的渴望\n你的影子挥不掉像风的倔强\n追着你飞扬穿越云海一样泛光\n\n[chorus]\n愿你是风吹过我的手\n暖暖的触碰像春日细雨温柔\n愿你是风盘绕我的身\n深情万万重不会有一天走远走\n\n[verse]\n深夜的钢琴弹起动人的旋律\n低音鼓砸进心底的每一次呼吸\n要是能将爱化作歌声传递\n你是否会听见我心里的真心实意"}), + }, + } + + CATEGORY = "🎤MW/MW-ACE-Step" + RETURN_TYPES = ("STRING",) + RETURN_NAMES = ("lyrics",) + FUNCTION = "lyricsgen" + + def lyricsgen(self, lyrics: str): + return (tokenize_lyrics(lyrics.strip()),) \ No newline at end of file