Commit b799fcd6 authored by hwangjeff's avatar hwangjeff Committed by Facebook GitHub Bot
Browse files

Introduce I/O backend dispatcher (#3015)

Summary:
Adds I/O backend dispatcher that routes I/O requests to FFmpeg, SoX, or Soundfile backend, per library availability. It allows users to specify a backend mapped to a media library, i.e. one of `["ffmpeg", "sox", "soundfile"]`, to use via keyword argument, with FFmpeg being the default. Environment variable `TORCHAUDIO_USE_BACKEND_DISPATCHER` gates enablement of the dispatcher; specifically, if `TORCHAUDIO_USE_BACKEND_DISPATCHER` is explicitly set to `1`, importing TorchAudio makes it accessible via `torchaudio.info`, `torchaudio.load`, and `torchaudio.save`.

Pull Request resolved: https://github.com/pytorch/audio/pull/3015

Reviewed By: mthrok

Differential Revision: D43258649

Pulled By: hwangjeff

fbshipit-source-id: 8f12e4e56b9fa3f0814dd3fed3e1783ab23a53a1
parent 9db4bdf1
This diff is collapsed.
# flake8: noqa # flake8: noqa
from . import utils import torchaudio
from .utils import get_audio_backend, list_audio_backends, set_audio_backend
from torchaudio._backend.utils import get_info_func, get_load_func, get_save_func
from . import utils
from .utils import _is_backend_dispatcher_enabled, get_audio_backend, list_audio_backends, set_audio_backend
utils._init_audio_backend() if _is_backend_dispatcher_enabled():
torchaudio.info = get_info_func()
torchaudio.load = get_load_func()
torchaudio.save = get_save_func()
else:
utils._init_audio_backend()
...@@ -20,8 +20,8 @@ class AudioMetaData: ...@@ -20,8 +20,8 @@ class AudioMetaData:
* ``ALAW``: A-law * ``ALAW``: A-law
* ``MP3`` : MP3, MPEG-1 Audio Layer III * ``MP3`` : MP3, MPEG-1 Audio Layer III
* ``VORBIS``: OGG Vorbis * ``VORBIS``: OGG Vorbis
* ``AMR_WB``: Adaptive Multi-Rate * ``AMR_WB``: Adaptive Multi-Rate Wideband
* ``AMR_NB``: Adaptive Multi-Rate Wideband * ``AMR_NB``: Adaptive Multi-Rate Narrowband
* ``OPUS``: Opus * ``OPUS``: Opus
* ``HTK``: Single channel 16-bit PCM * ``HTK``: Single channel 16-bit PCM
* ``UNKNOWN`` : None of above * ``UNKNOWN`` : None of above
......
...@@ -8,12 +8,15 @@ from torchaudio._internal import module_utils as _mod_utils ...@@ -8,12 +8,15 @@ from torchaudio._internal import module_utils as _mod_utils
from .common import AudioMetaData from .common import AudioMetaData
_IS_SOUNDFILE_AVAILABLE = False
# TODO: import soundfile only when it is used. # TODO: import soundfile only when it is used.
if _mod_utils.is_module_available("soundfile"): if _mod_utils.is_module_available("soundfile"):
try: try:
import soundfile import soundfile
_requires_soundfile = _mod_utils.no_op _requires_soundfile = _mod_utils.no_op
_IS_SOUNDFILE_AVAILABLE = True
except Exception: except Exception:
_requires_soundfile = _mod_utils.fail_with_message( _requires_soundfile = _mod_utils.fail_with_message(
"requires soundfile, but we failed to import it. Please check the installation of soundfile." "requires soundfile, but we failed to import it. Please check the installation of soundfile."
......
"""Defines utilities for switching audio backends""" """Defines utilities for switching audio backends"""
import os
import warnings import warnings
from typing import List, Optional from typing import List, Optional
...@@ -14,12 +15,19 @@ __all__ = [ ...@@ -14,12 +15,19 @@ __all__ = [
] ]
def _is_backend_dispatcher_enabled() -> bool:
return os.getenv("TORCHAUDIO_USE_BACKEND_DISPATCHER") == "1"
def list_audio_backends() -> List[str]: def list_audio_backends() -> List[str]:
"""List available backends """List available backends
Returns: Returns:
List[str]: The list of available backends. List[str]: The list of available backends.
""" """
if _is_backend_dispatcher_enabled():
warnings.warn("list_audio_backend's return value is irrelevant when the I/O backend dispatcher is enabled.")
backends = [] backends = []
if _mod_utils.is_module_available("soundfile"): if _mod_utils.is_module_available("soundfile"):
backends.append("soundfile") backends.append("soundfile")
...@@ -36,6 +44,10 @@ def set_audio_backend(backend: Optional[str]): ...@@ -36,6 +44,10 @@ def set_audio_backend(backend: Optional[str]):
One of ``"sox_io"`` or ``"soundfile"`` based on availability One of ``"sox_io"`` or ``"soundfile"`` based on availability
of the system. If ``None`` is provided the current backend is unassigned. of the system. If ``None`` is provided the current backend is unassigned.
""" """
if _is_backend_dispatcher_enabled():
warnings.warn("set_audio_backend is a no-op when the I/O backend dispatcher is enabled.")
return
if backend is not None and backend not in list_audio_backends(): if backend is not None and backend not in list_audio_backends():
raise RuntimeError(f'Backend "{backend}" is not one of ' f"available backends: {list_audio_backends()}.") raise RuntimeError(f'Backend "{backend}" is not one of ' f"available backends: {list_audio_backends()}.")
...@@ -69,6 +81,9 @@ def get_audio_backend() -> Optional[str]: ...@@ -69,6 +81,9 @@ def get_audio_backend() -> Optional[str]:
Returns: Returns:
Optional[str]: The name of the current backend or ``None`` if no backend is assigned. Optional[str]: The name of the current backend or ``None`` if no backend is assigned.
""" """
if _is_backend_dispatcher_enabled():
warnings.warn("get_audio_backend's return value is irrelevant when the I/O backend dispatcher is enabled.")
if torchaudio.load == no_backend.load: if torchaudio.load == no_backend.load:
return None return None
if torchaudio.load == sox_io_backend.load: if torchaudio.load == sox_io_backend.load:
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment