Files
diodiogod-TTS-Audio-Suite/utils/compatibility/step_audio_editx_device_patch.py
diodiogod 72f10c96fd Version 4.20.9
Add Step Audio EditX Mac (MPS) compatibility patch

Technical details:
- Create centralized device compatibility patch system
- Monkey-patch hardcoded CUDA device calls in upsample_encoder_v2.py
- Detect device from model parameters instead of hardcoding
- Gracefully disable CUDA Graphs on non-CUDA devices (MPS/CPU)
- Apply patch automatically when Step Audio EditX engine loads

Addresses issue #245 (requires Mac user testing)
2026-02-04 00:36:35 -03:00

143 lines
5.7 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""
Step Audio EditX Device Compatibility Patch
Patches hardcoded CUDA device calls to support MPS (Apple Silicon) and CPU.
Issue: Step Audio EditX bundled implementation (from Alibaba) has hardcoded
torch.device('cuda') calls that fail on Mac (MPS) and CPU-only systems.
Affected file:
- stepvocoder/cosyvoice2/transformer/upsample_encoder_v2.py
Lines: 270, 272, 274, 306, 308, 310
Solution: Monkey-patch the _init_cuda_graph method to use the actual device
from self.linear_out (which is correctly set during model initialization).
"""
import warnings
from typing import Optional
class StepAudioEditXDevicePatches:
"""Centralized Step Audio EditX device compatibility patches manager"""
_patches_applied = set()
@classmethod
def apply_all_patches(cls, verbose: bool = True):
"""Apply all Step Audio EditX device compatibility patches"""
cls.patch_cuda_graph_device(verbose=verbose)
@classmethod
def patch_cuda_graph_device(cls, verbose: bool = True):
"""
Patch hardcoded CUDA device in CUDA Graph initialization.
Issue: upsample_encoder_v2.py hardcodes torch.device('cuda') in _init_cuda_graph
This fails on Mac (MPS) and CPU-only systems with "Torch not compiled with CUDA enabled"
Solution: Replace _init_cuda_graph method to dynamically detect device from model
"""
if "cuda_graph_device" in cls._patches_applied:
return
try:
# Import the module that needs patching
import sys
import os
# Add Step Audio EditX impl to path if not already
impl_path = None
for path_item in sys.path:
if 'step_audio_editx_impl' in path_item or path_item.endswith('step_audio_editx/impl'):
impl_path = path_item
break
if impl_path is None:
# Try to find it relative to this file
compat_dir = os.path.dirname(os.path.abspath(__file__))
project_root = os.path.dirname(os.path.dirname(compat_dir))
impl_path = os.path.join(project_root, 'engines', 'step_audio_editx', 'step_audio_editx_impl')
if os.path.exists(impl_path) and impl_path not in sys.path:
sys.path.insert(0, impl_path)
# Try to import the module
try:
from stepvocoder.cosyvoice2.transformer.upsample_encoder_v2 import UpsampleEncoderV2
except ImportError:
# Module not loaded yet, patch will be applied when it loads
if verbose:
print("ℹ️ [Step Audio EditX Device Patch] Module not loaded yet, will patch on first use")
return
# Check if already patched
if hasattr(UpsampleEncoderV2._init_cuda_graph, '_device_patched'):
if verbose:
print("✓ [Step Audio EditX Device Patch] Already applied")
cls._patches_applied.add("cuda_graph_device")
return
# Save original method
original_init_cuda_graph = UpsampleEncoderV2._init_cuda_graph
# Track if we've already logged for non-CUDA devices (to avoid spam)
_logged_non_cuda = {'logged': False}
# Create patched version
def patched_init_cuda_graph(self):
"""
Patched version that uses dynamic device instead of hardcoded 'cuda'.
Detects device from model's parameters (self.linear_out) and uses that
for CUDA Graph initialization. Falls back to disabling CUDA graphs if
not on CUDA device.
"""
import torch
# Detect actual device from model
if hasattr(self, 'linear_out') and hasattr(self.linear_out, 'weight'):
device = self.linear_out.weight.device
else:
# Fallback: assume cuda if cuda is available
device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
# CUDA Graphs only work on CUDA devices
if device.type != 'cuda':
# Log once per session for non-CUDA devices
if not _logged_non_cuda['logged']:
print(f"ℹ️ Step Audio EditX: CUDA Graphs disabled (running on {device.type})")
_logged_non_cuda['logged'] = True
self.enable_cuda_graph = False
return
# Call original implementation (it will use CUDA correctly)
original_init_cuda_graph(self)
# Mark as patched
patched_init_cuda_graph._device_patched = True
# Apply patch
UpsampleEncoderV2._init_cuda_graph = patched_init_cuda_graph
if verbose:
print("✓ [Step Audio EditX Device Patch] Applied CUDA Graph device compatibility patch")
cls._patches_applied.add("cuda_graph_device")
except ImportError as e:
if verbose:
print(f"⚠️ [Step Audio EditX Device Patch] Could not apply patch (module not available): {e}")
except Exception as e:
if verbose:
print(f"⚠️ [Step Audio EditX Device Patch] Failed to apply patch: {e}")
# Auto-apply patches on import
def auto_apply_patches():
"""Automatically apply patches when this module is imported"""
StepAudioEditXDevicePatches.apply_all_patches(verbose=False)
# Export
__all__ = ["StepAudioEditXDevicePatches", "auto_apply_patches"]