Removed torchcodec from requirements.txt and install.py to eliminate the FFmpeg system dependency that was causing Windows installation failures. Implementation: - Removed torchcodec>=0.2.0 from requirements.txt - Removed torchcodec>=0.2.0 from install.py core_packages list - Removed torchaudio 2.9+ migration warnings (no longer applicable) - Added AudioProcessingUtils.safe_load_audio() utility for robust audio loading - Updated all audio loading calls to use fallback-aware utility function Audio Loading Changes: - character_voices_node.py: Uses safe_load_audio() with fallback support - tts_text_node.py: Uses safe_load_audio() for narrator and character voices (2x) - tts_srt_node.py: Uses safe_load_audio() for narrator voice - higgs_audio_tts_processor.py: Uses safe_load_audio() (2x) - higgs_audio_srt_processor.py: Uses safe_load_audio() (2x) - index_tts_processor.py: Uses safe_load_audio() (2x) - f5tts_streaming_adapter.py: Uses safe_load_audio() - higgs_audio_adapter.py: Uses safe_load_audio() - subprocess.py: Uses safe_load_audio() Compatibility: - torchaudio.load() works fine with fallback backends (soundfile, scipy) - No quality loss - only negligible audio loading speed difference (~milliseconds) - All existing audio formats supported - Better error handling with graceful fallbacks
115 lines
4.1 KiB
Python
115 lines
4.1 KiB
Python
#!/usr/bin/env python3
|
|
"""
|
|
ChatterBox TTS Subprocess Wrapper
|
|
|
|
This script runs ChatterBox TTS generation in an isolated subprocess to prevent
|
|
CUDA crashes from affecting the main ComfyUI process.
|
|
|
|
Usage:
|
|
python chatterbox_subprocess.py --text "Hello world" --reference_audio "path/to/ref.wav" --output "output.wav"
|
|
"""
|
|
|
|
import sys
|
|
import os
|
|
import argparse
|
|
import json
|
|
import traceback
|
|
import tempfile
|
|
import torch
|
|
import numpy as np
|
|
from pathlib import Path
|
|
|
|
# Add the project root to Python path
|
|
project_root = Path(__file__).parent.parent
|
|
sys.path.insert(0, str(project_root))
|
|
|
|
def main():
|
|
parser = argparse.ArgumentParser(description='ChatterBox TTS Subprocess')
|
|
parser.add_argument('--text', required=True, help='Text to synthesize')
|
|
parser.add_argument('--reference_audio', required=True, help='Path to reference audio')
|
|
parser.add_argument('--output', required=True, help='Output audio file path')
|
|
parser.add_argument('--device', default='auto', help='Device to use (auto/cuda/cpu)')
|
|
parser.add_argument('--exaggeration', type=float, default=0.5, help='Exaggeration factor')
|
|
parser.add_argument('--temperature', type=float, default=0.8, help='Temperature')
|
|
parser.add_argument('--cfg_weight', type=float, default=0.5, help='CFG weight')
|
|
parser.add_argument('--seed', type=int, default=0, help='Random seed')
|
|
|
|
args = parser.parse_args()
|
|
|
|
try:
|
|
# Import ChatterBox TTS modules
|
|
from engines.chatterbox.tts import ChatterboxTTS
|
|
import torchaudio
|
|
|
|
print(f"🔄 Subprocess: Loading ChatterBox TTS on {args.device}...")
|
|
|
|
# Initialize ChatterBox TTS
|
|
if args.device == 'auto':
|
|
device = 'cuda' if torch.cuda.is_available() else 'cpu'
|
|
else:
|
|
device = args.device
|
|
|
|
chatterbox = ChatterboxTTS.from_pretrained(device=device)
|
|
|
|
print(f"🔄 Subprocess: Loading reference audio from {args.reference_audio}...")
|
|
|
|
# Load reference audio with fallback support
|
|
from utils.audio.processing import AudioProcessingUtils
|
|
ref_audio, sample_rate = AudioProcessingUtils.safe_load_audio(args.reference_audio)
|
|
|
|
# Ensure mono audio
|
|
if ref_audio.shape[0] > 1:
|
|
ref_audio = ref_audio.mean(dim=0, keepdim=True)
|
|
|
|
# Resample if necessary
|
|
if sample_rate != chatterbox.sr:
|
|
resampler = torchaudio.transforms.Resample(sample_rate, chatterbox.sr)
|
|
ref_audio = resampler(ref_audio)
|
|
|
|
print(f"🔄 Subprocess: Generating speech for text: '{args.text[:50]}...'")
|
|
|
|
# Set seed for reproducibility
|
|
if args.seed != 0:
|
|
torch.manual_seed(args.seed)
|
|
if torch.cuda.is_available():
|
|
torch.cuda.manual_seed(args.seed)
|
|
|
|
# Generate audio
|
|
generated_audio = chatterbox.generate(
|
|
text=args.text,
|
|
reference_audio=ref_audio,
|
|
exaggeration=args.exaggeration,
|
|
temperature=args.temperature,
|
|
cfg_weight=args.cfg_weight
|
|
)
|
|
|
|
print(f"🔄 Subprocess: Saving audio to {args.output}...")
|
|
|
|
# Save output audio
|
|
torchaudio.save(args.output, generated_audio.cpu(), chatterbox.sr)
|
|
|
|
# Return success info
|
|
duration = generated_audio.size(-1) / chatterbox.sr
|
|
result = {
|
|
'success': True,
|
|
'output_path': args.output,
|
|
'duration': duration,
|
|
'sample_rate': chatterbox.sr,
|
|
'audio_shape': list(generated_audio.shape)
|
|
}
|
|
|
|
print(f"✅ Subprocess: Generation completed successfully ({duration:.2f}s)")
|
|
print(json.dumps(result))
|
|
|
|
except Exception as e:
|
|
error_result = {
|
|
'success': False,
|
|
'error': str(e),
|
|
'traceback': traceback.format_exc()
|
|
}
|
|
print(f"❌ Subprocess: Generation failed: {e}")
|
|
print(json.dumps(error_result))
|
|
sys.exit(1)
|
|
|
|
if __name__ == "__main__":
|
|
main() |