diff --git a/.github/scripts/unittest-linux/install.sh b/.github/scripts/unittest-linux/install.sh index 885296219c..70a8793c22 100755 --- a/.github/scripts/unittest-linux/install.sh +++ b/.github/scripts/unittest-linux/install.sh @@ -75,7 +75,7 @@ fi # Note: installing librosa via pip fail because it will try to compile numba. ( set -x - conda install -y -c conda-forge ${NUMBA_DEV_CHANNEL} 'librosa==0.10.0' parameterized 'requests>=2.20' 'ffmpeg>=5,<7' + conda install -y -c conda-forge ${NUMBA_DEV_CHANNEL} sox libvorbis 'librosa==0.10.0' parameterized 'requests>=2.20' 'ffmpeg>=5,<7' pip install kaldi-io SoundFile coverage pytest pytest-cov 'scipy==1.7.3' expecttest unidecode inflect Pillow sentencepiece pytorch-lightning 'protobuf<4.21.0' demucs tinytag pyroomacoustics flashlight-text git+https://github.com/kpu/kenlm ) # Install fairseq diff --git a/.github/workflows/build_docs.yml b/.github/workflows/build_docs.yml index d87f6d0125..9f9c6eea35 100644 --- a/.github/workflows/build_docs.yml +++ b/.github/workflows/build_docs.yml @@ -75,7 +75,7 @@ jobs: conda install \ --quiet --yes \ -c conda-forge \ - pandoc doxygen pysoundfile + sox libvorbis pandoc doxygen pysoundfile pip install --progress-bar off \ git+https://github.com/kpu/kenlm/ flashlight-text \ -r docs/requirements.txt -r docs/requirements-tutorials.txt diff --git a/.github/workflows/unittest-linux-cpu.yml b/.github/workflows/unittest-linux-cpu.yml index 50207c0131..b96dd200cb 100644 --- a/.github/workflows/unittest-linux-cpu.yml +++ b/.github/workflows/unittest-linux-cpu.yml @@ -50,6 +50,8 @@ jobs: export TORCHAUDIO_TEST_ALLOW_SKIP_IF_NO_AUDIO_OUT_DEVICE=true export TORCHAUDIO_TEST_ALLOW_SKIP_IF_NO_MACOS=true export TORCHAUDIO_TEST_ALLOW_SKIP_IF_TEMPORARY_DISABLED=true + export TORCHAUDIO_TEST_ALLOW_SKIP_IF_NO_SOX_DECODER=true + export TORCHAUDIO_TEST_ALLOW_SKIP_IF_NO_SOX_ENCODER=true echo '::endgroup::' set -euxo pipefail diff --git a/.github/workflows/unittest-linux-gpu.yml b/.github/workflows/unittest-linux-gpu.yml index 86ab9dcd16..d74489f90f 100644 --- a/.github/workflows/unittest-linux-gpu.yml +++ b/.github/workflows/unittest-linux-gpu.yml @@ -38,6 +38,8 @@ jobs: export TORCHAUDIO_TEST_ALLOW_SKIP_IF_CUDA_SMALL_MEMORY=true export TORCHAUDIO_TEST_ALLOW_SKIP_IF_ON_PYTHON_310=true export TORCHAUDIO_TEST_ALLOW_SKIP_IF_TEMPORARY_DISABLED=true + export TORCHAUDIO_TEST_ALLOW_SKIP_IF_NO_SOX_DECODER=true + export TORCHAUDIO_TEST_ALLOW_SKIP_IF_NO_SOX_ENCODER=true # Set CHANNEL if [[(${GITHUB_EVENT_NAME} = 'pull_request' && (${GITHUB_BASE_REF} = 'release'*)) || (${GITHUB_REF} = 'refs/heads/release'*) ]]; then @@ -76,7 +78,7 @@ jobs: --quiet --yes \ -c conda-forge \ -c numba/label/dev \ - 'librosa==0.10.0' parameterized 'requests>=2.20' + sox libvorbis 'librosa==0.10.0' parameterized 'requests>=2.20' pip3 install --progress-bar off \ kaldi-io \ SoundFile \ diff --git a/.github/workflows/unittest-macos-cpu.yml b/.github/workflows/unittest-macos-cpu.yml index 4e3cc59879..274d5eb648 100644 --- a/.github/workflows/unittest-macos-cpu.yml +++ b/.github/workflows/unittest-macos-cpu.yml @@ -49,6 +49,8 @@ jobs: export TORCHAUDIO_TEST_ALLOW_SKIP_IF_NO_MOD_sentencepiece=true export TORCHAUDIO_TEST_ALLOW_SKIP_IF_NO_AUDIO_OUT_DEVICE=true export TORCHAUDIO_TEST_ALLOW_SKIP_IF_TEMPORARY_DISABLED=true + export TORCHAUDIO_TEST_ALLOW_SKIP_IF_NO_SOX_DECODER=true + export TORCHAUDIO_TEST_ALLOW_SKIP_IF_NO_SOX_ENCODER=true echo '::endgroup::' set -euxo pipefail diff --git a/.github/workflows/unittest-windows-cpu.yml b/.github/workflows/unittest-windows-cpu.yml index e2eb73674d..a3ed8e3482 100644 --- a/.github/workflows/unittest-windows-cpu.yml +++ b/.github/workflows/unittest-windows-cpu.yml @@ -45,6 +45,7 @@ jobs: export TORCHAUDIO_TEST_ALLOW_SKIP_IF_NO_MOD_sentencepiece=true export TORCHAUDIO_TEST_ALLOW_SKIP_IF_NO_AUDIO_OUT_DEVICE=true export TORCHAUDIO_TEST_ALLOW_SKIP_IF_NO_MACOS=true + export TORCHAUDIO_TEST_ALLOW_SKIP_IF_TEMPORARY_DISABLED=true .github/scripts/unittest-windows/setup_env.sh .github/scripts/unittest-windows/install.sh diff --git a/setup.py b/setup.py index 0a8080d06e..38a4ab3449 100644 --- a/setup.py +++ b/setup.py @@ -4,7 +4,6 @@ import re import shutil import subprocess -import sys from pathlib import Path import torch @@ -83,18 +82,6 @@ def _get_packages(branch_name, tag): return find_packages(exclude=exclude) -def _init_submodule(): - print(" --- Initializing submodules") - try: - subprocess.check_call(["git", "submodule", "init"]) - subprocess.check_call(["git", "submodule", "update"]) - except Exception: - print(" --- Submodule initalization failed") - print("Please run:\n\tgit submodule update --init --recursive") - sys.exit(1) - print(" --- Initialized submodule") - - def _parse_url(path): with open(path, "r") as file_: for line in file_: @@ -104,18 +91,6 @@ def _parse_url(path): yield url -def _parse_sources(): - third_party_dir = ROOT_DIR / "third_party" - libs = ["sox"] - archive_dir = third_party_dir / "archives" - archive_dir.mkdir(exist_ok=True) - for lib in libs: - cmake_file = third_party_dir / lib / "CMakeLists.txt" - for url in _parse_url(cmake_file): - path = archive_dir / os.path.basename(url) - yield path, url - - def _fetch_archives(src): for dest, url in src: if not dest.exists(): @@ -123,13 +98,6 @@ def _fetch_archives(src): torch.hub.download_url_to_file(url, dest, progress=False) -def _fetch_third_party_libraries(): - # Revert this when a submodule is added again - # _init_submodule() - if os.name != "nt": - _fetch_archives(_parse_sources()) - - def _main(): sha = _run_cmd(["git", "rev-parse", "HEAD"]) branch = _run_cmd(["git", "rev-parse", "--abbrev-ref", "HEAD"]) @@ -143,7 +111,6 @@ def _main(): print("-- Building version", version) _make_version_file(version, sha) - _fetch_third_party_libraries() with open("README.md") as f: long_description = f.read() diff --git a/third_party/sox/CMakeLists.txt b/third_party/sox/CMakeLists.txt index c4f5dd8931..4fd0aa7376 100644 --- a/third_party/sox/CMakeLists.txt +++ b/third_party/sox/CMakeLists.txt @@ -1,212 +1,18 @@ -find_package(PkgConfig REQUIRED) +include(FetchContent) -include(ExternalProject) - -set(INSTALL_DIR ${CMAKE_CURRENT_SOURCE_DIR}/../install) -set(ARCHIVE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/../archives) -set(patch_dir ${PROJECT_SOURCE_DIR}/third_party/patches) -set(COMMON_ARGS --quiet --disable-shared --enable-static --prefix=${INSTALL_DIR} --with-pic --disable-dependency-tracking --disable-debug --disable-examples --disable-doc) - -# To pass custom environment variables to ExternalProject_Add command, -# we need to do `${CMAKE_COMMAND} -E env ${envs} `. -# https://stackoverflow.com/a/62437353 -# We constrcut the custom environment variables here -set(envs - "PKG_CONFIG_PATH=${INSTALL_DIR}/lib/pkgconfig" - "LDFLAGS=-L${INSTALL_DIR}/lib $ENV{LDFLAGS}" - "CFLAGS=-I${INSTALL_DIR}/include -fvisibility=hidden $ENV{CFLAGS}" -) - -ExternalProject_Add(amr - PREFIX ${CMAKE_CURRENT_BINARY_DIR} - DOWNLOAD_DIR ${ARCHIVE_DIR} - URL https://sourceforge.net/projects/opencore-amr/files/opencore-amr/opencore-amr-0.1.5.tar.gz - URL_HASH SHA256=2c006cb9d5f651bfb5e60156dbff6af3c9d35c7bbcc9015308c0aff1e14cd341 - PATCH_COMMAND cp ${patch_dir}/config.guess ${patch_dir}/config.sub ${CMAKE_CURRENT_BINARY_DIR}/src/amr/ - CONFIGURE_COMMAND ${CMAKE_COMMAND} -E env ${envs} ${CMAKE_CURRENT_BINARY_DIR}/src/amr/configure ${COMMON_ARGS} - DOWNLOAD_NO_PROGRESS ON - LOG_DOWNLOAD ON - LOG_UPDATE ON - LOG_CONFIGURE ON - LOG_BUILD ON - LOG_INSTALL ON - LOG_MERGED_STDOUTERR ON - LOG_OUTPUT_ON_FAILURE ON -) - -ExternalProject_Add(lame - PREFIX ${CMAKE_CURRENT_BINARY_DIR} - DOWNLOAD_DIR ${ARCHIVE_DIR} - URL https://downloads.sourceforge.net/project/lame/lame/3.99/lame-3.99.5.tar.gz - URL_HASH SHA256=24346b4158e4af3bd9f2e194bb23eb473c75fb7377011523353196b19b9a23ff - PATCH_COMMAND cp ${patch_dir}/config.guess ${patch_dir}/config.sub ${CMAKE_CURRENT_BINARY_DIR}/src/lame/ - CONFIGURE_COMMAND ${CMAKE_COMMAND} -E env ${envs} ${CMAKE_CURRENT_BINARY_DIR}/src/lame/configure ${COMMON_ARGS} --enable-nasm - DOWNLOAD_NO_PROGRESS ON - LOG_DOWNLOAD ON - LOG_UPDATE ON - LOG_CONFIGURE ON - LOG_BUILD ON - LOG_INSTALL ON - LOG_MERGED_STDOUTERR ON - LOG_OUTPUT_ON_FAILURE ON -) - -ExternalProject_Add(ogg - PREFIX ${CMAKE_CURRENT_BINARY_DIR} - DOWNLOAD_DIR ${ARCHIVE_DIR} - URL https://ftp.osuosl.org/pub/xiph/releases/ogg/libogg-1.3.3.tar.gz - URL_HASH SHA256=c2e8a485110b97550f453226ec644ebac6cb29d1caef2902c007edab4308d985 - PATCH_COMMAND cp ${patch_dir}/config.guess ${patch_dir}/config.sub ${CMAKE_CURRENT_BINARY_DIR}/src/ogg/ - CONFIGURE_COMMAND ${CMAKE_COMMAND} -E env ${envs} ${CMAKE_CURRENT_BINARY_DIR}/src/ogg/configure ${COMMON_ARGS} - DOWNLOAD_NO_PROGRESS ON - LOG_DOWNLOAD ON - LOG_UPDATE ON - LOG_CONFIGURE ON - LOG_BUILD ON - LOG_INSTALL ON - LOG_MERGED_STDOUTERR ON - LOG_OUTPUT_ON_FAILURE ON -) - -ExternalProject_Add(flac - PREFIX ${CMAKE_CURRENT_BINARY_DIR} - DEPENDS ogg - DOWNLOAD_DIR ${ARCHIVE_DIR} - URL https://ftp.osuosl.org/pub/xiph/releases/flac/flac-1.3.2.tar.xz - URL_HASH SHA256=91cfc3ed61dc40f47f050a109b08610667d73477af6ef36dcad31c31a4a8d53f - PATCH_COMMAND cp ${patch_dir}/config.guess ${patch_dir}/config.sub ${CMAKE_CURRENT_BINARY_DIR}/src/flac/ - CONFIGURE_COMMAND ${CMAKE_COMMAND} -E env ${envs} ${CMAKE_CURRENT_BINARY_DIR}/src/flac/configure ${COMMON_ARGS} --with-ogg --disable-cpplibs --disable-xmms-plugin - DOWNLOAD_NO_PROGRESS ON - LOG_DOWNLOAD ON - LOG_UPDATE ON - LOG_CONFIGURE ON - LOG_BUILD ON - LOG_INSTALL ON - LOG_MERGED_STDOUTERR ON - LOG_OUTPUT_ON_FAILURE ON -) - -ExternalProject_Add(vorbis - PREFIX ${CMAKE_CURRENT_BINARY_DIR} - DEPENDS ogg - DOWNLOAD_DIR ${ARCHIVE_DIR} - URL https://ftp.osuosl.org/pub/xiph/releases/vorbis/libvorbis-1.3.6.tar.gz - URL_HASH SHA256=6ed40e0241089a42c48604dc00e362beee00036af2d8b3f46338031c9e0351cb - PATCH_COMMAND cp ${patch_dir}/config.guess ${patch_dir}/config.sub ${CMAKE_CURRENT_BINARY_DIR}/src/vorbis/ - CONFIGURE_COMMAND ${CMAKE_COMMAND} -E env ${envs} ${CMAKE_CURRENT_BINARY_DIR}/src/vorbis/configure ${COMMON_ARGS} --with-ogg - DOWNLOAD_NO_PROGRESS ON - LOG_DOWNLOAD ON - LOG_UPDATE ON - LOG_CONFIGURE ON - LOG_BUILD ON - LOG_INSTALL ON - LOG_MERGED_STDOUTERR ON - LOG_OUTPUT_ON_FAILURE ON -) - -ExternalProject_Add(opus - PREFIX ${CMAKE_CURRENT_BINARY_DIR} - DEPENDS ogg - DOWNLOAD_DIR ${ARCHIVE_DIR} - URL https://ftp.osuosl.org/pub/xiph/releases/opus/opus-1.3.1.tar.gz - URL_HASH SHA256=65b58e1e25b2a114157014736a3d9dfeaad8d41be1c8179866f144a2fb44ff9d - PATCH_COMMAND cp ${patch_dir}/config.guess ${patch_dir}/config.sub ${CMAKE_CURRENT_BINARY_DIR}/src/opus/ - CONFIGURE_COMMAND ${CMAKE_COMMAND} -E env ${envs} ${CMAKE_CURRENT_BINARY_DIR}/src/opus/configure ${COMMON_ARGS} --with-ogg - DOWNLOAD_NO_PROGRESS ON - LOG_DOWNLOAD ON - LOG_UPDATE ON - LOG_CONFIGURE ON - LOG_BUILD ON - LOG_INSTALL ON - LOG_MERGED_STDOUTERR ON - LOG_OUTPUT_ON_FAILURE ON -) - -ExternalProject_Add(opusfile - PREFIX ${CMAKE_CURRENT_BINARY_DIR} - DEPENDS opus - DOWNLOAD_DIR ${ARCHIVE_DIR} - URL https://ftp.osuosl.org/pub/xiph/releases/opus/opusfile-0.12.tar.gz - URL_HASH SHA256=118d8601c12dd6a44f52423e68ca9083cc9f2bfe72da7a8c1acb22a80ae3550b - PATCH_COMMAND cp ${patch_dir}/config.guess ${patch_dir}/config.sub ${CMAKE_CURRENT_BINARY_DIR}/src/opusfile/ - CONFIGURE_COMMAND ${CMAKE_COMMAND} -E env ${envs} ${CMAKE_CURRENT_BINARY_DIR}/src/opusfile/configure ${COMMON_ARGS} --disable-http - DOWNLOAD_NO_PROGRESS ON - LOG_DOWNLOAD ON - LOG_UPDATE ON - LOG_CONFIGURE ON - LOG_BUILD ON - LOG_INSTALL ON - LOG_MERGED_STDOUTERR ON - LOG_OUTPUT_ON_FAILURE ON -) - -# OpenMP is by default compiled against GNU OpenMP, which conflicts with the version of OpenMP that PyTorch uses. -# See https://github.com/pytorch/audio/pull/1026 -# TODO: Add flags like https://github.com/suphoff/pytorch_parallel_extension_cpp/blob/master/setup.py -set(SOX_OPTIONS - --disable-openmp - --with-amrnb - --with-amrwb - --with-flac - --with-lame - --with-oggvorbis - --with-opus - --without-alsa - --without-ao - --without-coreaudio - --without-oss - --without-id3tag - --without-ladspa - --without-mad - --without-magic - --without-png - --without-pulseaudio - --without-sndfile - --without-sndio - --without-sunaudio - --without-waveaudio - --without-wavpack - --without-twolame - ) - -set(SOX_LIBRARIES - ${INSTALL_DIR}/lib/libsox.a - ${INSTALL_DIR}/lib/libopencore-amrnb.a - ${INSTALL_DIR}/lib/libopencore-amrwb.a - ${INSTALL_DIR}/lib/libmp3lame.a - ${INSTALL_DIR}/lib/libFLAC.a - ${INSTALL_DIR}/lib/libopusfile.a - ${INSTALL_DIR}/lib/libopus.a - ${INSTALL_DIR}/lib/libvorbisenc.a - ${INSTALL_DIR}/lib/libvorbisfile.a - ${INSTALL_DIR}/lib/libvorbis.a - ${INSTALL_DIR}/lib/libogg.a - ) - -set(sox_depends - ogg flac vorbis opusfile lame amr - ) - -ExternalProject_Add(sox - PREFIX ${CMAKE_CURRENT_BINARY_DIR} - DEPENDS ${sox_depends} - DOWNLOAD_DIR ${ARCHIVE_DIR} +FetchContent_Declare( + sox URL https://downloads.sourceforge.net/project/sox/sox/14.4.2/sox-14.4.2.tar.bz2 URL_HASH SHA256=81a6956d4330e75b5827316e44ae381e6f1e8928003c6aa45896da9041ea149c - PATCH_COMMAND cp ${patch_dir}/config.guess ${patch_dir}/config.sub ${CMAKE_CURRENT_BINARY_DIR}/src/sox/ - CONFIGURE_COMMAND ${CMAKE_COMMAND} -E env ${envs} ${CMAKE_CURRENT_BINARY_DIR}/src/sox/configure ${COMMON_ARGS} ${SOX_OPTIONS} - BUILD_BYPRODUCTS ${SOX_LIBRARIES} - DOWNLOAD_NO_PROGRESS ON - LOG_DOWNLOAD ON - LOG_UPDATE ON - LOG_CONFIGURE ON - LOG_BUILD ON - LOG_INSTALL ON - LOG_MERGED_STDOUTERR ON - LOG_OUTPUT_ON_FAILURE ON -) + PATCH_COMMAND "" + CONFIGURE_COMMAND "" + BUILD_COMMAND "" + ) +# FetchContent_MakeAvailable will parse the downloaded content and setup the targets. +# We want to only download and not build, so we run Populate manually. +if(NOT sox_POPULATED) + FetchContent_Populate(sox) +endif() add_library(libsox INTERFACE) -add_dependencies(libsox sox) -target_include_directories(libsox INTERFACE ${INSTALL_DIR}/include) -target_link_libraries(libsox INTERFACE ${SOX_LIBRARIES}) +target_include_directories(libsox INTERFACE ${sox_SOURCE_DIR}/src) diff --git a/tools/setup_helpers/extension.py b/tools/setup_helpers/extension.py index c0768ce8c0..b935e0da90 100644 --- a/tools/setup_helpers/extension.py +++ b/tools/setup_helpers/extension.py @@ -128,6 +128,7 @@ def build_extension(self, ext): "-DCMAKE_VERBOSE_MAKEFILE=ON", f"-DPython_INCLUDE_DIR={distutils.sysconfig.get_python_inc()}", f"-DBUILD_SOX:BOOL={'ON' if _BUILD_SOX else 'OFF'}", + "-DDLOPEN_SOX:BOOL=ON", f"-DBUILD_RIR:BOOL={'ON' if _BUILD_RIR else 'OFF'}", f"-DBUILD_RNNT:BOOL={'ON' if _BUILD_RNNT else 'OFF'}", f"-DBUILD_ALIGN:BOOL={'ON' if _BUILD_ALIGN else 'OFF'}", diff --git a/torchaudio/_extension/__init__.py b/torchaudio/_extension/__init__.py index b8a0e4237c..2927131704 100644 --- a/torchaudio/_extension/__init__.py +++ b/torchaudio/_extension/__init__.py @@ -2,13 +2,13 @@ import os import sys -from torchaudio._internal.module_utils import fail_with_message, is_module_available, no_op +from torchaudio._internal.module_utils import eval_env, fail_with_message, is_module_available, no_op try: from .fb import _init_ffmpeg except ImportError: from .utils import _init_ffmpeg -from .utils import _check_cuda_version, _fail_since_no_ffmpeg, _init_dll_path, _init_sox, _load_lib +from .utils import _check_cuda_version, _fail_since_no_ffmpeg, _fail_since_no_sox, _init_dll_path, _init_sox, _load_lib _LG = logging.getLogger(__name__) @@ -51,20 +51,40 @@ _IS_ALIGN_AVAILABLE = torchaudio.lib._torchaudio.is_align_available() -# Similar to libtorchaudio, sox-related features should be importable when present. -# -# Note: This will be change in the future when sox is dynamically linked. -# At that point, this initialization should handle the case where -# sox integration is built but libsox is not found. +# Initialize libsox-related features _SOX_INITIALIZED = False -if is_module_available("torchaudio.lib._torchaudio_sox"): - _init_sox() - _SOX_INITIALIZED = True +_USE_SOX = False if os.name == "nt" else eval_env("TORCHAUDIO_USE_SOX", True) +_SOX_MODULE_AVAILABLE = is_module_available("torchaudio.lib._torchaudio_sox") +if _USE_SOX and _SOX_MODULE_AVAILABLE: + try: + _init_sox() + _SOX_INITIALIZED = True + except Exception: + # The initialization of sox extension will fail if supported sox + # libraries are not found in the system. + # Since the rest of the torchaudio works without it, we do not report the + # error here. + # The error will be raised when user code attempts to use these features. + _LG.debug("Failed to initialize sox extension", exc_info=True) + + +if os.name == "nt": + fail_if_no_sox = fail_with_message("requires sox extension, which is not supported on Windows.") +elif not _USE_SOX: + fail_if_no_sox = fail_with_message("requires sox extension, but it is disabled. (TORCHAUDIO_USE_SOX=0)") +elif not _SOX_MODULE_AVAILABLE: + fail_if_no_sox = fail_with_message( + "requires sox extension, but TorchAudio is not compiled with it. " + "Please build TorchAudio with libsox support. (BUILD_SOX=1)" + ) +else: + fail_if_no_sox = no_op if _SOX_INITIALIZED else _fail_since_no_sox # Initialize FFmpeg-related features _FFMPEG_EXT = None -if _IS_TORCHAUDIO_EXT_AVAILABLE: +_USE_FFMPEG = eval_env("TORCHAUDIO_USE_FFMPEG", True) +if _USE_FFMPEG and _IS_TORCHAUDIO_EXT_AVAILABLE: try: _FFMPEG_EXT = _init_ffmpeg() except Exception: @@ -76,15 +96,11 @@ _LG.debug("Failed to initialize ffmpeg bindings", exc_info=True) -fail_if_no_sox = ( - no_op - if _SOX_INITIALIZED - else fail_with_message( - "requires sox extension, but TorchAudio is not compiled with it. Please build TorchAudio with libsox support." - ) -) +if _USE_FFMPEG: + fail_if_no_ffmpeg = _fail_since_no_ffmpeg if _FFMPEG_EXT is None else no_op +else: + fail_if_no_ffmpeg = fail_with_message("requires ffmpeg extension, but it is disabled. (TORCHAUDIO_USE_FFMPEG=0)") -fail_if_no_ffmpeg = _fail_since_no_ffmpeg if _FFMPEG_EXT is None else no_op fail_if_no_rir = ( no_op diff --git a/torchaudio/_extension/utils.py b/torchaudio/_extension/utils.py index 2045482cb6..edb71ed4dc 100644 --- a/torchaudio/_extension/utils.py +++ b/torchaudio/_extension/utils.py @@ -70,13 +70,6 @@ def _init_sox(): _load_lib("libtorchaudio_sox") import torchaudio.lib._torchaudio_sox # noqa - torchaudio.lib._torchaudio_sox.set_verbosity(0) - - import atexit - - torch.ops.torchaudio.sox_effects_initialize_sox_effects() - atexit.register(torch.ops.torchaudio.sox_effects_shutdown_sox_effects) - def _try_access_avutil(ffmpeg_ver): libname_template = { @@ -197,6 +190,25 @@ def _check_cuda_version(): return version +def _fail_since_no_sox(func): + @wraps(func) + def wrapped(*_args, **_kwargs): + try: + # Note: + # We run _init_sox again just to show users the stacktrace. + # _init_sox would not succeed here. + _init_sox() + except Exception as err: + raise RuntimeError( + f"{func.__name__} requires sox extension which is not available. " + "Please refer to the stacktrace above for how to resolve this." + ) from err + # This should not happen in normal execution, but just in case. + return func(*_args, **_kwargs) + + return wrapped + + def _fail_since_no_ffmpeg(func): @wraps(func) def wrapped(*_args, **_kwargs): diff --git a/torchaudio/csrc/sox/CMakeLists.txt b/torchaudio/csrc/sox/CMakeLists.txt index 3391a4fc37..6ae9db70b2 100644 --- a/torchaudio/csrc/sox/CMakeLists.txt +++ b/torchaudio/csrc/sox/CMakeLists.txt @@ -1,17 +1,23 @@ set( sources + stub.cpp io.cpp utils.cpp effects.cpp effects_chain.cpp types.cpp ) +set( + compiler_definitions + DLOPEN_SOX + ) + torchaudio_library( libtorchaudio_sox "${sources}" "" "torch;libsox" - "" + "${compiler_definitions}" ) if (BUILD_TORCHAUDIO_PYTHON_EXTENSION) @@ -20,6 +26,6 @@ if (BUILD_TORCHAUDIO_PYTHON_EXTENSION) "pybind/pybind.cpp;" "" "libtorchaudio_sox" - "" + "${compiler_definitions}" ) endif() diff --git a/torchaudio/csrc/sox/effects.cpp b/torchaudio/csrc/sox/effects.cpp index a159663a10..aeeb738e69 100644 --- a/torchaudio/csrc/sox/effects.cpp +++ b/torchaudio/csrc/sox/effects.cpp @@ -1,49 +1,10 @@ #include #include #include +#include #include namespace torchaudio::sox { -namespace { - -enum SoxEffectsResourceState { NotInitialized, Initialized, ShutDown }; -SoxEffectsResourceState SOX_RESOURCE_STATE = NotInitialized; -std::mutex SOX_RESOUCE_STATE_MUTEX; - -} // namespace - -void initialize_sox_effects() { - const std::lock_guard lock(SOX_RESOUCE_STATE_MUTEX); - - switch (SOX_RESOURCE_STATE) { - case NotInitialized: - TORCH_CHECK( - sox_init() == SOX_SUCCESS, "Failed to initialize sox effects."); - SOX_RESOURCE_STATE = Initialized; - break; - case Initialized: - break; - case ShutDown: - TORCH_CHECK( - false, "SoX Effects has been shut down. Cannot initialize again."); - } -}; - -void shutdown_sox_effects() { - const std::lock_guard lock(SOX_RESOUCE_STATE_MUTEX); - - switch (SOX_RESOURCE_STATE) { - case NotInitialized: - TORCH_CHECK(false, "SoX Effects is not initialized. Cannot shutdown."); - case Initialized: - TORCH_CHECK( - sox_quit() == SOX_SUCCESS, "Failed to initialize sox effects."); - SOX_RESOURCE_STATE = ShutDown; - break; - case ShutDown: - break; - } -} auto apply_effects_tensor( torch::Tensor waveform, @@ -91,7 +52,7 @@ auto apply_effects_file( const c10::optional& format) -> c10::optional> { // Open input file - SoxFormat sf(sox_open_read( + SoxFormat sf(SOX sox_open_read( path.c_str(), /*signal=*/nullptr, /*encoding=*/nullptr, @@ -129,7 +90,6 @@ auto apply_effects_file( dtype, normalize.value_or(true), channels_first_); - return std::tuple( tensor, chain.getOutputSampleRate()); } @@ -137,13 +97,8 @@ auto apply_effects_file( namespace { TORCH_LIBRARY_FRAGMENT(torchaudio, m) { - m.def( - "torchaudio::sox_effects_initialize_sox_effects", - &initialize_sox_effects); - m.def("torchaudio::sox_effects_shutdown_sox_effects", &shutdown_sox_effects); m.def("torchaudio::sox_effects_apply_effects_tensor", &apply_effects_tensor); m.def("torchaudio::sox_effects_apply_effects_file", &apply_effects_file); } - } // namespace } // namespace torchaudio::sox diff --git a/torchaudio/csrc/sox/effects_chain.cpp b/torchaudio/csrc/sox/effects_chain.cpp index 81dddada28..059c4bab19 100644 --- a/torchaudio/csrc/sox/effects_chain.cpp +++ b/torchaudio/csrc/sox/effects_chain.cpp @@ -1,11 +1,11 @@ #include +#include #include #include "c10/util/Exception.h" using namespace torch::indexing; namespace torchaudio::sox { - namespace { /// helper classes for passing the location of input tensor and output buffer @@ -112,12 +112,12 @@ int file_output_flow( *osamp = 0; if (*isamp) { auto sf = static_cast(effp->priv)->sf; - if (sox_write(sf, ibuf, *isamp) != *isamp) { + if (SOX sox_write(sf, ibuf, *isamp) != *isamp) { TORCH_CHECK( !sf->sox_errno, sf->sox_errstr, " ", - sox_strerror(sf->sox_errno), + SOX sox_strerror(sf->sox_errno), " ", sf->filename); return SOX_EOF; @@ -197,18 +197,18 @@ SoxEffectsChain::SoxEffectsChain( in_sig_(), interm_sig_(), out_sig_(), - sec_(sox_create_effects_chain(&in_enc_, &out_enc_)) { + sec_(SOX sox_create_effects_chain(&in_enc_, &out_enc_)) { TORCH_CHECK(sec_, "Failed to create effect chain."); } SoxEffectsChain::~SoxEffectsChain() { if (sec_ != nullptr) { - sox_delete_effects_chain(sec_); + SOX sox_delete_effects_chain(sec_); } } void SoxEffectsChain::run() { - sox_flow_effects(sec_, NULL, NULL); + SOX sox_flow_effects(sec_, NULL, NULL); } void SoxEffectsChain::addInputTensor( @@ -217,44 +217,44 @@ void SoxEffectsChain::addInputTensor( bool channels_first) { in_sig_ = get_signalinfo(waveform, sample_rate, "wav", channels_first); interm_sig_ = in_sig_; - SoxEffect e(sox_create_effect(get_tensor_input_handler())); + SoxEffect e(SOX sox_create_effect(get_tensor_input_handler())); auto priv = static_cast(e->priv); priv->index = 0; priv->waveform = waveform; priv->sample_rate = sample_rate; priv->channels_first = channels_first; TORCH_CHECK( - sox_add_effect(sec_, e, &interm_sig_, &in_sig_) == SOX_SUCCESS, + SOX sox_add_effect(sec_, e, &interm_sig_, &in_sig_) == SOX_SUCCESS, "Internal Error: Failed to add effect: input_tensor"); } void SoxEffectsChain::addOutputBuffer( std::vector* output_buffer) { - SoxEffect e(sox_create_effect(get_tensor_output_handler())); + SoxEffect e(SOX sox_create_effect(get_tensor_output_handler())); static_cast(e->priv)->buffer = output_buffer; TORCH_CHECK( - sox_add_effect(sec_, e, &interm_sig_, &in_sig_) == SOX_SUCCESS, + SOX sox_add_effect(sec_, e, &interm_sig_, &in_sig_) == SOX_SUCCESS, "Internal Error: Failed to add effect: output_tensor"); } void SoxEffectsChain::addInputFile(sox_format_t* sf) { in_sig_ = sf->signal; interm_sig_ = in_sig_; - SoxEffect e(sox_create_effect(sox_find_effect("input"))); + SoxEffect e(SOX sox_create_effect(SOX sox_find_effect("input"))); char* opts[] = {(char*)sf}; - sox_effect_options(e, 1, opts); + SOX sox_effect_options(e, 1, opts); TORCH_CHECK( - sox_add_effect(sec_, e, &interm_sig_, &in_sig_) == SOX_SUCCESS, + SOX sox_add_effect(sec_, e, &interm_sig_, &in_sig_) == SOX_SUCCESS, "Internal Error: Failed to add effect: input ", sf->filename); } void SoxEffectsChain::addOutputFile(sox_format_t* sf) { out_sig_ = sf->signal; - SoxEffect e(sox_create_effect(get_file_output_handler())); + SoxEffect e(SOX sox_create_effect(get_file_output_handler())); static_cast(e->priv)->sf = sf; TORCH_CHECK( - sox_add_effect(sec_, e, &interm_sig_, &out_sig_) == SOX_SUCCESS, + SOX sox_add_effect(sec_, e, &interm_sig_, &out_sig_) == SOX_SUCCESS, "Internal Error: Failed to add effect: output ", sf->filename); } @@ -266,12 +266,12 @@ void SoxEffectsChain::addEffect(const std::vector effect) { TORCH_CHECK( UNSUPPORTED_EFFECTS.find(name) == UNSUPPORTED_EFFECTS.end(), "Unsupported effect: ", - name) + name); - auto returned_effect = sox_find_effect(name.c_str()); + auto returned_effect = SOX sox_find_effect(name.c_str()); TORCH_CHECK(returned_effect, "Unsupported effect: ", name) - SoxEffect e(sox_create_effect(returned_effect)); + SoxEffect e(SOX sox_create_effect(returned_effect)); const auto num_options = num_args - 1; std::vector opts; @@ -279,12 +279,12 @@ void SoxEffectsChain::addEffect(const std::vector effect) { opts.push_back((char*)effect[i].c_str()); } TORCH_CHECK( - sox_effect_options(e, num_options, num_options ? opts.data() : nullptr) == - SOX_SUCCESS, + SOX sox_effect_options( + e, num_options, num_options ? opts.data() : nullptr) == SOX_SUCCESS, "Invalid effect option: ", c10::Join(" ", effect)) TORCH_CHECK( - sox_add_effect(sec_, e, &interm_sig_, &in_sig_) == SOX_SUCCESS, + SOX sox_add_effect(sec_, e, &interm_sig_, &in_sig_) == SOX_SUCCESS, "Internal Error: Failed to add effect: \"", c10::Join(" ", effect), "\""); diff --git a/torchaudio/csrc/sox/io.cpp b/torchaudio/csrc/sox/io.cpp index b8aac89372..d4cdeb0665 100644 --- a/torchaudio/csrc/sox/io.cpp +++ b/torchaudio/csrc/sox/io.cpp @@ -1,6 +1,7 @@ #include #include #include +#include #include #include @@ -11,7 +12,7 @@ namespace torchaudio::sox { c10::optional get_info_file( const std::string& path, const c10::optional& format) { - SoxFormat sf(sox_open_read( + SoxFormat sf(SOX sox_open_read( path.c_str(), /*signal=*/nullptr, /*encoding=*/nullptr, @@ -107,7 +108,7 @@ void save_audio_file( const auto encoding_info = get_encodinginfo_for_save( filetype, tensor.dtype(), compression, encoding, bits_per_sample); - SoxFormat sf(sox_open_write( + SoxFormat sf(SOX sox_open_write( path.c_str(), &signal_info, &encoding_info, diff --git a/torchaudio/csrc/sox/stub.cpp b/torchaudio/csrc/sox/stub.cpp new file mode 100644 index 0000000000..9fae4c3976 --- /dev/null +++ b/torchaudio/csrc/sox/stub.cpp @@ -0,0 +1,96 @@ +#include +#include +#include +#include + +namespace torchaudio::sox::detail { +namespace { + +const char* get_compile_version() { + static char versionstr[20]; + + sprintf( + versionstr, + "%d.%d.%d", + (SOX_LIB_VERSION_CODE & 0xff0000) >> 16, + (SOX_LIB_VERSION_CODE & 0x00ff00) >> 8, + (SOX_LIB_VERSION_CODE & 0x0000ff)); + return versionstr; +} + +LibSoxStub get_stub(at::DynamicLibrary& handle) { + // Validate version: It's only tested on 14.4.2, and we don't expect + // new version of sox to be compatible nor we intend to support. + auto fn = (const sox_version_info_t* (*)())handle.sym("sox_version_info"); + if (SOX_LIB_VERSION_CODE != fn()->version_code) { + auto runtime_ver = ((const char* (*)())handle.sym("sox_version"))(); + TORCH_WARN( + "TorchAudio was compiled with sox version ", + get_compile_version(), + ". But the version found is ", + runtime_ver, + ". If this causes a problem, you can disable sox integration by setting environment variable TORCHAUDIO_USE_SOX=0."); + } + +#define get_symbol(X) (decltype(LibSoxStub::X)) handle.sym(#X) + return LibSoxStub{ + get_symbol(sox_add_effect), + get_symbol(sox_close), + get_symbol(sox_create_effect), + get_symbol(sox_create_effects_chain), + get_symbol(sox_delete_effect), + get_symbol(sox_delete_effects_chain), + get_symbol(sox_effect_options), + get_symbol(sox_find_effect), + get_symbol(sox_flow_effects), + get_symbol(sox_get_effect_fns), + get_symbol(sox_get_format_fns), + get_symbol(sox_get_globals), + get_symbol(sox_open_read), + get_symbol(sox_open_write), + get_symbol(sox_strerror), + get_symbol(sox_write)}; +#undef get_symbol +} + +// Handle to the dlopen-ed libsox +class StubImpl { + at::DynamicLibrary handle; + + public: + const LibSoxStub stub; + + StubImpl(const char* name) : handle(name), stub(get_stub(handle)) { + // Global config + auto config = stub.sox_get_globals(); + config->verbosity = 0; + config->use_threads = sox_false; + + // Init sox effect plugins + auto fn = (int (*)())handle.sym("sox_init"); + TORCH_CHECK(SOX_SUCCESS == fn(), "Failed to initialize sox effects."); + } + + ~StubImpl() { + auto fn = (int (*)())handle.sym("sox_quit"); + if (SOX_SUCCESS != fn()) { + TORCH_WARN("Failed to release sox effect plugins."); + } + } +}; +} // namespace + +#if defined(_WIN32) +#define EXT "lib" +#elif defined(__APPLE__) +#define EXT "dylib" +#else +#define EXT "so" +#endif +const LibSoxStub& libsox_stub() { + static const StubImpl s{"libsox." EXT}; + return s.stub; +} +#undef EXT + +} // namespace torchaudio::sox::detail diff --git a/torchaudio/csrc/sox/stub.h b/torchaudio/csrc/sox/stub.h new file mode 100644 index 0000000000..6999ee9359 --- /dev/null +++ b/torchaudio/csrc/sox/stub.h @@ -0,0 +1,67 @@ +#pragma once +#include + +#ifndef DLOPEN_SOX +#define SOX +#else +#define SOX detail::libsox_stub(). + +namespace torchaudio::sox::detail { + +// Interface to provide handle to libsox library. +struct LibSoxStub { + int (*sox_add_effect)( + sox_effects_chain_t* chain, + sox_effect_t* effp, + sox_signalinfo_t* in, + sox_signalinfo_t const* out); + int (*sox_close)(sox_format_t* ft); + + sox_effect_t* (*sox_create_effect)(sox_effect_handler_t const* eh); + + sox_effects_chain_t* (*sox_create_effects_chain)( + sox_encodinginfo_t const* in_enc, + sox_encodinginfo_t const* out_enc); + + void (*sox_delete_effect)(sox_effect_t* effp); + void (*sox_delete_effects_chain)(sox_effects_chain_t* ecp); + + int (*sox_effect_options)(sox_effect_t* effp, int argc, char* const argv[]); + + const sox_effect_handler_t* (*sox_find_effect)(char const* name); + + int (*sox_flow_effects)( + sox_effects_chain_t* chain, + int (*callback)(sox_bool all_done, void* client_data), + void* client_data); + + const sox_effect_fn_t* (*sox_get_effect_fns)(void); + + const sox_format_tab_t* (*sox_get_format_fns)(void); + + sox_globals_t* (*sox_get_globals)(void); + + sox_format_t* (*sox_open_read)( + char const* path, + sox_signalinfo_t const* signal, + sox_encodinginfo_t const* encoding, + char const* filetype); + + sox_format_t* (*sox_open_write)( + char const* path, + sox_signalinfo_t const* signal, + sox_encodinginfo_t const* encoding, + char const* filetype, + sox_oob_t const* oob, + sox_bool (*overwrite_permitted)(char const* filename)); + + const char* (*sox_strerror)(int sox_errno); + + size_t (*sox_write)(sox_format_t* ft, const sox_sample_t* buf, size_t len); +}; + +const LibSoxStub& libsox_stub(); + +} // namespace torchaudio::sox::detail + +#endif diff --git a/torchaudio/csrc/sox/utils.cpp b/torchaudio/csrc/sox/utils.cpp index 5b662bd6ff..1bf6143077 100644 --- a/torchaudio/csrc/sox/utils.cpp +++ b/torchaudio/csrc/sox/utils.cpp @@ -1,33 +1,34 @@ #include #include +#include #include #include namespace torchaudio::sox { void set_seed(const int64_t seed) { - sox_get_globals()->ranqd1 = static_cast(seed); + SOX sox_get_globals()->ranqd1 = static_cast(seed); } void set_verbosity(const int64_t verbosity) { - sox_get_globals()->verbosity = static_cast(verbosity); + SOX sox_get_globals()->verbosity = static_cast(verbosity); } void set_use_threads(const bool use_threads) { - sox_get_globals()->use_threads = static_cast(use_threads); + SOX sox_get_globals()->use_threads = static_cast(use_threads); } void set_buffer_size(const int64_t buffer_size) { - sox_get_globals()->bufsiz = static_cast(buffer_size); + SOX sox_get_globals()->bufsiz = static_cast(buffer_size); } int64_t get_buffer_size() { - return sox_get_globals()->bufsiz; + return SOX sox_get_globals()->bufsiz; } std::vector> list_effects() { std::vector> effects; - for (const sox_effect_fn_t* fns = sox_get_effect_fns(); *fns; ++fns) { + for (const sox_effect_fn_t* fns = SOX sox_get_effect_fns(); *fns; ++fns) { const sox_effect_handler_t* handler = (*fns)(); if (handler && handler->name) { if (UNSUPPORTED_EFFECTS.find(handler->name) == @@ -43,7 +44,7 @@ std::vector> list_effects() { std::vector list_write_formats() { std::vector formats; - for (const sox_format_tab_t* fns = sox_get_format_fns(); fns->fn; ++fns) { + for (const sox_format_tab_t* fns = SOX sox_get_format_fns(); fns->fn; ++fns) { const sox_format_handler_t* handler = fns->fn(); for (const char* const* names = handler->names; *names; ++names) { if (!strchr(*names, '/') && handler->write) @@ -55,7 +56,7 @@ std::vector list_write_formats() { std::vector list_read_formats() { std::vector formats; - for (const sox_format_tab_t* fns = sox_get_format_fns(); fns->fn; ++fns) { + for (const sox_format_tab_t* fns = SOX sox_get_format_fns(); fns->fn; ++fns) { const sox_format_handler_t* handler = fns->fn(); for (const char* const* names = handler->names; *names; ++names) { if (!strchr(*names, '/') && handler->read) @@ -79,7 +80,7 @@ SoxFormat::operator sox_format_t*() const noexcept { void SoxFormat::close() { if (fd_ != nullptr) { - sox_close(fd_); + SOX sox_close(fd_); fd_ = nullptr; } } @@ -490,5 +491,4 @@ sox_encodinginfo_t get_encodinginfo_for_save( /*reverse_bits=*/sox_option_default, /*opposite_endian=*/sox_false}; } - } // namespace torchaudio::sox diff --git a/torchaudio/csrc/sox/utils.h b/torchaudio/csrc/sox/utils.h index 255d7270fe..b118365999 100644 --- a/torchaudio/csrc/sox/utils.h +++ b/torchaudio/csrc/sox/utils.h @@ -2,7 +2,7 @@ #define TORCHAUDIO_SOX_UTILS_H #include -#include +#include namespace torchaudio::sox {