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)
This commit is contained in:
+27
@@ -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/
|
||||
@@ -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.
|
||||
@@ -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
|
||||
```
|
||||
@@ -0,0 +1,2 @@
|
||||
from .nodes import NODE_CLASS_MAPPINGS, NODE_DISPLAY_NAME_MAPPINGS
|
||||
__all__ = ["NODE_CLASS_MAPPINGS", "NODE_DISPLAY_NAME_MAPPINGS"]
|
||||
@@ -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",
|
||||
}
|
||||
@@ -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"
|
||||
Reference in New Issue
Block a user