Files
filliptm-ComfyUI-FL-FishSpeech/fl_utils/tensor_utils.py
T
FillipandClaude Opus 4.6 66afde80f3 Initial release: FL FishSpeech v1.0.0
AI Text-to-Speech & Voice Cloning for ComfyUI using OpenAudio S1-mini
(Fish Audio). Features DualAR Transformer, DAC codec, voice cloning,
emotion control tags, Whisper transcription, and per-token progress bars.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-12 13:24:30 -07:00

79 lines
2.2 KiB
Python

"""Audio tensor conversion utilities for FL FishSpeech ComfyUI nodes."""
from __future__ import annotations
import torch
def comfyui_audio_to_tensor(audio: dict) -> tuple[torch.Tensor, int]:
"""Extract waveform tensor and sample rate from ComfyUI AUDIO format.
Args:
audio: ComfyUI AUDIO dict with "waveform" [B, C, T] and "sample_rate" int.
Returns:
(waveform, sample_rate) where waveform is [B, C, T] float tensor.
"""
waveform = audio["waveform"]
sample_rate = audio["sample_rate"]
return waveform, sample_rate
def tensor_to_comfyui_audio(waveform: torch.Tensor, sample_rate: int) -> dict:
"""Convert a waveform tensor to ComfyUI AUDIO format.
Args:
waveform: [B, C, T] or [C, T] or [T] tensor.
sample_rate: Audio sample rate.
Returns:
ComfyUI AUDIO dict.
"""
if waveform.dim() == 1:
waveform = waveform.unsqueeze(0).unsqueeze(0) # [1, 1, T]
elif waveform.dim() == 2:
waveform = waveform.unsqueeze(0) # [1, C, T]
return {"waveform": waveform.cpu().float(), "sample_rate": sample_rate}
def ensure_mono(waveform: torch.Tensor) -> torch.Tensor:
"""Ensure audio is mono by averaging channels if stereo.
Args:
waveform: Audio tensor (1D, 2D [C, T], or 3D [B, C, T]).
Returns:
Mono audio tensor.
"""
if waveform.dim() == 1:
return waveform
elif waveform.dim() == 2:
if waveform.shape[0] > 1:
return waveform.mean(dim=0)
return waveform[0]
elif waveform.dim() == 3:
if waveform.shape[1] > 1:
return waveform.mean(dim=1, keepdim=True)
return waveform
return waveform
def resample_audio(waveform: torch.Tensor, orig_sr: int, target_sr: int) -> torch.Tensor:
"""Resample audio waveform if sample rates differ.
Args:
waveform: Audio tensor (any shape, last dim is time).
orig_sr: Original sample rate.
target_sr: Target sample rate.
Returns:
Resampled tensor (same shape except time dimension).
"""
if orig_sr == target_sr:
return waveform
import torchaudio
return torchaudio.functional.resample(waveform, orig_sr, target_sr)