commit 9ebd2a85e5b32f32223dce2876af23141e755b68 Author: Ryan Holanda Date: Fri Feb 20 15:41:32 2026 -0300 feat: initial release - Audio ↔ Tensor bridge nodes for ComfyUI - AudioToTensor: Converts ComfyUI AUDIO to VCAUDIOTENSOR (mono, resampled) - TensorToAudio: Converts VCAUDIOTENSOR back to ComfyUI AUDIO - Compatible with MuseTalk-KJ, VoiceCraft, and any Whisper-based node - Zero additional dependencies (uses PyTorch + torchaudio) diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..f005078 --- /dev/null +++ b/.gitignore @@ -0,0 +1,27 @@ +# Python +__pycache__/ +*.py[cod] +*$py.class +*.so +*.egg-info/ +dist/ +build/ +*.egg + +# Virtual environments +.venv/ +venv/ +env/ + +# IDE +.vscode/ +.idea/ +*.swp +*.swo +*~ +.DS_Store + +# Testing +.pytest_cache/ +.coverage +htmlcov/ diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..76d6b3b --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2026 Ryan Holanda + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..4bc73ee --- /dev/null +++ b/README.md @@ -0,0 +1,29 @@ +# ComfyUI-AudioBridge + +Bridges ComfyUI's native **AUDIO** type to raw audio tensors (**VCAUDIOTENSOR**) used by nodes like [MuseTalk-KJ](https://github.com/kijai/ComfyUI-MuseTalk-KJ) and VoiceCraft. + +## Nodes + +| Node | Description | +| ------------------ | ------------------------------------------------------------------------- | +| **Audio → Tensor** | Converts `AUDIO` → `VCAUDIOTENSOR` (mono, resampled to target rate) | +| **Tensor → Audio** | Converts `VCAUDIOTENSOR` → `AUDIO` (wraps tensor back for standard nodes) | + +## Why? + +ComfyUI's native `AUDIO` type is a dict (`{waveform, sample_rate}`), but some custom nodes expect `VHS_AUDIO` (a callable) or `VCAUDIOTENSOR` (a raw torch tensor). This node bridges the gap without requiring VideoHelperSuite or duplicate audio loading. + +## Install + +```bash +cd ComfyUI/custom_nodes +git clone https://github.com/RyanHolanda/ComfyUI-AudioBridge.git +``` + +No additional dependencies required — uses PyTorch and torchaudio (already included with ComfyUI). + +## Usage Example + +``` +LoadAudio → [Audio → Tensor] → whisper_to_features → MuseTalk Sampler +``` diff --git a/__init__.py b/__init__.py new file mode 100644 index 0000000..574ce91 --- /dev/null +++ b/__init__.py @@ -0,0 +1,2 @@ +from .nodes import NODE_CLASS_MAPPINGS, NODE_DISPLAY_NAME_MAPPINGS +__all__ = ["NODE_CLASS_MAPPINGS", "NODE_DISPLAY_NAME_MAPPINGS"] diff --git a/nodes.py b/nodes.py new file mode 100644 index 0000000..7ccb889 --- /dev/null +++ b/nodes.py @@ -0,0 +1,104 @@ +""" +ComfyUI-AudioBridge +Bridges ComfyUI's native AUDIO type to other audio formats used by +custom nodes (e.g. MuseTalk, VoiceCraft). +""" +import torch +import torchaudio + + +class AudioToTensor: + """ + Converts ComfyUI's native AUDIO type to a raw audio tensor. + + ComfyUI AUDIO = {"waveform": Tensor, "sample_rate": int} + Output = mono torch tensor resampled to target rate (default 16kHz). + + Compatible with MuseTalk-KJ's VCAUDIOTENSOR input type and any node + that expects a raw audio waveform tensor. + """ + + @classmethod + def INPUT_TYPES(s): + return { + "required": { + "audio": ("AUDIO",), + "target_sample_rate": ("INT", { + "default": 16000, + "min": 8000, + "max": 48000, + "step": 1000, + "tooltip": "Target sample rate in Hz. 16000 for Whisper/MuseTalk." + }), + }, + } + + RETURN_TYPES = ("VCAUDIOTENSOR", "INT") + RETURN_NAMES = ("audio_tensor", "audio_dur") + FUNCTION = "convert" + CATEGORY = "AudioBridge" + DESCRIPTION = "Converts ComfyUI AUDIO to a raw tensor (VCAUDIOTENSOR). Use with MuseTalk, VoiceCraft, or any node expecting raw audio tensors." + + def convert(self, audio, target_sample_rate): + waveform = audio["waveform"].squeeze(0) # (channels, samples) + sample_rate = audio["sample_rate"] + + # Convert to mono + if waveform.shape[0] > 1: + waveform = waveform.mean(0, keepdim=True) + + # Resample if needed + if sample_rate != target_sample_rate: + waveform = torchaudio.functional.resample( + waveform, sample_rate, target_sample_rate + ) + + audio_dur = waveform.shape[1] / target_sample_rate + return (waveform, int(audio_dur)) + + +class TensorToAudio: + """ + Converts a raw audio tensor back to ComfyUI's native AUDIO type. + Useful for routing processed audio back into standard ComfyUI nodes. + """ + + @classmethod + def INPUT_TYPES(s): + return { + "required": { + "audio_tensor": ("VCAUDIOTENSOR",), + "sample_rate": ("INT", { + "default": 16000, + "min": 8000, + "max": 48000, + "step": 1000, + }), + }, + } + + RETURN_TYPES = ("AUDIO",) + RETURN_NAMES = ("audio",) + FUNCTION = "convert" + CATEGORY = "AudioBridge" + DESCRIPTION = "Converts a raw audio tensor (VCAUDIOTENSOR) back to ComfyUI AUDIO type." + + def convert(self, audio_tensor, sample_rate): + # Ensure correct shape: (batch, channels, samples) + if audio_tensor.dim() == 1: + audio_tensor = audio_tensor.unsqueeze(0).unsqueeze(0) + elif audio_tensor.dim() == 2: + audio_tensor = audio_tensor.unsqueeze(0) + + return ({"waveform": audio_tensor, "sample_rate": sample_rate},) + + +NODE_CLASS_MAPPINGS = { + "AudioToTensor": AudioToTensor, + "TensorToAudio": TensorToAudio, +} + +NODE_DISPLAY_NAME_MAPPINGS = { + "AudioToTensor": "Audio → Tensor", + "TensorToAudio": "Tensor → Audio", +} diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..00bb5c6 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,22 @@ +[project] +name = "comfyui-audiobridge" +version = "1.0.0" +description = "Bridges ComfyUI's native AUDIO type to raw audio tensors (VCAUDIOTENSOR) for MuseTalk, VoiceCraft, and more." +readme = "README.md" +license = {text = "MIT"} +requires-python = ">=3.10" +authors = [ + {name = "Ryan Holanda", email = ""}, +] +keywords = ["comfyui", "audio", "musetalk", "whisper", "tts"] +classifiers = [ + "Development Status :: 5 - Production/Stable", + "License :: OSI Approved :: MIT License", + "Programming Language :: Python :: 3", + "Topic :: Multimedia :: Sound/Audio", +] + +[project.urls] +Homepage = "https://github.com/RyanHolanda/ComfyUI-AudioBridge" +Repository = "https://github.com/RyanHolanda/ComfyUI-AudioBridge" +Issues = "https://github.com/RyanHolanda/ComfyUI-AudioBridge/issues"