Skip to content

Commit dde08ba

Browse files
authored
Simplify the logic to initialize sox
Differential Revision: D50197331 Pull Request resolved: #3654
1 parent f62367a commit dde08ba

File tree

14 files changed

+107
-131
lines changed

14 files changed

+107
-131
lines changed

src/libtorchaudio/sox/effects.cpp

Lines changed: 0 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -130,17 +130,4 @@ auto apply_effects_file(
130130
return std::tuple<torch::Tensor, int64_t>(
131131
tensor, chain.getOutputSampleRate());
132132
}
133-
134-
namespace {
135-
136-
TORCH_LIBRARY_FRAGMENT(torchaudio, m) {
137-
m.def(
138-
"torchaudio::sox_effects_initialize_sox_effects",
139-
&initialize_sox_effects);
140-
m.def("torchaudio::sox_effects_shutdown_sox_effects", &shutdown_sox_effects);
141-
m.def("torchaudio::sox_effects_apply_effects_tensor", &apply_effects_tensor);
142-
m.def("torchaudio::sox_effects_apply_effects_file", &apply_effects_file);
143-
}
144-
145-
} // namespace
146133
} // namespace torchaudio::sox

src/libtorchaudio/sox/io.cpp

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -125,11 +125,4 @@ void save_audio_file(
125125
chain.addOutputFile(sf);
126126
chain.run();
127127
}
128-
129-
TORCH_LIBRARY_FRAGMENT(torchaudio, m) {
130-
m.def("torchaudio::sox_io_get_info", &get_info_file);
131-
m.def("torchaudio::sox_io_load_audio_file", &load_audio_file);
132-
m.def("torchaudio::sox_io_save_audio_file", &save_audio_file);
133-
}
134-
135128
} // namespace torchaudio::sox

src/libtorchaudio/sox/pybind/pybind.cpp

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,22 @@
1+
#include <libtorchaudio/sox/effects.h>
2+
#include <libtorchaudio/sox/io.h>
13
#include <libtorchaudio/sox/utils.h>
24
#include <torch/extension.h>
35

46
namespace torchaudio {
57
namespace sox {
68
namespace {
79

10+
TORCH_LIBRARY(torchaudio_sox, m) {
11+
m.def("torchaudio_sox::get_info", &get_info_file);
12+
m.def("torchaudio_sox::load_audio_file", &load_audio_file);
13+
m.def("torchaudio_sox::save_audio_file", &save_audio_file);
14+
m.def("torchaudio_sox::initialize_sox_effects", &initialize_sox_effects);
15+
m.def("torchaudio_sox::shutdown_sox_effects", &shutdown_sox_effects);
16+
m.def("torchaudio_sox::apply_effects_tensor", &apply_effects_tensor);
17+
m.def("torchaudio_sox::apply_effects_file", &apply_effects_file);
18+
}
19+
820
PYBIND11_MODULE(_torchaudio_sox, m) {
921
m.def("set_seed", &set_seed, "Set random seed.");
1022
m.def("set_verbosity", &set_verbosity, "Set verbosity.");

src/torchaudio/_backend/sox.py

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,13 @@
22
from typing import BinaryIO, Optional, Tuple, Union
33

44
import torch
5+
import torchaudio
56

67
from .backend import Backend
78
from .common import AudioMetaData
89

10+
sox_ext = torchaudio._extension.lazy_import_sox_ext()
11+
912

1013
class SoXBackend(Backend):
1114
@staticmethod
@@ -16,7 +19,7 @@ def info(uri: Union[BinaryIO, str, os.PathLike], format: Optional[str], buffer_s
1619
"Please use an alternative backend that does support reading from file-like objects, e.g. FFmpeg.",
1720
)
1821
else:
19-
sinfo = torch.ops.torchaudio.sox_io_get_info(uri, format)
22+
sinfo = sox_ext.get_info(uri, format)
2023
if sinfo:
2124
return AudioMetaData(*sinfo)
2225
else:
@@ -38,9 +41,7 @@ def load(
3841
"Please use an alternative backend that does support loading from file-like objects, e.g. FFmpeg.",
3942
)
4043
else:
41-
ret = torch.ops.torchaudio.sox_io_load_audio_file(
42-
uri, frame_offset, num_frames, normalize, channels_first, format
43-
)
44+
ret = sox_ext.load_audio_file(uri, frame_offset, num_frames, normalize, channels_first, format)
4445
if not ret:
4546
raise RuntimeError(f"Failed to load audio from {uri}.")
4647
return ret
@@ -62,7 +63,7 @@ def save(
6263
"Please use an alternative backend that does support writing to file-like objects, e.g. FFmpeg.",
6364
)
6465
else:
65-
torch.ops.torchaudio.sox_io_save_audio_file(
66+
sox_ext.save_audio_file(
6667
uri,
6768
src,
6869
sample_rate,

src/torchaudio/_backend/utils.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44

55
import torch
66

7-
from torchaudio._extension import _SOX_INITIALIZED, lazy_import_ffmpeg_ext
7+
from torchaudio._extension import lazy_import_ffmpeg_ext, lazy_import_sox_ext
88

99
from . import soundfile_backend
1010

@@ -20,7 +20,7 @@ def get_available_backends() -> Dict[str, Type[Backend]]:
2020
backend_specs: Dict[str, Type[Backend]] = {}
2121
if lazy_import_ffmpeg_ext().is_available():
2222
backend_specs["ffmpeg"] = FFmpegBackend
23-
if _SOX_INITIALIZED:
23+
if lazy_import_sox_ext().is_available():
2424
backend_specs["sox"] = SoXBackend
2525
if soundfile_backend._IS_SOUNDFILE_AVAILABLE:
2626
backend_specs["soundfile"] = SoundfileBackend

src/torchaudio/_extension/__init__.py

Lines changed: 14 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -2,17 +2,9 @@
22
import os
33
import sys
44

5-
from torchaudio._internal.module_utils import eval_env, fail_with_message, is_module_available, no_op
6-
7-
from .utils import (
8-
_check_cuda_version,
9-
_fail_since_no_sox,
10-
_init_dll_path,
11-
_init_ffmpeg,
12-
_init_sox,
13-
_LazyImporter,
14-
_load_lib,
15-
)
5+
from torchaudio._internal.module_utils import fail_with_message, is_module_available, no_op
6+
7+
from .utils import _check_cuda_version, _init_dll_path, _init_ffmpeg, _init_sox, _LazyImporter, _load_lib
168

179
_LG = logging.getLogger(__name__)
1810

@@ -22,11 +14,10 @@
2214
# Builder uses it for debugging purpose, so we export it.
2315
# https://github.com/pytorch/builder/blob/e2e4542b8eb0bdf491214451a1a4128bd606cce2/test/smoke_test/smoke_test.py#L80
2416
__all__ = [
25-
"fail_if_no_sox",
2617
"_check_cuda_version",
2718
"_IS_TORCHAUDIO_EXT_AVAILABLE",
2819
"_IS_RIR_AVAILABLE",
29-
"_SOX_INITIALIZED",
20+
"lazy_import_sox_ext",
3021
"lazy_import_ffmpeg_ext",
3122
]
3223

@@ -54,34 +45,16 @@
5445
_IS_ALIGN_AVAILABLE = torchaudio.lib._torchaudio.is_align_available()
5546

5647

57-
# Initialize libsox-related features
58-
_SOX_INITIALIZED = False
59-
_USE_SOX = False if os.name == "nt" else eval_env("TORCHAUDIO_USE_SOX", True)
60-
_SOX_MODULE_AVAILABLE = is_module_available("torchaudio.lib._torchaudio_sox")
61-
if _USE_SOX and _SOX_MODULE_AVAILABLE:
62-
try:
63-
_init_sox()
64-
_SOX_INITIALIZED = True
65-
except Exception:
66-
# The initialization of sox extension will fail if supported sox
67-
# libraries are not found in the system.
68-
# Since the rest of the torchaudio works without it, we do not report the
69-
# error here.
70-
# The error will be raised when user code attempts to use these features.
71-
_LG.debug("Failed to initialize sox extension", exc_info=True)
72-
73-
74-
if os.name == "nt":
75-
fail_if_no_sox = fail_with_message("requires sox extension, which is not supported on Windows.")
76-
elif not _USE_SOX:
77-
fail_if_no_sox = fail_with_message("requires sox extension, but it is disabled. (TORCHAUDIO_USE_SOX=0)")
78-
elif not _SOX_MODULE_AVAILABLE:
79-
fail_if_no_sox = fail_with_message(
80-
"requires sox extension, but TorchAudio is not compiled with it. "
81-
"Please build TorchAudio with libsox support. (BUILD_SOX=1)"
82-
)
83-
else:
84-
fail_if_no_sox = no_op if _SOX_INITIALIZED else _fail_since_no_sox
48+
_SOX_EXT = None
49+
50+
51+
def lazy_import_sox_ext():
52+
"""Load SoX integration based on availability in lazy manner"""
53+
54+
global _SOX_EXT
55+
if _SOX_EXT is None:
56+
_SOX_EXT = _LazyImporter("_torchaudio_sox", _init_sox)
57+
return _SOX_EXT
8558

8659

8760
_FFMPEG_EXT = None

src/torchaudio/_extension/utils.py

Lines changed: 39 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,10 @@
99
import logging
1010
import os
1111
import types
12-
from functools import wraps
1312
from pathlib import Path
1413

1514
import torch
15+
from torchaudio._internal.module_utils import eval_env
1616

1717
_LG = logging.getLogger(__name__)
1818
_LIB_DIR = Path(__file__).parent.parent / "lib"
@@ -62,16 +62,49 @@ def _load_lib(lib: str) -> bool:
6262
return True
6363

6464

65-
def _init_sox():
65+
def _import_sox_ext():
66+
if os.name == "nt":
67+
raise RuntimeError("sox extension is not supported on Windows")
68+
if not eval_env("TORCHAUDIO_USE_SOX", True):
69+
raise RuntimeError("sox extension is disabled. (TORCHAUDIO_USE_SOX=0)")
70+
71+
ext = "torchaudio.lib._torchaudio_sox"
72+
73+
if not importlib.util.find_spec(ext):
74+
raise RuntimeError(
75+
# fmt: off
76+
"TorchAudio is not built with sox extension. "
77+
"Please build TorchAudio with libsox support. (BUILD_SOX=1)"
78+
# fmt: on
79+
)
80+
6681
_load_lib("libtorchaudio_sox")
67-
import torchaudio.lib._torchaudio_sox # noqa
82+
return importlib.import_module(ext)
6883

69-
torchaudio.lib._torchaudio_sox.set_verbosity(0)
84+
85+
def _init_sox():
86+
ext = _import_sox_ext()
87+
ext.set_verbosity(0)
7088

7189
import atexit
7290

73-
torch.ops.torchaudio.sox_effects_initialize_sox_effects()
74-
atexit.register(torch.ops.torchaudio.sox_effects_shutdown_sox_effects)
91+
torch.ops.torchaudio_sox.initialize_sox_effects()
92+
atexit.register(torch.ops.torchaudio_sox.shutdown_sox_effects)
93+
94+
# Bundle functions registered with TORCH_LIBRARY into extension
95+
# so that they can also be accessed in the same (lazy) manner
96+
# from the extension.
97+
keys = [
98+
"get_info",
99+
"load_audio_file",
100+
"save_audio_file",
101+
"apply_effects_tensor",
102+
"apply_effects_file",
103+
]
104+
for key in keys:
105+
setattr(ext, key, getattr(torch.ops.torchaudio_sox, key))
106+
107+
return ext
75108

76109

77110
_FFMPEG_VERS = ["6", "5", "4", ""]
@@ -197,22 +230,3 @@ def _check_cuda_version():
197230
"Please install the TorchAudio version that matches your PyTorch version."
198231
)
199232
return version
200-
201-
202-
def _fail_since_no_sox(func):
203-
@wraps(func)
204-
def wrapped(*_args, **_kwargs):
205-
try:
206-
# Note:
207-
# We run _init_sox again just to show users the stacktrace.
208-
# _init_sox would not succeed here.
209-
_init_sox()
210-
except Exception as err:
211-
raise RuntimeError(
212-
f"{func.__name__} requires sox extension which is not available. "
213-
"Please refer to the stacktrace above for how to resolve this."
214-
) from err
215-
# This should not happen in normal execution, but just in case.
216-
return func(*_args, **_kwargs)
217-
218-
return wrapped

src/torchaudio/backend/_sox_io_backend.py

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,9 @@
55
import torchaudio
66
from torchaudio import AudioMetaData
77

8+
sox_ext = torchaudio._extension.lazy_import_sox_ext()
9+
810

9-
@torchaudio._extension.fail_if_no_sox
1011
def info(
1112
filepath: str,
1213
format: Optional[str] = None,
@@ -29,11 +30,10 @@ def info(
2930
if hasattr(filepath, "read"):
3031
raise RuntimeError("sox_io backend does not support file-like object.")
3132
filepath = os.fspath(filepath)
32-
sinfo = torch.ops.torchaudio.sox_io_get_info(filepath, format)
33+
sinfo = sox_ext.get_info(filepath, format)
3334
return AudioMetaData(*sinfo)
3435

3536

36-
@torchaudio._extension.fail_if_no_sox
3737
def load(
3838
filepath: str,
3939
frame_offset: int = 0,
@@ -123,12 +123,9 @@ def load(
123123
if hasattr(filepath, "read"):
124124
raise RuntimeError("sox_io backend does not support file-like object.")
125125
filepath = os.fspath(filepath)
126-
return torch.ops.torchaudio.sox_io_load_audio_file(
127-
filepath, frame_offset, num_frames, normalize, channels_first, format
128-
)
126+
return sox_ext.load_audio_file(filepath, frame_offset, num_frames, normalize, channels_first, format)
129127

130128

131-
@torchaudio._extension.fail_if_no_sox
132129
def save(
133130
filepath: str,
134131
src: torch.Tensor,
@@ -285,7 +282,7 @@ def save(
285282
if hasattr(filepath, "write"):
286283
raise RuntimeError("sox_io backend does not handle file-like object.")
287284
filepath = os.fspath(filepath)
288-
torch.ops.torchaudio.sox_io_save_audio_file(
285+
sox_ext.save_audio_file(
289286
filepath,
290287
src,
291288
sample_rate,

src/torchaudio/functional/functional.py

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1295,7 +1295,6 @@ def spectral_centroid(
12951295
return (freqs * specgram).sum(dim=freq_dim) / specgram.sum(dim=freq_dim)
12961296

12971297

1298-
@torchaudio._extension.fail_if_no_sox
12991298
@deprecated("Please migrate to :py:class:`torchaudio.io.AudioEffector`.", remove=False)
13001299
def apply_codec(
13011300
waveform: Tensor,
@@ -1329,11 +1328,13 @@ def apply_codec(
13291328
Tensor: Resulting Tensor.
13301329
If ``channels_first=True``, it has `(channel, time)` else `(time, channel)`.
13311330
"""
1331+
from torchaudio.backend import _sox_io_backend
1332+
13321333
with tempfile.NamedTemporaryFile() as f:
1333-
torchaudio.backend.sox_io_backend.save(
1334+
torchaudio.backend._sox_io_backend.save(
13341335
f.name, waveform, sample_rate, channels_first, compression, format, encoding, bits_per_sample
13351336
)
1336-
augmented, sr = torchaudio.backend.sox_io_backend.load(f.name, channels_first=channels_first, format=format)
1337+
augmented, sr = _sox_io_backend.load(f.name, channels_first=channels_first, format=format)
13371338
if sr != sample_rate:
13381339
augmented = resample(augmented, sr, sample_rate)
13391340
return augmented
@@ -1371,7 +1372,8 @@ def _get_sinc_resample_kernel(
13711372
warnings.warn(
13721373
f'"{resampling_method}" resampling method name is being deprecated and replaced by '
13731374
f'"{method_map[resampling_method]}" in the next release. '
1374-
"The default behavior remains unchanged."
1375+
"The default behavior remains unchanged.",
1376+
stacklevel=3,
13751377
)
13761378
elif resampling_method not in ["sinc_interp_hann", "sinc_interp_kaiser"]:
13771379
raise ValueError("Invalid resampling method: {}".format(resampling_method))

0 commit comments

Comments
 (0)