ffasr / evaluation /runtime.py
whojavumusic's picture
added moving source eval
0ac9178
Raw
History Blame Contribute Delete
5.68 kB
"""Shared evaluation runtime: device selection, ZeroGPU bypass, CPU thread hints."""
from __future__ import annotations
import importlib.util
import os
import sys
import types
_cpu_thread_settings_applied = False
_torchcodec_patch_applied = False
def _force_transformers_torchcodec_unavailable() -> None:
"""Tell transformers to use librosa/soundfile for audio, not torchcodec."""
try:
import transformers.utils.import_utils as import_utils
import_utils._torchcodec_available = False
def _is_torchcodec_available() -> bool:
return False
import_utils.is_torchcodec_available = _is_torchcodec_available
except Exception:
pass
def disable_broken_torchcodec() -> None:
"""
Transformers prefers torchcodec for ``load_audio`` when the package is installed,
but Hub Job images often lack FFmpeg shared libraries (libavutil.so.*).
If torchcodec cannot load, mark it unavailable so transformers falls back to
librosa / soundfile.
"""
global _torchcodec_patch_applied
if _torchcodec_patch_applied:
return
_torchcodec_patch_applied = True
force_off = os.environ.get("FFASR_DISABLE_TORCHCODEC", "").strip().lower() in (
"1",
"true",
"yes",
"on",
)
if force_off or importlib.util.find_spec("torchcodec") is None:
if force_off and importlib.util.find_spec("torchcodec") is not None:
print(
"[ffasr] FFASR_DISABLE_TORCHCODEC=1: using librosa/soundfile for audio "
"(torchcodec disabled on Hub Jobs).",
flush=True,
)
_force_transformers_torchcodec_unavailable()
return
try:
from torchcodec.decoders import AudioDecoder # noqa: F401
except (OSError, RuntimeError, ImportError, AttributeError):
print(
"[ffasr] torchcodec native libraries unavailable; "
"transformers will use librosa/soundfile for audio.",
flush=True,
)
_force_transformers_torchcodec_unavailable()
stub = types.ModuleType("torchcodec")
stub.__dict__["decoders"] = types.ModuleType("torchcodec.decoders")
sys.modules["torchcodec"] = stub
sys.modules["torchcodec.decoders"] = stub.__dict__["decoders"]
except Exception:
_force_transformers_torchcodec_unavailable()
def patch_transformers_load_audio_for_paths() -> None:
"""
Route ``transformers.audio_utils.load_audio`` for file paths through soundfile
(no torchcodec / FFmpeg). Safe to call before loading user custom scripts.
"""
try:
import numpy as np
import transformers.audio_utils as audio_utils
from backends._audio_utils import load_wav_mono
_orig = audio_utils.load_audio
def _load_audio(audio, sampling_rate=16000, timeout=None):
if isinstance(audio, str):
return load_wav_mono(audio, sampling_rate=int(sampling_rate))
if isinstance(audio, np.ndarray):
return audio
return _orig(audio, sampling_rate=sampling_rate, timeout=timeout)
audio_utils.load_audio = _load_audio
except Exception:
pass
def zerogpu_disabled() -> bool:
"""When True, never wrap evaluation in ``spaces.GPU`` even if ``spaces`` is installed."""
return os.environ.get("FFASR_DISABLE_ZEROGPU", "").strip().lower() in (
"1",
"true",
"yes",
"on",
)
def spaces_available() -> bool:
return importlib.util.find_spec("spaces") is not None
def use_spaces_gpu_decorator() -> bool:
"""Use ZeroGPU decorator only when ``spaces`` is present and not explicitly disabled."""
return spaces_available() and not zerogpu_disabled()
def resolve_eval_devices() -> tuple[str, int]:
"""
Return ``(device_str, device_int)`` for backends / HF pipeline.
``FFASR_DEVICE`` (default ``auto``): ``cpu`` | ``cuda`` | ``auto``.
``auto`` uses CUDA when available, else CPU.
"""
choice = (os.environ.get("FFASR_DEVICE", "auto") or "auto").strip().lower()
if choice not in ("auto", "cpu", "cuda"):
choice = "auto"
import torch
has_cuda = torch.cuda.is_available()
if choice == "cpu":
return "cpu", -1
if choice == "cuda":
if not has_cuda:
raise RuntimeError("FFASR_DEVICE=cuda but torch.cuda.is_available() is False.")
return "cuda", 0
# auto
if has_cuda:
return "cuda", 0
return "cpu", -1
def apply_cpu_thread_settings_once() -> None:
"""
Apply optional torch thread limits from env (once per process).
- ``FFASR_TORCH_NUM_THREADS`` — passed to ``torch.set_num_threads`` if a positive int.
- ``FFASR_TORCH_NUM_INTEROP_THREADS`` — ``torch.set_num_interop_threads`` if set.
For BLAS/OpenMP, operators typically set ``OMP_NUM_THREADS`` / ``MKL_NUM_THREADS``
in the Space environment (see docs); those are read by the native libs at load time.
"""
global _cpu_thread_settings_applied
if _cpu_thread_settings_applied:
return
_cpu_thread_settings_applied = True
import torch
raw = os.environ.get("FFASR_TORCH_NUM_THREADS", "").strip()
if raw.isdigit():
n = int(raw)
if n > 0:
try:
torch.set_num_threads(n)
except Exception:
pass
raw_i = os.environ.get("FFASR_TORCH_NUM_INTEROP_THREADS", "").strip()
if raw_i.isdigit():
ni = int(raw_i)
if ni > 0:
try:
torch.set_num_interop_threads(ni)
except Exception:
pass