From 2c68f1dac3df946afc01825c320cf065bcad280d Mon Sep 17 00:00:00 2001 From: Robin Scheibler Date: Thu, 2 Apr 2026 23:52:46 +0900 Subject: [PATCH 1/9] Allows to provide normalized frequency to measured directivity plotting function. --- pyroomacoustics/directivities/measured.py | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/pyroomacoustics/directivities/measured.py b/pyroomacoustics/directivities/measured.py index 894bd6eb..900af3dc 100644 --- a/pyroomacoustics/directivities/measured.py +++ b/pyroomacoustics/directivities/measured.py @@ -301,8 +301,9 @@ def plot(self, freq_bin=0, n_grid=100, ax=None, depth=False, offset=None): Parameters ---------- - freq_bin: int - The frequency bin to plot + freq_bin: int or float + The frequency bin to plot if an int. Normalized frequency ``freq_hz / fs`` + if a float. n_grid: int The number of points to use for the interpolation grid ax: matplotlib.axes.Axes, optional @@ -321,6 +322,14 @@ def plot(self, freq_bin=0, n_grid=100, ax=None, depth=False, offset=None): """ import matplotlib.pyplot as plt + if isinstance(freq_bin, float): + n_fft = self._irs.shape[-1] + freq_bin = int(freq_bin * n_fft) + elif not isinstance(freq_bin, int): + raise TypeError( + f"freq_bin should be of type int or float (got {type(freq_bin)})." + ) + cart = self._grid.cartesian.T length = np.abs(np.fft.rfft(self._irs, axis=-1)[:, freq_bin]) From 18d3f450a7006d3a36ccded62bad6f2afa941a6f Mon Sep 17 00:00:00 2001 From: Robin Scheibler Date: Sat, 4 Apr 2026 10:22:07 +0900 Subject: [PATCH 2/9] Workaround for num_channels > 128 in resampling with samplerate. --- pyroomacoustics/utilities.py | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/pyroomacoustics/utilities.py b/pyroomacoustics/utilities.py index 3b9bf9ea..972de362 100644 --- a/pyroomacoustics/utilities.py +++ b/pyroomacoustics/utilities.py @@ -26,6 +26,7 @@ import fractions import functools import itertools +import math import warnings import numpy as np @@ -935,9 +936,16 @@ def resample(data, old_fs, new_fs, backend=None, *args, **kwargs): if backend == "soxr": resampled_data = soxr.resample(data, old_fs, new_fs, *args, **kwargs) elif backend == "samplerate": - resampled_data = samplerate.resample( - data, new_fs / old_fs, "sinc_best", *args, **kwargs - ) + # Split into arrays of 128 channels to accomodate a limitation of samplerate. + block = 128 + data_inputs = [ + data[:, i : i + block].copy() for i in range(0, data.shape[1], block) + ] + data_outputs = [ + samplerate.resample(d, new_fs / old_fs, "sinc_best", *args, **kwargs) + for d in data_inputs + ] + resampled_data = np.concatenate(data_outputs, axis=1) else: # first, simplify the fraction rate_frac = fractions.Fraction(int(new_fs), int(old_fs)) From bfd917323f4fae571fa1987a6e24dceceb40937c Mon Sep 17 00:00:00 2001 From: Robin Scheibler Date: Fri, 3 Apr 2026 17:50:23 +0900 Subject: [PATCH 3/9] lint --- pyroomacoustics/datasets/cmu_arctic.py | 2 +- pyroomacoustics/doa/cssm.py | 4 ++-- pyroomacoustics/doa/doa.py | 2 +- pyroomacoustics/doa/frida.py | 2 +- pyroomacoustics/doa/music.py | 4 ++-- pyroomacoustics/doa/normmusic.py | 4 ++-- pyroomacoustics/doa/plotters.py | 2 +- pyroomacoustics/doa/srp.py | 4 ++-- pyroomacoustics/doa/tools_fri_doa_plane.py | 6 +++--- pyroomacoustics/doa/tops.py | 4 ++-- pyroomacoustics/doa/waves.py | 4 ++-- pyroomacoustics/phase/gl.py | 2 +- pyroomacoustics/transform/stft.py | 2 +- pyroomacoustics/utilities.py | 2 +- 14 files changed, 22 insertions(+), 22 deletions(-) diff --git a/pyroomacoustics/datasets/cmu_arctic.py b/pyroomacoustics/datasets/cmu_arctic.py index f9836197..a20f2dcb 100644 --- a/pyroomacoustics/datasets/cmu_arctic.py +++ b/pyroomacoustics/datasets/cmu_arctic.py @@ -210,7 +210,7 @@ def build_corpus(self, **kwargs): speaker=speaker, tag=tag, text=info["text"], - **cmu_arctic_speakers[speaker] + **cmu_arctic_speakers[speaker], ) # it there is a match, add it diff --git a/pyroomacoustics/doa/cssm.py b/pyroomacoustics/doa/cssm.py index c40adcdb..9f4ce7ba 100644 --- a/pyroomacoustics/doa/cssm.py +++ b/pyroomacoustics/doa/cssm.py @@ -59,7 +59,7 @@ def __init__( azimuth=None, colatitude=None, num_iter=5, - **kwargs + **kwargs, ): MUSIC.__init__( self, @@ -72,7 +72,7 @@ def __init__( r=r, azimuth=azimuth, colatitude=colatitude, - **kwargs + **kwargs, ) self.iter = num_iter diff --git a/pyroomacoustics/doa/doa.py b/pyroomacoustics/doa/doa.py index 132b70cb..31ea4578 100644 --- a/pyroomacoustics/doa/doa.py +++ b/pyroomacoustics/doa/doa.py @@ -189,7 +189,7 @@ def __init__( n_grid=None, dim=2, *args, - **kwargs + **kwargs, ): if dim > L.shape[0]: raise ValueError("Microphones locations missing dimensions.") diff --git a/pyroomacoustics/doa/frida.py b/pyroomacoustics/doa/frida.py index 6e51a0e6..1c1dac57 100644 --- a/pyroomacoustics/doa/frida.py +++ b/pyroomacoustics/doa/frida.py @@ -91,7 +91,7 @@ def __init__( verbose=False, symb=True, use_cache=False, - **kwargs + **kwargs, ): DOA.__init__(self, L, fs, nfft, c=c, num_src=num_src, mode="far", **kwargs) diff --git a/pyroomacoustics/doa/music.py b/pyroomacoustics/doa/music.py index 07cf6a6d..05589204 100644 --- a/pyroomacoustics/doa/music.py +++ b/pyroomacoustics/doa/music.py @@ -53,7 +53,7 @@ def __init__( azimuth=None, colatitude=None, frequency_normalization=False, - **kwargs + **kwargs, ): DOA.__init__( self, @@ -66,7 +66,7 @@ def __init__( r=r, azimuth=azimuth, colatitude=colatitude, - **kwargs + **kwargs, ) self.Pssl = None diff --git a/pyroomacoustics/doa/normmusic.py b/pyroomacoustics/doa/normmusic.py index b91a1eb1..d6e0bc2d 100644 --- a/pyroomacoustics/doa/normmusic.py +++ b/pyroomacoustics/doa/normmusic.py @@ -48,7 +48,7 @@ def __init__( azimuth=None, colatitude=None, frequency_normalization=True, - **kwargs + **kwargs, ): super().__init__( L, @@ -61,5 +61,5 @@ def __init__( azimuth, colatitude, frequency_normalization, - **kwargs + **kwargs, ) diff --git a/pyroomacoustics/doa/plotters.py b/pyroomacoustics/doa/plotters.py index 764f3d24..8d134bd3 100644 --- a/pyroomacoustics/doa/plotters.py +++ b/pyroomacoustics/doa/plotters.py @@ -397,7 +397,7 @@ def sph_plot_diracs( colatitude_grid=None, azimuth_grid=None, file_name="sph_recon_2d_dirac.pdf", - **kwargs + **kwargs, ): """ This function plots the dirty image with sources locations on diff --git a/pyroomacoustics/doa/srp.py b/pyroomacoustics/doa/srp.py index 7bc8438e..b206a7a0 100644 --- a/pyroomacoustics/doa/srp.py +++ b/pyroomacoustics/doa/srp.py @@ -49,7 +49,7 @@ def __init__( r=None, azimuth=None, colatitude=None, - **kwargs + **kwargs, ): DOA.__init__( self, @@ -62,7 +62,7 @@ def __init__( r=r, azimuth=azimuth, colatitude=colatitude, - **kwargs + **kwargs, ) self.num_pairs = self.M * (self.M - 1) / 2 diff --git a/pyroomacoustics/doa/tools_fri_doa_plane.py b/pyroomacoustics/doa/tools_fri_doa_plane.py index 255685b4..6cfaee88 100644 --- a/pyroomacoustics/doa/tools_fri_doa_plane.py +++ b/pyroomacoustics/doa/tools_fri_doa_plane.py @@ -1738,7 +1738,7 @@ def pt_src_recon_multiband( G_lst=None, GtG_lst=None, GtG_inv_lst=None, - **kwargs + **kwargs, ): """ reconstruct point sources on the circle from the visibility measurements @@ -1920,7 +1920,7 @@ def pt_src_recon( update_G=False, verbose=False, signal_type="visibility", - **kwargs + **kwargs, ): """ reconstruct point sources on the circle from the visibility measurements @@ -2026,7 +2026,7 @@ def pt_src_recon_rotate( num_rotation=1, verbose=False, signal_type="visibility", - **kwargs + **kwargs, ): """ reconstruct point sources on the circle from the visibility measurements. diff --git a/pyroomacoustics/doa/tops.py b/pyroomacoustics/doa/tops.py index 7a9eb0c4..eb2d6f91 100644 --- a/pyroomacoustics/doa/tops.py +++ b/pyroomacoustics/doa/tops.py @@ -59,7 +59,7 @@ def __init__( r=None, azimuth=None, colatitude=None, - **kwargs + **kwargs, ): MUSIC.__init__( self, @@ -72,7 +72,7 @@ def __init__( r=r, azimuth=azimuth, colatitude=colatitude, - **kwargs + **kwargs, ) def _process(self, X): diff --git a/pyroomacoustics/doa/waves.py b/pyroomacoustics/doa/waves.py index e0510262..d87ac4cd 100644 --- a/pyroomacoustics/doa/waves.py +++ b/pyroomacoustics/doa/waves.py @@ -58,7 +58,7 @@ def __init__( azimuth=None, colatitude=None, num_iter=5, - **kwargs + **kwargs, ): MUSIC.__init__( self, @@ -71,7 +71,7 @@ def __init__( r=r, azimuth=azimuth, colatitude=colatitude, - **kwargs + **kwargs, ) self.iter = num_iter diff --git a/pyroomacoustics/phase/gl.py b/pyroomacoustics/phase/gl.py index e31d02a4..98df942c 100644 --- a/pyroomacoustics/phase/gl.py +++ b/pyroomacoustics/phase/gl.py @@ -159,7 +159,7 @@ def griffin_lim( hop=hop, analysis_window=analysis_window, synthesis_window=synthesis_window, - **stft_kwargs + **stft_kwargs, ) # Initialize the signal diff --git a/pyroomacoustics/transform/stft.py b/pyroomacoustics/transform/stft.py index 73dd75f2..30c62ccb 100644 --- a/pyroomacoustics/transform/stft.py +++ b/pyroomacoustics/transform/stft.py @@ -84,7 +84,7 @@ def __init__( transform="numpy", streaming=True, precision="double", - **kwargs + **kwargs, ): # initialize parameters self.num_samples = N # number of samples per frame diff --git a/pyroomacoustics/utilities.py b/pyroomacoustics/utilities.py index 972de362..9837e9a0 100644 --- a/pyroomacoustics/utilities.py +++ b/pyroomacoustics/utilities.py @@ -955,7 +955,7 @@ def resample(data, old_fs, new_fs, backend=None, *args, **kwargs): down=rate_frac.denominator, axis=0, *args, - **kwargs + **kwargs, ) # restore the original shape of the data From 32d8412e6ac1d36455a22820c91a945c2eaa279e Mon Sep 17 00:00:00 2001 From: Robin Scheibler Date: Fri, 3 Apr 2026 01:05:15 +0900 Subject: [PATCH 4/9] * Modernizes the build system to use cmake. CMake handles dependencies via FetchContent in ./external. * Moves to setuptools-scm to handle version number tracking. * Moving package management to pyproject.toml. --- .gitignore | 2 + .gitmodules | 6 - CMakeLists.txt | 49 ++++ MANIFEST.in | 9 +- external/CMakeLists.txt | 37 +++ pyproject.toml | 72 +++++- pyroomacoustics/libroom_src/common.hpp | 2 + pyroomacoustics/libroom_src/ext/eigen | 1 - pyroomacoustics/libroom_src/ext/nanoflann | 1 - pyroomacoustics/version.py | 1 - setup.py | 280 +++++----------------- 11 files changed, 225 insertions(+), 235 deletions(-) create mode 100644 CMakeLists.txt create mode 100644 external/CMakeLists.txt delete mode 160000 pyroomacoustics/libroom_src/ext/eigen delete mode 160000 pyroomacoustics/libroom_src/ext/nanoflann delete mode 100644 pyroomacoustics/version.py diff --git a/.gitignore b/.gitignore index bd81b87b..08d6525b 100644 --- a/.gitignore +++ b/.gitignore @@ -16,3 +16,5 @@ pyroomacoustics.egg-info/ pyroomacoustics/build_rir.c pyroomacoustics/**/*.so pyroomacoustics/directivities/tests/data/*.pdf +# version file generated by setuptools_scm +pyroomacoustics/version.py diff --git a/.gitmodules b/.gitmodules index 05df7510..e69de29b 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,6 +0,0 @@ -[submodule "pyroomacoustics/libroom_src/ext/eigen"] - path = pyroomacoustics/libroom_src/ext/eigen - url = https://github.com/eigenteam/eigen-git-mirror.git -[submodule "pyroomacoustics/libroom_src/ext/nanoflann"] - path = pyroomacoustics/libroom_src/ext/nanoflann - url = https://github.com/jlblancoc/nanoflann.git diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 00000000..56c2d0ad --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,49 @@ +cmake_minimum_required(VERSION 3.15) +set(CMAKE_POLICY_VERSION_MINIMUM 3.5) + +project(pyroomacoustics) + +# Find pybind11 (handled by external/CMakeLists.txt via FetchContent) +set(PYBIND11_FINDPYTHON ON) + +# adds the external dependencies +add_subdirectory(external) + +# Source directory for libroom +set(LIBROOM_SRC_DIR pyroomacoustics/libroom_src) + +# Collect source files +set(LIBROOM_SOURCES + ${LIBROOM_SRC_DIR}/libroom.cpp +) + +# Define the extension module +pybind11_add_module(libroom ${LIBROOM_SOURCES}) + +# Include directories +target_include_directories(libroom PRIVATE + . + ${LIBROOM_SRC_DIR} +) + +# Link dependencies from FetchContent +target_link_libraries(libroom PRIVATE + Eigen3::Eigen + nanoflann::nanoflann +) + +# Compile options +if(MSVC) + target_compile_options(libroom PRIVATE /EHsc) +else() + target_compile_options(libroom PRIVATE -O3 -Wall -fvisibility=hidden) +endif() + +# Eigen definitions +target_compile_definitions(libroom PRIVATE EIGEN_MPL2_ONLY EIGEN_NO_DEBUG) + +# Setting the output name and location +set_target_properties(libroom PROPERTIES + PREFIX "" + OUTPUT_NAME "libroom" +) diff --git a/MANIFEST.in b/MANIFEST.in index c497c5bf..a07e0a44 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -7,11 +7,9 @@ include pyroomacoustics/libroom_src/*.h include pyroomacoustics/libroom_src/*.hpp include pyroomacoustics/libroom_src/*.cpp -# The compiled extension code rely on Eigen and nanoflann, -# which are included -include pyroomacoustics/libroom_src/ext/eigen/COPYING.* -graft pyroomacoustics/libroom_src/ext/eigen/Eigen -graft pyroomacoustics/libroom_src/ext/nanoflann/include +# CMake build files +include CMakeLists.txt +graft external include pyproject.toml include requirements.txt @@ -26,7 +24,6 @@ graft pyroomacoustics/denoise/tests graft pyroomacoustics/directivities/tests graft pyroomacoustics/doa/tests graft pyroomacoustics/experimental/tests -graft pyroomacoustics/libroom_src/tests graft pyroomacoustics/phase/tests graft pyroomacoustics/transform/tests diff --git a/external/CMakeLists.txt b/external/CMakeLists.txt new file mode 100644 index 00000000..4df7d600 --- /dev/null +++ b/external/CMakeLists.txt @@ -0,0 +1,37 @@ +include(FetchContent) + +# eigen +FetchContent_Declare( + eigen + GIT_REPOSITORY https://gitlab.com/libeigen/eigen + GIT_TAG 5.0.1 +) + +# nanoflann +FetchContent_Declare( + nanoflann + GIT_REPOSITORY https://github.com/jlblancoc/nanoflann + GIT_TAG v1.9.0 +) + +# pybind11 +FetchContent_Declare( + pybind11 + GIT_REPOSITORY https://github.com/pybind/pybind11 + GIT_TAG v3.0.1 +) + +# eigen +set(EIGEN_BUILD_PKGCONFIG OFF CACHE BOOL "Disable Eigen pkg-config") +set(EIGEN_BUILD_TESTING OFF CACHE BOOL "Disable Eigen testing") +set(BUILD_TESTING OFF CACHE BOOL "Disable testing") + +# nanoflann +set(NANOFLANN_BUILD_EXAMPLES OFF CACHE BOOL "Disable nanoflann examples" FORCE) +set(NANOFLANN_BUILD_TESTS OFF CACHE BOOL "Disable nanoflann tests" FORCE) +set(BUILD_BENCHMARKS OFF CACHE BOOL "Disable nanoflann benchmarks" FORCE) + +# pybind11 +set(PYBIND11_TEST OFF CACHE BOOL "Disable pybind11 tests") + +FetchContent_MakeAvailable(eigen nanoflann pybind11) diff --git a/pyproject.toml b/pyproject.toml index d6903457..e62e6438 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,8 +1,76 @@ [build-system] -# Minimum requirements for the build system to execute. -requires = ["setuptools", "wheel", "numpy>=1.13.0", "Cython", "pybind11>=2.2"] # PEP 508 specifications. +requires = ["setuptools>=61", "setuptools-scm>=8", "wheel", "numpy>=1.13.0", "Cython", "cmake"] +build-backend = "setuptools.build_meta" + +[project] +name = "pyroomacoustics" +dynamic = ["version", "readme"] +description = "A simple framework for room acoustics and audio processing in Python." +authors = [ + { name = "Laboratory for Audiovisual Communications, EPFL", email = "fakufaku@gmail.ch" } +] +license = "MIT" +keywords = ["room", "acoustics", "signal processing", "doa", "beamforming", "adaptive"] +classifiers = [ + "Development Status :: 4 - Beta", + "Intended Audience :: Science/Research", + "Intended Audience :: Information Technology", + "Topic :: Scientific/Engineering :: Information Analysis", + "Topic :: Scientific/Engineering :: Physics", + "Topic :: Multimedia :: Sound/Audio :: Speech", + "Topic :: Multimedia :: Sound/Audio :: Analysis", + "Programming Language :: Python :: 3.8", + "Programming Language :: Python :: 3.9", + "Programming Language :: Python :: 3.10", + "Programming Language :: Python :: 3.11", + "Programming Language :: Python :: 3.12", +] +dependencies = [ + "Cython", + "numpy>=1.13.0", + "scipy>=0.18.0", +] +requires-python = ">=3.8" + +[project.urls] +Homepage = "https://github.com/LCAV/pyroomacoustics" + +[tool.setuptools.dynamic] +readme = { file = ["README.rst"], content-type = "text/x-rst" } + +[tool.setuptools] +packages = [ + "pyroomacoustics", + "pyroomacoustics.adaptive", + "pyroomacoustics.bss", + "pyroomacoustics.datasets", + "pyroomacoustics.denoise", + "pyroomacoustics.directivities", + "pyroomacoustics.doa", + "pyroomacoustics.experimental", + "pyroomacoustics.phase", + "pyroomacoustics.random", + "pyroomacoustics.simulation", + "pyroomacoustics.transform", +] + +[tool.setuptools.package-data] +pyroomacoustics = [ + "*.pxd", + "*.pyx", + "data/materials.json", + "data/sofa_files.json", + "data/sofa/AKG_c480_c414_CUBE.sofa", + "data/sofa/EM32_Directivity.sofa", + "data/sofa/mit_kemar_large_pinna.sofa", + "data/sofa/mit_kemar_normal_pinna.sofa", +] + +[tool.setuptools_scm] +write_to = "pyroomacoustics/version.py" [tool.pytest.ini_options] +testpaths = ["tests"] filterwarnings = [ 'ignore:Matplotlib is currently using agg, which is a non-GUI backend, so cannot show the figure.:UserWarning' ] diff --git a/pyroomacoustics/libroom_src/common.hpp b/pyroomacoustics/libroom_src/common.hpp index 8d1ef382..664ae1eb 100644 --- a/pyroomacoustics/libroom_src/common.hpp +++ b/pyroomacoustics/libroom_src/common.hpp @@ -29,6 +29,8 @@ #define __COMMON_HPP__ #include +#include +#include #include extern float libroom_eps; // epsilon is the precision for floating point computations. It is defined in libroom.cpp diff --git a/pyroomacoustics/libroom_src/ext/eigen b/pyroomacoustics/libroom_src/ext/eigen deleted file mode 160000 index a1b9c26c..00000000 --- a/pyroomacoustics/libroom_src/ext/eigen +++ /dev/null @@ -1 +0,0 @@ -Subproject commit a1b9c26c5e62cb8c17836e601edd64b92aa8e5ae diff --git a/pyroomacoustics/libroom_src/ext/nanoflann b/pyroomacoustics/libroom_src/ext/nanoflann deleted file mode 160000 index b3e81831..00000000 --- a/pyroomacoustics/libroom_src/ext/nanoflann +++ /dev/null @@ -1 +0,0 @@ -Subproject commit b3e81831b847a77473e0da2ad7ee266b4f4e0d9a diff --git a/pyroomacoustics/version.py b/pyroomacoustics/version.py deleted file mode 100644 index 61fb31ca..00000000 --- a/pyroomacoustics/version.py +++ /dev/null @@ -1 +0,0 @@ -__version__ = "0.10.0" diff --git a/setup.py b/setup.py index 6a2246ba..43abc039 100644 --- a/setup.py +++ b/setup.py @@ -1,83 +1,75 @@ -#!/usr/bin/env python -from __future__ import print_function - import os +import platform +import subprocess import sys +from pathlib import Path -# To use a consistent encoding -from os import path - -# import version from file -with open("pyroomacoustics/version.py") as f: - exec(f.read()) - -try: - from setuptools import Extension, distutils, setup - from setuptools.command.build_ext import build_ext -except ImportError: - print("Setuptools unavailable. Falling back to distutils.") - import distutils - from distutils.command.build_ext import build_ext - from distutils.core import setup - from distutils.extension import Extension +from setuptools import Extension, setup +from setuptools.command.build_ext import build_ext -class get_pybind_include(object): - """Helper class to determine the pybind11 include path +class CMakeExtension(Extension): + def __init__(self, name: str, sourcedir: str = "") -> None: + super().__init__(name, sources=[]) + self.sourcedir = os.fspath(Path(sourcedir).resolve()) - The purpose of this class is to postpone importing pybind11 - until it is actually installed, so that the ``get_include()`` - method can be invoked.""" - def __init__(self, user=False): - self.user = user +class CMakeBuild(build_ext): + def run(self): + try: + subprocess.check_output(["cmake", "--version"]) + except OSError: + raise RuntimeError( + "CMake must be installed to build the following extensions: " + + ", ".join( + e.name for e in self.extensions if isinstance(e, CMakeExtension) + ) + ) + + super().run() + + def build_extension(self, ext): + if not isinstance(ext, CMakeExtension): + super().build_extension(ext) + return + + extdir = os.path.abspath(os.path.dirname(self.get_ext_fullpath(ext.name))) + cmake_args = [ + "-DCMAKE_LIBRARY_OUTPUT_DIRECTORY=" + extdir, + "-DPYTHON_EXECUTABLE=" + sys.executable, + ] - def __str__(self): - import pybind11 + cfg = "Debug" if self.debug else "Release" + build_args = ["--config", cfg] + + if platform.system() == "Windows": + cmake_args += [ + "-DCMAKE_LIBRARY_OUTPUT_DIRECTORY_{}={}".format(cfg.upper(), extdir) + ] + if sys.maxsize > 2**32: + cmake_args += ["-A", "x64"] + build_args += ["--", "/m"] + else: + cmake_args += ["-DCMAKE_BUILD_TYPE=" + cfg] + build_args += ["--", "-j2"] + + env = os.environ.copy() + env["CXXFLAGS"] = '{} -DVERSION_INFO=\\"{}\\"'.format( + env.get("CXXFLAGS", ""), self.distribution.get_version() + ) + if not os.path.exists(self.build_temp): + os.makedirs(self.build_temp) - return pybind11.get_include(self.user) + subprocess.check_call( + ["cmake", ext.sourcedir] + cmake_args, cwd=self.build_temp, env=env + ) + subprocess.check_call( + ["cmake", "--build", "."] + build_args, cwd=self.build_temp + ) -# build C extension for image source model -libroom_src_dir = "pyroomacoustics/libroom_src" -libroom_files = [ - os.path.join(libroom_src_dir, f) - for f in [ - "room.hpp", - "room.cpp", - "wall.hpp", - "wall.cpp", - "microphone.hpp", - "geometry.hpp", - "geometry.cpp", - "common.hpp", - "rir_builder.cpp", - "rir_builder.hpp", - "libroom.cpp", - "threadpool.hpp", - ] -] ext_modules = [ - Extension( - "pyroomacoustics.libroom", - [os.path.join(libroom_src_dir, f) for f in ["libroom.cpp"]], - depends=libroom_files, - include_dirs=[ - ".", - libroom_src_dir, - str(get_pybind_include()), - str(get_pybind_include(user=True)), - os.path.join(libroom_src_dir, "ext/eigen"), - os.path.join(libroom_src_dir, "ext/nanoflann/include"), - ], - language="c++", - extra_compile_args=[ - "-DEIGEN_MPL2_ONLY", - "-Wall", - "-O3", - "-DEIGEN_NO_DEBUG", - ], - ), + CMakeExtension("pyroomacoustics.libroom", sourcedir="."), Extension( "pyroomacoustics.build_rir", ["pyroomacoustics/build_rir.pyx"], @@ -86,155 +78,7 @@ def __str__(self): ), ] -here = path.abspath(path.dirname(__file__)) - -# Get the long description from the README file -with open(path.join(here, "README.rst"), encoding="utf-8") as f: - long_description = f.read() - - -### Build Tools (taken from pybind11 example) ### - - -# As of Python 3.6, CCompiler has a `has_flag` method. -# cf http://bugs.python.org/issue26689 -def has_flag(compiler, flagname): - """Return a boolean indicating whether a flag name is supported on - the specified compiler. - """ - import tempfile - - with tempfile.NamedTemporaryFile("w", suffix=".cpp") as f: - f.write("int main (int argc, char **argv) { return 0; }") - try: - compiler.compile([f.name], extra_postargs=[flagname]) - except distutils.errors.CompileError: - return False - return True - - -def cpp_flag(compiler): - """Return the -std=c++[11/14] compiler flag. - - The c++14 is prefered over c++11 (when it is available). - """ - if has_flag(compiler, "-std=c++14"): - return "-std=c++14" - elif has_flag(compiler, "-std=c++11"): - return "-std=c++11" - else: - raise RuntimeError( - "Unsupported compiler -- at least C++11 support " "is needed!" - ) - - -class BuildExt(build_ext): - """A custom build extension for adding compiler-specific options.""" - - c_opts = { - "msvc": ["/EHsc"], - "unix": [], - } - - if sys.platform == "darwin": - c_opts["unix"] += ["-stdlib=libc++", "-mmacosx-version-min=10.7"] - - def build_extensions(self): - ct = self.compiler.compiler_type - opts = self.c_opts.get(ct, []) - if ct == "unix": - opts.append('-DVERSION_INFO="%s"' % self.distribution.get_version()) - opts.append(cpp_flag(self.compiler)) - if has_flag(self.compiler, "-fvisibility=hidden"): - opts.append("-fvisibility=hidden") - elif ct == "msvc": - opts.append('/DVERSION_INFO=\\"%s\\"' % self.distribution.get_version()) - for ext in self.extensions: - if ext.language == "c++": - ext.extra_compile_args += opts - ext.extra_link_args += opts - build_ext.build_extensions(self) - - -### Build Tools End ### - - -setup_kwargs = dict( - name="pyroomacoustics", - version=__version__, - description="A simple framework for room acoustics and audio processing in Python.", - long_description=long_description, - long_description_content_type="text/x-rst", - author="Laboratory for Audiovisual Communications, EPFL", - author_email="fakufaku@gmail.ch", - url="https://github.com/LCAV/pyroomacoustics", - license="MIT", - # You can just specify the packages manually here if your project is - # simple. Or you can use find_packages(). - packages=[ - "pyroomacoustics", - "pyroomacoustics.adaptive", - "pyroomacoustics.bss", - "pyroomacoustics.datasets", - "pyroomacoustics.denoise", - "pyroomacoustics.directivities", - "pyroomacoustics.doa", - "pyroomacoustics.experimental", - "pyroomacoustics.phase", - "pyroomacoustics.random", - "pyroomacoustics.simulation", - "pyroomacoustics.transform", - ], - # Libroom C extension +setup( ext_modules=ext_modules, - # Necessary to keep the source files - package_data={ - "pyroomacoustics": [ - "*.pxd", - "*.pyx", - "data/materials.json", - "data/sofa_files.json", - "data/sofa/AKG_c480_c414_CUBE.sofa", - "data/sofa/EM32_Directivity.sofa", - "data/sofa/mit_kemar_large_pinna.sofa", - "data/sofa/mit_kemar_normal_pinna.sofa", - ] - }, - install_requires=[ - "Cython", - "numpy>=1.13.0", - "scipy>=0.18.0", - "pybind11>=2.2", - ], - cmdclass={"build_ext": BuildExt}, # taken from pybind11 example - zip_safe=False, - test_suite="pytest", - tests_require=["pytest"], - classifiers=[ - # How mature is this project? Common values are - # 3 - Alpha - # 4 - Beta - # 5 - Production/Stable - "Development Status :: 4 - Beta", - # Indicate who your project is intended for - "Intended Audience :: Science/Research", - "Intended Audience :: Information Technology", - "Topic :: Scientific/Engineering :: Information Analysis", - "Topic :: Scientific/Engineering :: Physics", - "Topic :: Multimedia :: Sound/Audio :: Speech", - "Topic :: Multimedia :: Sound/Audio :: Analysis", - # Pick your license as you wish (should match "license" above) - "License :: OSI Approved :: MIT License", - # Specify the Python versions you support here. In particular, ensure - # that you indicate whether you support Python 2, Python 3 or both. - "Programming Language :: Python :: 3.8", - "Programming Language :: Python :: 3.9", - "Programming Language :: Python :: 3.10", - "Programming Language :: Python :: 3.11", - "Programming Language :: Python :: 3.12", - ], - # What does your project relate to? - keywords="room acoustics signal processing doa beamforming adaptive", + cmdclass={"build_ext": CMakeBuild}, ) - -setup(**setup_kwargs) From a2b1d19914eafd3c0523494171a887f91744d2cc Mon Sep 17 00:00:00 2001 From: Robin Scheibler Date: Fri, 3 Apr 2026 17:15:07 +0900 Subject: [PATCH 5/9] Moving tests out of the main package. --- .pre-commit-config.yaml | 4 ++-- MANIFEST.in | 21 +++++++++--------- .../tests => tests/adaptive}/test_adaptive.py | 0 .../adaptive}/test_adaptive_frequency.py | 0 .../bss/tests => tests/bss}/test_bss.py | 14 +++++++----- .../bss/tests => tests/bss}/test_trinicon.py | 15 ++++++++----- .../datasets}/test_corpus_base.py | 0 .../datasets}/test_download_uncompress.py | 0 .../denoise}/test_iterative_wiener.py | 0 .../tests => tests/denoise}/test_subspace.py | 0 ...CUBE-AKG_c414A-minphase_False-cardioid.npy | Bin ..._CUBE-AKG_c414A-minphase_False-oneside.npy | Bin ..._CUBE-AKG_c414A-minphase_True-cardioid.npy | Bin ...4_CUBE-AKG_c414A-minphase_True-oneside.npy | Bin ...CUBE-AKG_c414K-minphase_False-cardioid.npy | Bin ..._CUBE-AKG_c414K-minphase_False-oneside.npy | Bin ..._CUBE-AKG_c414K-minphase_True-cardioid.npy | Bin ...4_CUBE-AKG_c414K-minphase_True-oneside.npy | Bin ...CUBE-AKG_c414N-minphase_False-cardioid.npy | Bin ..._CUBE-AKG_c414N-minphase_False-oneside.npy | Bin ..._CUBE-AKG_c414N-minphase_True-cardioid.npy | Bin ...4_CUBE-AKG_c414N-minphase_True-oneside.npy | Bin ...CUBE-AKG_c414S-minphase_False-cardioid.npy | Bin ..._CUBE-AKG_c414S-minphase_False-oneside.npy | Bin ..._CUBE-AKG_c414S-minphase_True-cardioid.npy | Bin ...4_CUBE-AKG_c414S-minphase_True-oneside.npy | Bin ..._CUBE-AKG_c480-minphase_False-cardioid.npy | Bin ...4_CUBE-AKG_c480-minphase_False-oneside.npy | Bin ...4_CUBE-AKG_c480-minphase_True-cardioid.npy | Bin ...14_CUBE-AKG_c480-minphase_True-oneside.npy | Bin ...tivity-EM_32_0-minphase_False-cardioid.npy | Bin ...ctivity-EM_32_0-minphase_False-oneside.npy | Bin ...ctivity-EM_32_0-minphase_True-cardioid.npy | Bin ...ectivity-EM_32_0-minphase_True-oneside.npy | Bin ...ivity-EM_32_31-minphase_False-cardioid.npy | Bin ...tivity-EM_32_31-minphase_False-oneside.npy | Bin ...tivity-EM_32_31-minphase_True-cardioid.npy | Bin ...ctivity-EM_32_31-minphase_True-oneside.npy | Bin ..._CUBE-AKG_c480-minphase_False-twosides.npy | Bin ...4_CUBE-AKG_c480-minphase_True-twosides.npy | Bin ...ivity-EM_32_31-minphase_False-twosides.npy | Bin ...tivity-EM_32_31-minphase_True-twosides.npy | Bin ...z-Genelec_8020-minphase_False-cardioid.npy | Bin ...tz-Genelec_8020-minphase_False-oneside.npy | Bin ...tz-Genelec_8020-minphase_True-cardioid.npy | Bin ...atz-Genelec_8020-minphase_True-oneside.npy | Bin ...CUBE-AKG_c414K-minphase_False-twosides.npy | Bin ..._CUBE-AKG_c414K-minphase_True-twosides.npy | Bin ...tivity-EM_32_0-minphase_False-twosides.npy | Bin ...ctivity-EM_32_0-minphase_True-twosides.npy | Bin ...rolux_2x10inch-minphase_False-cardioid.npy | Bin ...brolux_2x10inch-minphase_False-oneside.npy | Bin ...brolux_2x10inch-minphase_True-cardioid.npy | Bin ...ibrolux_2x10inch-minphase_True-oneside.npy | Bin .../directivities}/test_cardioid_energy.py | 0 .../test_cardioid_energy_distribution.py | 0 .../test_directive_energy_rt_vs_ism.py | 0 .../test_harmonics_directivities.py | 0 .../test_measured_energy_distribution.py | 0 .../directivities}/test_omni_global_delay.py | 0 .../directivities}/test_sofa_directivities.py | 0 .../test_source_directivities.py | 0 .../test_source_directivities_nonshoebox.py | 0 .../test_source_directivity_flipping.py | 0 .../doa/tests => tests/doa}/test_doa.py | 0 .../doa/tests => tests/doa}/test_grid.py | 0 .../doa/tests => tests/doa}/test_utils.py | 0 .../experimental}/test_deconvolution.py | 0 .../experimental}/test_measure_rt60.py | 0 .../libroom}/test_ccw3p.py | 0 .../libroom}/test_geometry_routines.py | 0 .../libroom}/test_is_inside_2d_polygon.py | 0 .../libroom}/test_microphone.py | 0 .../libroom}/test_ray_energy.py | 0 .../libroom}/test_room_broadcast_n_bands.py | 0 .../libroom}/test_room_construct.py | 0 .../libroom}/test_room_walls.py | 0 .../test_sample_lambertian_reflection.py | 0 .../libroom}/test_shoebox_simple.py | 0 .../libroom}/test_wall_construct.py | 0 .../libroom}/test_wall_intersection.py | 0 .../libroom}/test_wall_side_reflect.py | 0 .../phase/tests => tests/phase}/test_gl.py | 5 ++++- .../random}/test_distributions.py | 0 .../tests => tests/random}/test_rejection.py | 0 .../tests => tests/random}/test_spherical.py | 0 .../tests => tests}/test_air_absorption.py | 0 .../tests => tests}/test_anechoic_room.py | 0 .../tests => tests}/test_angle_function.py | 0 .../tests => tests}/test_autocorr.py | 0 .../test_bandpass_filterbank.py | 0 .../tests => tests}/test_build_rir.py | 0 .../test_create_noisy_signal.py | 1 - .../test_from_corners_extrude.py | 0 {pyroomacoustics/tests => tests}/test_hpf.py | 0 .../tests => tests}/test_issue_115.py | 0 .../tests => tests}/test_issue_162.py | 0 .../tests => tests}/test_issue_22.py | 0 .../tests => tests}/test_issue_235.py | 0 .../tests => tests}/test_issue_236.py | 0 .../tests => tests}/test_issue_313.py | 0 .../tests => tests}/test_issue_348.py | 0 .../tests => tests}/test_issue_353.py | 0 .../tests => tests}/test_issue_69.py | 0 .../tests => tests}/test_issue_87.py | 0 .../tests => tests}/test_materials.py | 0 .../tests => tests}/test_metrics.py | 0 .../tests => tests}/test_microphone_array.py | 0 .../tests => tests}/test_octave_bands.py | 0 .../tests => tests}/test_rake_filters.py | 0 .../tests => tests}/test_random_ism.py | 0 .../tests => tests}/test_resample.py | 0 .../tests => tests}/test_rir_hpf.py | 0 .../tests => tests}/test_room_add.py | 0 .../tests => tests}/test_room_is_insided.py | 0 .../tests => tests}/test_room_mix.py | 0 .../tests => tests}/test_room_plot.py | 0 .../test_room_scattering_rt60.py | 0 {pyroomacoustics/tests => tests}/test_rt60.py | 0 .../test_rt_multiband_energy.py | 0 .../tests => tests}/test_shoebox_constants.py | 0 .../tests => tests}/test_soundsource.py | 0 {pyroomacoustics/tests => tests}/test_sync.py | 0 .../test_visibility_shoebox2d.py | 0 .../test_visibility_shoebox3d.py | 0 .../tests => tests}/test_visibility_uroom.py | 0 .../tests => tests}/test_volume_area.py | 0 .../tests => tests/transform}/test_dft.py | 0 .../transform}/test_dft_timing.py | 0 .../tests => tests/transform}/test_stft.py | 0 .../transform}/test_stft_oneshot.py | 0 .../transform}/test_stft_timing.py | 0 132 files changed, 34 insertions(+), 26 deletions(-) rename {pyroomacoustics/adaptive/tests => tests/adaptive}/test_adaptive.py (100%) rename {pyroomacoustics/adaptive/tests => tests/adaptive}/test_adaptive_frequency.py (100%) rename {pyroomacoustics/bss/tests => tests/bss}/test_bss.py (91%) rename {pyroomacoustics/bss/tests => tests/bss}/test_trinicon.py (89%) rename {pyroomacoustics/datasets/tests => tests/datasets}/test_corpus_base.py (100%) rename {pyroomacoustics/datasets/tests => tests/datasets}/test_download_uncompress.py (100%) rename {pyroomacoustics/denoise/tests => tests/denoise}/test_iterative_wiener.py (100%) rename {pyroomacoustics/denoise/tests => tests/denoise}/test_subspace.py (100%) rename {pyroomacoustics/directivities/tests => tests/directivities}/data/AKG_c480_c414_CUBE-AKG_c414A-minphase_False-cardioid.npy (100%) rename {pyroomacoustics/directivities/tests => tests/directivities}/data/AKG_c480_c414_CUBE-AKG_c414A-minphase_False-oneside.npy (100%) rename {pyroomacoustics/directivities/tests => tests/directivities}/data/AKG_c480_c414_CUBE-AKG_c414A-minphase_True-cardioid.npy (100%) rename {pyroomacoustics/directivities/tests => tests/directivities}/data/AKG_c480_c414_CUBE-AKG_c414A-minphase_True-oneside.npy (100%) rename {pyroomacoustics/directivities/tests => tests/directivities}/data/AKG_c480_c414_CUBE-AKG_c414K-minphase_False-cardioid.npy (100%) rename {pyroomacoustics/directivities/tests => tests/directivities}/data/AKG_c480_c414_CUBE-AKG_c414K-minphase_False-oneside.npy (100%) rename {pyroomacoustics/directivities/tests => tests/directivities}/data/AKG_c480_c414_CUBE-AKG_c414K-minphase_True-cardioid.npy (100%) rename {pyroomacoustics/directivities/tests => tests/directivities}/data/AKG_c480_c414_CUBE-AKG_c414K-minphase_True-oneside.npy (100%) rename {pyroomacoustics/directivities/tests => tests/directivities}/data/AKG_c480_c414_CUBE-AKG_c414N-minphase_False-cardioid.npy (100%) rename {pyroomacoustics/directivities/tests => tests/directivities}/data/AKG_c480_c414_CUBE-AKG_c414N-minphase_False-oneside.npy (100%) rename {pyroomacoustics/directivities/tests => tests/directivities}/data/AKG_c480_c414_CUBE-AKG_c414N-minphase_True-cardioid.npy (100%) rename {pyroomacoustics/directivities/tests => tests/directivities}/data/AKG_c480_c414_CUBE-AKG_c414N-minphase_True-oneside.npy (100%) rename {pyroomacoustics/directivities/tests => tests/directivities}/data/AKG_c480_c414_CUBE-AKG_c414S-minphase_False-cardioid.npy (100%) rename {pyroomacoustics/directivities/tests => tests/directivities}/data/AKG_c480_c414_CUBE-AKG_c414S-minphase_False-oneside.npy (100%) rename {pyroomacoustics/directivities/tests => tests/directivities}/data/AKG_c480_c414_CUBE-AKG_c414S-minphase_True-cardioid.npy (100%) rename {pyroomacoustics/directivities/tests => tests/directivities}/data/AKG_c480_c414_CUBE-AKG_c414S-minphase_True-oneside.npy (100%) rename {pyroomacoustics/directivities/tests => tests/directivities}/data/AKG_c480_c414_CUBE-AKG_c480-minphase_False-cardioid.npy (100%) rename {pyroomacoustics/directivities/tests => tests/directivities}/data/AKG_c480_c414_CUBE-AKG_c480-minphase_False-oneside.npy (100%) rename {pyroomacoustics/directivities/tests => tests/directivities}/data/AKG_c480_c414_CUBE-AKG_c480-minphase_True-cardioid.npy (100%) rename {pyroomacoustics/directivities/tests => tests/directivities}/data/AKG_c480_c414_CUBE-AKG_c480-minphase_True-oneside.npy (100%) rename {pyroomacoustics/directivities/tests => tests/directivities}/data/EM32_Directivity-EM_32_0-minphase_False-cardioid.npy (100%) rename {pyroomacoustics/directivities/tests => tests/directivities}/data/EM32_Directivity-EM_32_0-minphase_False-oneside.npy (100%) rename {pyroomacoustics/directivities/tests => tests/directivities}/data/EM32_Directivity-EM_32_0-minphase_True-cardioid.npy (100%) rename {pyroomacoustics/directivities/tests => tests/directivities}/data/EM32_Directivity-EM_32_0-minphase_True-oneside.npy (100%) rename {pyroomacoustics/directivities/tests => tests/directivities}/data/EM32_Directivity-EM_32_31-minphase_False-cardioid.npy (100%) rename {pyroomacoustics/directivities/tests => tests/directivities}/data/EM32_Directivity-EM_32_31-minphase_False-oneside.npy (100%) rename {pyroomacoustics/directivities/tests => tests/directivities}/data/EM32_Directivity-EM_32_31-minphase_True-cardioid.npy (100%) rename {pyroomacoustics/directivities/tests => tests/directivities}/data/EM32_Directivity-EM_32_31-minphase_True-oneside.npy (100%) rename {pyroomacoustics/directivities/tests => tests/directivities}/data/LSPs_HATS_GuitarCabinets_Akustikmessplatz-Genelec_8020-AKG_c480_c414_CUBE-AKG_c480-minphase_False-twosides.npy (100%) rename {pyroomacoustics/directivities/tests => tests/directivities}/data/LSPs_HATS_GuitarCabinets_Akustikmessplatz-Genelec_8020-AKG_c480_c414_CUBE-AKG_c480-minphase_True-twosides.npy (100%) rename {pyroomacoustics/directivities/tests => tests/directivities}/data/LSPs_HATS_GuitarCabinets_Akustikmessplatz-Genelec_8020-EM32_Directivity-EM_32_31-minphase_False-twosides.npy (100%) rename {pyroomacoustics/directivities/tests => tests/directivities}/data/LSPs_HATS_GuitarCabinets_Akustikmessplatz-Genelec_8020-EM32_Directivity-EM_32_31-minphase_True-twosides.npy (100%) rename {pyroomacoustics/directivities/tests => tests/directivities}/data/LSPs_HATS_GuitarCabinets_Akustikmessplatz-Genelec_8020-minphase_False-cardioid.npy (100%) rename {pyroomacoustics/directivities/tests => tests/directivities}/data/LSPs_HATS_GuitarCabinets_Akustikmessplatz-Genelec_8020-minphase_False-oneside.npy (100%) rename {pyroomacoustics/directivities/tests => tests/directivities}/data/LSPs_HATS_GuitarCabinets_Akustikmessplatz-Genelec_8020-minphase_True-cardioid.npy (100%) rename {pyroomacoustics/directivities/tests => tests/directivities}/data/LSPs_HATS_GuitarCabinets_Akustikmessplatz-Genelec_8020-minphase_True-oneside.npy (100%) rename {pyroomacoustics/directivities/tests => tests/directivities}/data/LSPs_HATS_GuitarCabinets_Akustikmessplatz-Vibrolux_2x10inch-AKG_c480_c414_CUBE-AKG_c414K-minphase_False-twosides.npy (100%) rename {pyroomacoustics/directivities/tests => tests/directivities}/data/LSPs_HATS_GuitarCabinets_Akustikmessplatz-Vibrolux_2x10inch-AKG_c480_c414_CUBE-AKG_c414K-minphase_True-twosides.npy (100%) rename {pyroomacoustics/directivities/tests => tests/directivities}/data/LSPs_HATS_GuitarCabinets_Akustikmessplatz-Vibrolux_2x10inch-EM32_Directivity-EM_32_0-minphase_False-twosides.npy (100%) rename {pyroomacoustics/directivities/tests => tests/directivities}/data/LSPs_HATS_GuitarCabinets_Akustikmessplatz-Vibrolux_2x10inch-EM32_Directivity-EM_32_0-minphase_True-twosides.npy (100%) rename {pyroomacoustics/directivities/tests => tests/directivities}/data/LSPs_HATS_GuitarCabinets_Akustikmessplatz-Vibrolux_2x10inch-minphase_False-cardioid.npy (100%) rename {pyroomacoustics/directivities/tests => tests/directivities}/data/LSPs_HATS_GuitarCabinets_Akustikmessplatz-Vibrolux_2x10inch-minphase_False-oneside.npy (100%) rename {pyroomacoustics/directivities/tests => tests/directivities}/data/LSPs_HATS_GuitarCabinets_Akustikmessplatz-Vibrolux_2x10inch-minphase_True-cardioid.npy (100%) rename {pyroomacoustics/directivities/tests => tests/directivities}/data/LSPs_HATS_GuitarCabinets_Akustikmessplatz-Vibrolux_2x10inch-minphase_True-oneside.npy (100%) rename {pyroomacoustics/directivities/tests => tests/directivities}/test_cardioid_energy.py (100%) rename {pyroomacoustics/directivities/tests => tests/directivities}/test_cardioid_energy_distribution.py (100%) rename {pyroomacoustics/directivities/tests => tests/directivities}/test_directive_energy_rt_vs_ism.py (100%) rename {pyroomacoustics/directivities/tests => tests/directivities}/test_harmonics_directivities.py (100%) rename {pyroomacoustics/directivities/tests => tests/directivities}/test_measured_energy_distribution.py (100%) rename {pyroomacoustics/directivities/tests => tests/directivities}/test_omni_global_delay.py (100%) rename {pyroomacoustics/directivities/tests => tests/directivities}/test_sofa_directivities.py (100%) rename {pyroomacoustics/directivities/tests => tests/directivities}/test_source_directivities.py (100%) rename {pyroomacoustics/directivities/tests => tests/directivities}/test_source_directivities_nonshoebox.py (100%) rename {pyroomacoustics/directivities/tests => tests/directivities}/test_source_directivity_flipping.py (100%) rename {pyroomacoustics/doa/tests => tests/doa}/test_doa.py (100%) rename {pyroomacoustics/doa/tests => tests/doa}/test_grid.py (100%) rename {pyroomacoustics/doa/tests => tests/doa}/test_utils.py (100%) rename {pyroomacoustics/experimental/tests => tests/experimental}/test_deconvolution.py (100%) rename {pyroomacoustics/experimental/tests => tests/experimental}/test_measure_rt60.py (100%) rename {pyroomacoustics/tests/tests_libroom => tests/libroom}/test_ccw3p.py (100%) rename {pyroomacoustics/tests/tests_libroom => tests/libroom}/test_geometry_routines.py (100%) rename {pyroomacoustics/tests/tests_libroom => tests/libroom}/test_is_inside_2d_polygon.py (100%) rename {pyroomacoustics/tests/tests_libroom => tests/libroom}/test_microphone.py (100%) rename {pyroomacoustics/tests/tests_libroom => tests/libroom}/test_ray_energy.py (100%) rename {pyroomacoustics/tests/tests_libroom => tests/libroom}/test_room_broadcast_n_bands.py (100%) rename {pyroomacoustics/tests/tests_libroom => tests/libroom}/test_room_construct.py (100%) rename {pyroomacoustics/tests/tests_libroom => tests/libroom}/test_room_walls.py (100%) rename {pyroomacoustics/tests/tests_libroom => tests/libroom}/test_sample_lambertian_reflection.py (100%) rename {pyroomacoustics/tests/tests_libroom => tests/libroom}/test_shoebox_simple.py (100%) rename {pyroomacoustics/tests/tests_libroom => tests/libroom}/test_wall_construct.py (100%) rename {pyroomacoustics/tests/tests_libroom => tests/libroom}/test_wall_intersection.py (100%) rename {pyroomacoustics/tests/tests_libroom => tests/libroom}/test_wall_side_reflect.py (100%) rename {pyroomacoustics/phase/tests => tests/phase}/test_gl.py (94%) rename {pyroomacoustics/random/tests => tests/random}/test_distributions.py (100%) rename {pyroomacoustics/random/tests => tests/random}/test_rejection.py (100%) rename {pyroomacoustics/random/tests => tests/random}/test_spherical.py (100%) rename {pyroomacoustics/tests => tests}/test_air_absorption.py (100%) rename {pyroomacoustics/tests => tests}/test_anechoic_room.py (100%) rename {pyroomacoustics/tests => tests}/test_angle_function.py (100%) rename {pyroomacoustics/tests => tests}/test_autocorr.py (100%) rename {pyroomacoustics/tests => tests}/test_bandpass_filterbank.py (100%) rename {pyroomacoustics/tests => tests}/test_build_rir.py (100%) rename {pyroomacoustics/tests => tests}/test_create_noisy_signal.py (99%) rename {pyroomacoustics/tests => tests}/test_from_corners_extrude.py (100%) rename {pyroomacoustics/tests => tests}/test_hpf.py (100%) rename {pyroomacoustics/tests => tests}/test_issue_115.py (100%) rename {pyroomacoustics/tests => tests}/test_issue_162.py (100%) rename {pyroomacoustics/tests => tests}/test_issue_22.py (100%) rename {pyroomacoustics/tests => tests}/test_issue_235.py (100%) rename {pyroomacoustics/tests => tests}/test_issue_236.py (100%) rename {pyroomacoustics/tests => tests}/test_issue_313.py (100%) rename {pyroomacoustics/tests => tests}/test_issue_348.py (100%) rename {pyroomacoustics/tests => tests}/test_issue_353.py (100%) rename {pyroomacoustics/tests => tests}/test_issue_69.py (100%) rename {pyroomacoustics/tests => tests}/test_issue_87.py (100%) rename {pyroomacoustics/tests => tests}/test_materials.py (100%) rename {pyroomacoustics/tests => tests}/test_metrics.py (100%) rename {pyroomacoustics/tests => tests}/test_microphone_array.py (100%) rename {pyroomacoustics/tests => tests}/test_octave_bands.py (100%) rename {pyroomacoustics/tests => tests}/test_rake_filters.py (100%) rename {pyroomacoustics/tests => tests}/test_random_ism.py (100%) rename {pyroomacoustics/tests => tests}/test_resample.py (100%) rename {pyroomacoustics/tests => tests}/test_rir_hpf.py (100%) rename {pyroomacoustics/tests => tests}/test_room_add.py (100%) rename {pyroomacoustics/tests => tests}/test_room_is_insided.py (100%) rename {pyroomacoustics/tests => tests}/test_room_mix.py (100%) rename {pyroomacoustics/tests => tests}/test_room_plot.py (100%) rename {pyroomacoustics/tests => tests}/test_room_scattering_rt60.py (100%) rename {pyroomacoustics/tests => tests}/test_rt60.py (100%) rename {pyroomacoustics/tests => tests}/test_rt_multiband_energy.py (100%) rename {pyroomacoustics/tests => tests}/test_shoebox_constants.py (100%) rename {pyroomacoustics/tests => tests}/test_soundsource.py (100%) rename {pyroomacoustics/tests => tests}/test_sync.py (100%) rename {pyroomacoustics/tests => tests}/test_visibility_shoebox2d.py (100%) rename {pyroomacoustics/tests => tests}/test_visibility_shoebox3d.py (100%) rename {pyroomacoustics/tests => tests}/test_visibility_uroom.py (100%) rename {pyroomacoustics/tests => tests}/test_volume_area.py (100%) rename {pyroomacoustics/transform/tests => tests/transform}/test_dft.py (100%) rename {pyroomacoustics/transform/tests => tests/transform}/test_dft_timing.py (100%) rename {pyroomacoustics/transform/tests => tests/transform}/test_stft.py (100%) rename {pyroomacoustics/transform/tests => tests/transform}/test_stft_oneshot.py (100%) rename {pyroomacoustics/transform/tests => tests/transform}/test_stft_timing.py (100%) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 9d92b6c9..e6800ab1 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -6,13 +6,13 @@ repos: rev: 7.0.0 hooks: - id: isort - files: ^(pyroomacoustics/|examples/|setup\.py) + files: ^(pyroomacoustics/|examples/|tests/|setup\.py) - repo: https://github.com/psf/black rev: 26.1.0 hooks: - id: black - files: ^(pyroomacoustics/|examples/|setup\.py) + files: ^(pyroomacoustics/|examples/|tests/|setup\.py) # - repo: https://github.com/pycqa/flake8 # rev: 7.3.0 diff --git a/MANIFEST.in b/MANIFEST.in index a07e0a44..fe73e872 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -16,16 +16,17 @@ include requirements.txt include LICENSE # Keep the test files too -graft pyroomacoustics/tests -graft pyroomacoustics/adaptive/tests -graft pyroomacoustics/bss/tests -graft pyroomacoustics/datasets/tests -graft pyroomacoustics/denoise/tests -graft pyroomacoustics/directivities/tests -graft pyroomacoustics/doa/tests -graft pyroomacoustics/experimental/tests -graft pyroomacoustics/phase/tests -graft pyroomacoustics/transform/tests +graft tests +graft tests/adaptive +graft tests/bss +graft tests/datasets +graft tests/denoise +graft tests/directivities +graft tests/doa +graft tests/experimental +graft tests/libroom +graft tests/phase +graft tests/transform global-exclude *.py[co] global-exclude __pycache__ diff --git a/pyroomacoustics/adaptive/tests/test_adaptive.py b/tests/adaptive/test_adaptive.py similarity index 100% rename from pyroomacoustics/adaptive/tests/test_adaptive.py rename to tests/adaptive/test_adaptive.py diff --git a/pyroomacoustics/adaptive/tests/test_adaptive_frequency.py b/tests/adaptive/test_adaptive_frequency.py similarity index 100% rename from pyroomacoustics/adaptive/tests/test_adaptive_frequency.py rename to tests/adaptive/test_adaptive_frequency.py diff --git a/pyroomacoustics/bss/tests/test_bss.py b/tests/bss/test_bss.py similarity index 91% rename from pyroomacoustics/bss/tests/test_bss.py rename to tests/bss/test_bss.py index 24e0aee5..287470f7 100644 --- a/pyroomacoustics/bss/tests/test_bss.py +++ b/tests/bss/test_bss.py @@ -1,4 +1,5 @@ import unittest +from pathlib import Path import numpy as np from scipy.io import wavfile @@ -8,16 +9,17 @@ np.random.seed(0) # We use several sound samples for each source to have a long enough length +wav_root = Path(__file__).parent / "../../" wav_files = [ [ - "examples/input_samples/cmu_arctic_us_axb_a0004.wav", - "examples/input_samples/cmu_arctic_us_axb_a0005.wav", - "examples/input_samples/cmu_arctic_us_axb_a0006.wav", + wav_root / "examples/input_samples/cmu_arctic_us_axb_a0004.wav", + wav_root / "examples/input_samples/cmu_arctic_us_axb_a0005.wav", + wav_root / "examples/input_samples/cmu_arctic_us_axb_a0006.wav", ], [ - "examples/input_samples/cmu_arctic_us_aew_a0001.wav", - "examples/input_samples/cmu_arctic_us_aew_a0002.wav", - "examples/input_samples/cmu_arctic_us_aew_a0003.wav", + wav_root / "examples/input_samples/cmu_arctic_us_aew_a0001.wav", + wav_root / "examples/input_samples/cmu_arctic_us_aew_a0002.wav", + wav_root / "examples/input_samples/cmu_arctic_us_aew_a0003.wav", ], ] diff --git a/pyroomacoustics/bss/tests/test_trinicon.py b/tests/bss/test_trinicon.py similarity index 89% rename from pyroomacoustics/bss/tests/test_trinicon.py rename to tests/bss/test_trinicon.py index f023ff3d..599c23e2 100644 --- a/pyroomacoustics/bss/tests/test_trinicon.py +++ b/tests/bss/test_trinicon.py @@ -1,19 +1,22 @@ +from pathlib import Path + import numpy as np from scipy.io import wavfile import pyroomacoustics as pra # We use several sound samples for each source to have a long enough length +wav_root = Path(__file__).parent / "../../" wav_files = [ [ - "examples/input_samples/cmu_arctic_us_axb_a0004.wav", - "examples/input_samples/cmu_arctic_us_axb_a0005.wav", - "examples/input_samples/cmu_arctic_us_axb_a0006.wav", + wav_root / "examples/input_samples/cmu_arctic_us_axb_a0004.wav", + wav_root / "examples/input_samples/cmu_arctic_us_axb_a0005.wav", + wav_root / "examples/input_samples/cmu_arctic_us_axb_a0006.wav", ], [ - "examples/input_samples/cmu_arctic_us_aew_a0001.wav", - "examples/input_samples/cmu_arctic_us_aew_a0002.wav", - "examples/input_samples/cmu_arctic_us_aew_a0003.wav", + wav_root / "examples/input_samples/cmu_arctic_us_aew_a0001.wav", + wav_root / "examples/input_samples/cmu_arctic_us_aew_a0002.wav", + wav_root / "examples/input_samples/cmu_arctic_us_aew_a0003.wav", ], ] diff --git a/pyroomacoustics/datasets/tests/test_corpus_base.py b/tests/datasets/test_corpus_base.py similarity index 100% rename from pyroomacoustics/datasets/tests/test_corpus_base.py rename to tests/datasets/test_corpus_base.py diff --git a/pyroomacoustics/datasets/tests/test_download_uncompress.py b/tests/datasets/test_download_uncompress.py similarity index 100% rename from pyroomacoustics/datasets/tests/test_download_uncompress.py rename to tests/datasets/test_download_uncompress.py diff --git a/pyroomacoustics/denoise/tests/test_iterative_wiener.py b/tests/denoise/test_iterative_wiener.py similarity index 100% rename from pyroomacoustics/denoise/tests/test_iterative_wiener.py rename to tests/denoise/test_iterative_wiener.py diff --git a/pyroomacoustics/denoise/tests/test_subspace.py b/tests/denoise/test_subspace.py similarity index 100% rename from pyroomacoustics/denoise/tests/test_subspace.py rename to tests/denoise/test_subspace.py diff --git a/pyroomacoustics/directivities/tests/data/AKG_c480_c414_CUBE-AKG_c414A-minphase_False-cardioid.npy b/tests/directivities/data/AKG_c480_c414_CUBE-AKG_c414A-minphase_False-cardioid.npy similarity index 100% rename from pyroomacoustics/directivities/tests/data/AKG_c480_c414_CUBE-AKG_c414A-minphase_False-cardioid.npy rename to tests/directivities/data/AKG_c480_c414_CUBE-AKG_c414A-minphase_False-cardioid.npy diff --git a/pyroomacoustics/directivities/tests/data/AKG_c480_c414_CUBE-AKG_c414A-minphase_False-oneside.npy b/tests/directivities/data/AKG_c480_c414_CUBE-AKG_c414A-minphase_False-oneside.npy similarity index 100% rename from pyroomacoustics/directivities/tests/data/AKG_c480_c414_CUBE-AKG_c414A-minphase_False-oneside.npy rename to tests/directivities/data/AKG_c480_c414_CUBE-AKG_c414A-minphase_False-oneside.npy diff --git a/pyroomacoustics/directivities/tests/data/AKG_c480_c414_CUBE-AKG_c414A-minphase_True-cardioid.npy b/tests/directivities/data/AKG_c480_c414_CUBE-AKG_c414A-minphase_True-cardioid.npy similarity index 100% rename from pyroomacoustics/directivities/tests/data/AKG_c480_c414_CUBE-AKG_c414A-minphase_True-cardioid.npy rename to tests/directivities/data/AKG_c480_c414_CUBE-AKG_c414A-minphase_True-cardioid.npy diff --git a/pyroomacoustics/directivities/tests/data/AKG_c480_c414_CUBE-AKG_c414A-minphase_True-oneside.npy b/tests/directivities/data/AKG_c480_c414_CUBE-AKG_c414A-minphase_True-oneside.npy similarity index 100% rename from pyroomacoustics/directivities/tests/data/AKG_c480_c414_CUBE-AKG_c414A-minphase_True-oneside.npy rename to tests/directivities/data/AKG_c480_c414_CUBE-AKG_c414A-minphase_True-oneside.npy diff --git a/pyroomacoustics/directivities/tests/data/AKG_c480_c414_CUBE-AKG_c414K-minphase_False-cardioid.npy b/tests/directivities/data/AKG_c480_c414_CUBE-AKG_c414K-minphase_False-cardioid.npy similarity index 100% rename from pyroomacoustics/directivities/tests/data/AKG_c480_c414_CUBE-AKG_c414K-minphase_False-cardioid.npy rename to tests/directivities/data/AKG_c480_c414_CUBE-AKG_c414K-minphase_False-cardioid.npy diff --git a/pyroomacoustics/directivities/tests/data/AKG_c480_c414_CUBE-AKG_c414K-minphase_False-oneside.npy b/tests/directivities/data/AKG_c480_c414_CUBE-AKG_c414K-minphase_False-oneside.npy similarity index 100% rename from pyroomacoustics/directivities/tests/data/AKG_c480_c414_CUBE-AKG_c414K-minphase_False-oneside.npy rename to tests/directivities/data/AKG_c480_c414_CUBE-AKG_c414K-minphase_False-oneside.npy diff --git a/pyroomacoustics/directivities/tests/data/AKG_c480_c414_CUBE-AKG_c414K-minphase_True-cardioid.npy b/tests/directivities/data/AKG_c480_c414_CUBE-AKG_c414K-minphase_True-cardioid.npy similarity index 100% rename from pyroomacoustics/directivities/tests/data/AKG_c480_c414_CUBE-AKG_c414K-minphase_True-cardioid.npy rename to tests/directivities/data/AKG_c480_c414_CUBE-AKG_c414K-minphase_True-cardioid.npy diff --git a/pyroomacoustics/directivities/tests/data/AKG_c480_c414_CUBE-AKG_c414K-minphase_True-oneside.npy b/tests/directivities/data/AKG_c480_c414_CUBE-AKG_c414K-minphase_True-oneside.npy similarity index 100% rename from pyroomacoustics/directivities/tests/data/AKG_c480_c414_CUBE-AKG_c414K-minphase_True-oneside.npy rename to tests/directivities/data/AKG_c480_c414_CUBE-AKG_c414K-minphase_True-oneside.npy diff --git a/pyroomacoustics/directivities/tests/data/AKG_c480_c414_CUBE-AKG_c414N-minphase_False-cardioid.npy b/tests/directivities/data/AKG_c480_c414_CUBE-AKG_c414N-minphase_False-cardioid.npy similarity index 100% rename from pyroomacoustics/directivities/tests/data/AKG_c480_c414_CUBE-AKG_c414N-minphase_False-cardioid.npy rename to tests/directivities/data/AKG_c480_c414_CUBE-AKG_c414N-minphase_False-cardioid.npy diff --git a/pyroomacoustics/directivities/tests/data/AKG_c480_c414_CUBE-AKG_c414N-minphase_False-oneside.npy b/tests/directivities/data/AKG_c480_c414_CUBE-AKG_c414N-minphase_False-oneside.npy similarity index 100% rename from pyroomacoustics/directivities/tests/data/AKG_c480_c414_CUBE-AKG_c414N-minphase_False-oneside.npy rename to tests/directivities/data/AKG_c480_c414_CUBE-AKG_c414N-minphase_False-oneside.npy diff --git a/pyroomacoustics/directivities/tests/data/AKG_c480_c414_CUBE-AKG_c414N-minphase_True-cardioid.npy b/tests/directivities/data/AKG_c480_c414_CUBE-AKG_c414N-minphase_True-cardioid.npy similarity index 100% rename from pyroomacoustics/directivities/tests/data/AKG_c480_c414_CUBE-AKG_c414N-minphase_True-cardioid.npy rename to tests/directivities/data/AKG_c480_c414_CUBE-AKG_c414N-minphase_True-cardioid.npy diff --git a/pyroomacoustics/directivities/tests/data/AKG_c480_c414_CUBE-AKG_c414N-minphase_True-oneside.npy b/tests/directivities/data/AKG_c480_c414_CUBE-AKG_c414N-minphase_True-oneside.npy similarity index 100% rename from pyroomacoustics/directivities/tests/data/AKG_c480_c414_CUBE-AKG_c414N-minphase_True-oneside.npy rename to tests/directivities/data/AKG_c480_c414_CUBE-AKG_c414N-minphase_True-oneside.npy diff --git a/pyroomacoustics/directivities/tests/data/AKG_c480_c414_CUBE-AKG_c414S-minphase_False-cardioid.npy b/tests/directivities/data/AKG_c480_c414_CUBE-AKG_c414S-minphase_False-cardioid.npy similarity index 100% rename from pyroomacoustics/directivities/tests/data/AKG_c480_c414_CUBE-AKG_c414S-minphase_False-cardioid.npy rename to tests/directivities/data/AKG_c480_c414_CUBE-AKG_c414S-minphase_False-cardioid.npy diff --git a/pyroomacoustics/directivities/tests/data/AKG_c480_c414_CUBE-AKG_c414S-minphase_False-oneside.npy b/tests/directivities/data/AKG_c480_c414_CUBE-AKG_c414S-minphase_False-oneside.npy similarity index 100% rename from pyroomacoustics/directivities/tests/data/AKG_c480_c414_CUBE-AKG_c414S-minphase_False-oneside.npy rename to tests/directivities/data/AKG_c480_c414_CUBE-AKG_c414S-minphase_False-oneside.npy diff --git a/pyroomacoustics/directivities/tests/data/AKG_c480_c414_CUBE-AKG_c414S-minphase_True-cardioid.npy b/tests/directivities/data/AKG_c480_c414_CUBE-AKG_c414S-minphase_True-cardioid.npy similarity index 100% rename from pyroomacoustics/directivities/tests/data/AKG_c480_c414_CUBE-AKG_c414S-minphase_True-cardioid.npy rename to tests/directivities/data/AKG_c480_c414_CUBE-AKG_c414S-minphase_True-cardioid.npy diff --git a/pyroomacoustics/directivities/tests/data/AKG_c480_c414_CUBE-AKG_c414S-minphase_True-oneside.npy b/tests/directivities/data/AKG_c480_c414_CUBE-AKG_c414S-minphase_True-oneside.npy similarity index 100% rename from pyroomacoustics/directivities/tests/data/AKG_c480_c414_CUBE-AKG_c414S-minphase_True-oneside.npy rename to tests/directivities/data/AKG_c480_c414_CUBE-AKG_c414S-minphase_True-oneside.npy diff --git a/pyroomacoustics/directivities/tests/data/AKG_c480_c414_CUBE-AKG_c480-minphase_False-cardioid.npy b/tests/directivities/data/AKG_c480_c414_CUBE-AKG_c480-minphase_False-cardioid.npy similarity index 100% rename from pyroomacoustics/directivities/tests/data/AKG_c480_c414_CUBE-AKG_c480-minphase_False-cardioid.npy rename to tests/directivities/data/AKG_c480_c414_CUBE-AKG_c480-minphase_False-cardioid.npy diff --git a/pyroomacoustics/directivities/tests/data/AKG_c480_c414_CUBE-AKG_c480-minphase_False-oneside.npy b/tests/directivities/data/AKG_c480_c414_CUBE-AKG_c480-minphase_False-oneside.npy similarity index 100% rename from pyroomacoustics/directivities/tests/data/AKG_c480_c414_CUBE-AKG_c480-minphase_False-oneside.npy rename to tests/directivities/data/AKG_c480_c414_CUBE-AKG_c480-minphase_False-oneside.npy diff --git a/pyroomacoustics/directivities/tests/data/AKG_c480_c414_CUBE-AKG_c480-minphase_True-cardioid.npy b/tests/directivities/data/AKG_c480_c414_CUBE-AKG_c480-minphase_True-cardioid.npy similarity index 100% rename from pyroomacoustics/directivities/tests/data/AKG_c480_c414_CUBE-AKG_c480-minphase_True-cardioid.npy rename to tests/directivities/data/AKG_c480_c414_CUBE-AKG_c480-minphase_True-cardioid.npy diff --git a/pyroomacoustics/directivities/tests/data/AKG_c480_c414_CUBE-AKG_c480-minphase_True-oneside.npy b/tests/directivities/data/AKG_c480_c414_CUBE-AKG_c480-minphase_True-oneside.npy similarity index 100% rename from pyroomacoustics/directivities/tests/data/AKG_c480_c414_CUBE-AKG_c480-minphase_True-oneside.npy rename to tests/directivities/data/AKG_c480_c414_CUBE-AKG_c480-minphase_True-oneside.npy diff --git a/pyroomacoustics/directivities/tests/data/EM32_Directivity-EM_32_0-minphase_False-cardioid.npy b/tests/directivities/data/EM32_Directivity-EM_32_0-minphase_False-cardioid.npy similarity index 100% rename from pyroomacoustics/directivities/tests/data/EM32_Directivity-EM_32_0-minphase_False-cardioid.npy rename to tests/directivities/data/EM32_Directivity-EM_32_0-minphase_False-cardioid.npy diff --git a/pyroomacoustics/directivities/tests/data/EM32_Directivity-EM_32_0-minphase_False-oneside.npy b/tests/directivities/data/EM32_Directivity-EM_32_0-minphase_False-oneside.npy similarity index 100% rename from pyroomacoustics/directivities/tests/data/EM32_Directivity-EM_32_0-minphase_False-oneside.npy rename to tests/directivities/data/EM32_Directivity-EM_32_0-minphase_False-oneside.npy diff --git a/pyroomacoustics/directivities/tests/data/EM32_Directivity-EM_32_0-minphase_True-cardioid.npy b/tests/directivities/data/EM32_Directivity-EM_32_0-minphase_True-cardioid.npy similarity index 100% rename from pyroomacoustics/directivities/tests/data/EM32_Directivity-EM_32_0-minphase_True-cardioid.npy rename to tests/directivities/data/EM32_Directivity-EM_32_0-minphase_True-cardioid.npy diff --git a/pyroomacoustics/directivities/tests/data/EM32_Directivity-EM_32_0-minphase_True-oneside.npy b/tests/directivities/data/EM32_Directivity-EM_32_0-minphase_True-oneside.npy similarity index 100% rename from pyroomacoustics/directivities/tests/data/EM32_Directivity-EM_32_0-minphase_True-oneside.npy rename to tests/directivities/data/EM32_Directivity-EM_32_0-minphase_True-oneside.npy diff --git a/pyroomacoustics/directivities/tests/data/EM32_Directivity-EM_32_31-minphase_False-cardioid.npy b/tests/directivities/data/EM32_Directivity-EM_32_31-minphase_False-cardioid.npy similarity index 100% rename from pyroomacoustics/directivities/tests/data/EM32_Directivity-EM_32_31-minphase_False-cardioid.npy rename to tests/directivities/data/EM32_Directivity-EM_32_31-minphase_False-cardioid.npy diff --git a/pyroomacoustics/directivities/tests/data/EM32_Directivity-EM_32_31-minphase_False-oneside.npy b/tests/directivities/data/EM32_Directivity-EM_32_31-minphase_False-oneside.npy similarity index 100% rename from pyroomacoustics/directivities/tests/data/EM32_Directivity-EM_32_31-minphase_False-oneside.npy rename to tests/directivities/data/EM32_Directivity-EM_32_31-minphase_False-oneside.npy diff --git a/pyroomacoustics/directivities/tests/data/EM32_Directivity-EM_32_31-minphase_True-cardioid.npy b/tests/directivities/data/EM32_Directivity-EM_32_31-minphase_True-cardioid.npy similarity index 100% rename from pyroomacoustics/directivities/tests/data/EM32_Directivity-EM_32_31-minphase_True-cardioid.npy rename to tests/directivities/data/EM32_Directivity-EM_32_31-minphase_True-cardioid.npy diff --git a/pyroomacoustics/directivities/tests/data/EM32_Directivity-EM_32_31-minphase_True-oneside.npy b/tests/directivities/data/EM32_Directivity-EM_32_31-minphase_True-oneside.npy similarity index 100% rename from pyroomacoustics/directivities/tests/data/EM32_Directivity-EM_32_31-minphase_True-oneside.npy rename to tests/directivities/data/EM32_Directivity-EM_32_31-minphase_True-oneside.npy diff --git a/pyroomacoustics/directivities/tests/data/LSPs_HATS_GuitarCabinets_Akustikmessplatz-Genelec_8020-AKG_c480_c414_CUBE-AKG_c480-minphase_False-twosides.npy b/tests/directivities/data/LSPs_HATS_GuitarCabinets_Akustikmessplatz-Genelec_8020-AKG_c480_c414_CUBE-AKG_c480-minphase_False-twosides.npy similarity index 100% rename from pyroomacoustics/directivities/tests/data/LSPs_HATS_GuitarCabinets_Akustikmessplatz-Genelec_8020-AKG_c480_c414_CUBE-AKG_c480-minphase_False-twosides.npy rename to tests/directivities/data/LSPs_HATS_GuitarCabinets_Akustikmessplatz-Genelec_8020-AKG_c480_c414_CUBE-AKG_c480-minphase_False-twosides.npy diff --git a/pyroomacoustics/directivities/tests/data/LSPs_HATS_GuitarCabinets_Akustikmessplatz-Genelec_8020-AKG_c480_c414_CUBE-AKG_c480-minphase_True-twosides.npy b/tests/directivities/data/LSPs_HATS_GuitarCabinets_Akustikmessplatz-Genelec_8020-AKG_c480_c414_CUBE-AKG_c480-minphase_True-twosides.npy similarity index 100% rename from pyroomacoustics/directivities/tests/data/LSPs_HATS_GuitarCabinets_Akustikmessplatz-Genelec_8020-AKG_c480_c414_CUBE-AKG_c480-minphase_True-twosides.npy rename to tests/directivities/data/LSPs_HATS_GuitarCabinets_Akustikmessplatz-Genelec_8020-AKG_c480_c414_CUBE-AKG_c480-minphase_True-twosides.npy diff --git a/pyroomacoustics/directivities/tests/data/LSPs_HATS_GuitarCabinets_Akustikmessplatz-Genelec_8020-EM32_Directivity-EM_32_31-minphase_False-twosides.npy b/tests/directivities/data/LSPs_HATS_GuitarCabinets_Akustikmessplatz-Genelec_8020-EM32_Directivity-EM_32_31-minphase_False-twosides.npy similarity index 100% rename from pyroomacoustics/directivities/tests/data/LSPs_HATS_GuitarCabinets_Akustikmessplatz-Genelec_8020-EM32_Directivity-EM_32_31-minphase_False-twosides.npy rename to tests/directivities/data/LSPs_HATS_GuitarCabinets_Akustikmessplatz-Genelec_8020-EM32_Directivity-EM_32_31-minphase_False-twosides.npy diff --git a/pyroomacoustics/directivities/tests/data/LSPs_HATS_GuitarCabinets_Akustikmessplatz-Genelec_8020-EM32_Directivity-EM_32_31-minphase_True-twosides.npy b/tests/directivities/data/LSPs_HATS_GuitarCabinets_Akustikmessplatz-Genelec_8020-EM32_Directivity-EM_32_31-minphase_True-twosides.npy similarity index 100% rename from pyroomacoustics/directivities/tests/data/LSPs_HATS_GuitarCabinets_Akustikmessplatz-Genelec_8020-EM32_Directivity-EM_32_31-minphase_True-twosides.npy rename to tests/directivities/data/LSPs_HATS_GuitarCabinets_Akustikmessplatz-Genelec_8020-EM32_Directivity-EM_32_31-minphase_True-twosides.npy diff --git a/pyroomacoustics/directivities/tests/data/LSPs_HATS_GuitarCabinets_Akustikmessplatz-Genelec_8020-minphase_False-cardioid.npy b/tests/directivities/data/LSPs_HATS_GuitarCabinets_Akustikmessplatz-Genelec_8020-minphase_False-cardioid.npy similarity index 100% rename from pyroomacoustics/directivities/tests/data/LSPs_HATS_GuitarCabinets_Akustikmessplatz-Genelec_8020-minphase_False-cardioid.npy rename to tests/directivities/data/LSPs_HATS_GuitarCabinets_Akustikmessplatz-Genelec_8020-minphase_False-cardioid.npy diff --git a/pyroomacoustics/directivities/tests/data/LSPs_HATS_GuitarCabinets_Akustikmessplatz-Genelec_8020-minphase_False-oneside.npy b/tests/directivities/data/LSPs_HATS_GuitarCabinets_Akustikmessplatz-Genelec_8020-minphase_False-oneside.npy similarity index 100% rename from pyroomacoustics/directivities/tests/data/LSPs_HATS_GuitarCabinets_Akustikmessplatz-Genelec_8020-minphase_False-oneside.npy rename to tests/directivities/data/LSPs_HATS_GuitarCabinets_Akustikmessplatz-Genelec_8020-minphase_False-oneside.npy diff --git a/pyroomacoustics/directivities/tests/data/LSPs_HATS_GuitarCabinets_Akustikmessplatz-Genelec_8020-minphase_True-cardioid.npy b/tests/directivities/data/LSPs_HATS_GuitarCabinets_Akustikmessplatz-Genelec_8020-minphase_True-cardioid.npy similarity index 100% rename from pyroomacoustics/directivities/tests/data/LSPs_HATS_GuitarCabinets_Akustikmessplatz-Genelec_8020-minphase_True-cardioid.npy rename to tests/directivities/data/LSPs_HATS_GuitarCabinets_Akustikmessplatz-Genelec_8020-minphase_True-cardioid.npy diff --git a/pyroomacoustics/directivities/tests/data/LSPs_HATS_GuitarCabinets_Akustikmessplatz-Genelec_8020-minphase_True-oneside.npy b/tests/directivities/data/LSPs_HATS_GuitarCabinets_Akustikmessplatz-Genelec_8020-minphase_True-oneside.npy similarity index 100% rename from pyroomacoustics/directivities/tests/data/LSPs_HATS_GuitarCabinets_Akustikmessplatz-Genelec_8020-minphase_True-oneside.npy rename to tests/directivities/data/LSPs_HATS_GuitarCabinets_Akustikmessplatz-Genelec_8020-minphase_True-oneside.npy diff --git a/pyroomacoustics/directivities/tests/data/LSPs_HATS_GuitarCabinets_Akustikmessplatz-Vibrolux_2x10inch-AKG_c480_c414_CUBE-AKG_c414K-minphase_False-twosides.npy b/tests/directivities/data/LSPs_HATS_GuitarCabinets_Akustikmessplatz-Vibrolux_2x10inch-AKG_c480_c414_CUBE-AKG_c414K-minphase_False-twosides.npy similarity index 100% rename from pyroomacoustics/directivities/tests/data/LSPs_HATS_GuitarCabinets_Akustikmessplatz-Vibrolux_2x10inch-AKG_c480_c414_CUBE-AKG_c414K-minphase_False-twosides.npy rename to tests/directivities/data/LSPs_HATS_GuitarCabinets_Akustikmessplatz-Vibrolux_2x10inch-AKG_c480_c414_CUBE-AKG_c414K-minphase_False-twosides.npy diff --git a/pyroomacoustics/directivities/tests/data/LSPs_HATS_GuitarCabinets_Akustikmessplatz-Vibrolux_2x10inch-AKG_c480_c414_CUBE-AKG_c414K-minphase_True-twosides.npy b/tests/directivities/data/LSPs_HATS_GuitarCabinets_Akustikmessplatz-Vibrolux_2x10inch-AKG_c480_c414_CUBE-AKG_c414K-minphase_True-twosides.npy similarity index 100% rename from pyroomacoustics/directivities/tests/data/LSPs_HATS_GuitarCabinets_Akustikmessplatz-Vibrolux_2x10inch-AKG_c480_c414_CUBE-AKG_c414K-minphase_True-twosides.npy rename to tests/directivities/data/LSPs_HATS_GuitarCabinets_Akustikmessplatz-Vibrolux_2x10inch-AKG_c480_c414_CUBE-AKG_c414K-minphase_True-twosides.npy diff --git a/pyroomacoustics/directivities/tests/data/LSPs_HATS_GuitarCabinets_Akustikmessplatz-Vibrolux_2x10inch-EM32_Directivity-EM_32_0-minphase_False-twosides.npy b/tests/directivities/data/LSPs_HATS_GuitarCabinets_Akustikmessplatz-Vibrolux_2x10inch-EM32_Directivity-EM_32_0-minphase_False-twosides.npy similarity index 100% rename from pyroomacoustics/directivities/tests/data/LSPs_HATS_GuitarCabinets_Akustikmessplatz-Vibrolux_2x10inch-EM32_Directivity-EM_32_0-minphase_False-twosides.npy rename to tests/directivities/data/LSPs_HATS_GuitarCabinets_Akustikmessplatz-Vibrolux_2x10inch-EM32_Directivity-EM_32_0-minphase_False-twosides.npy diff --git a/pyroomacoustics/directivities/tests/data/LSPs_HATS_GuitarCabinets_Akustikmessplatz-Vibrolux_2x10inch-EM32_Directivity-EM_32_0-minphase_True-twosides.npy b/tests/directivities/data/LSPs_HATS_GuitarCabinets_Akustikmessplatz-Vibrolux_2x10inch-EM32_Directivity-EM_32_0-minphase_True-twosides.npy similarity index 100% rename from pyroomacoustics/directivities/tests/data/LSPs_HATS_GuitarCabinets_Akustikmessplatz-Vibrolux_2x10inch-EM32_Directivity-EM_32_0-minphase_True-twosides.npy rename to tests/directivities/data/LSPs_HATS_GuitarCabinets_Akustikmessplatz-Vibrolux_2x10inch-EM32_Directivity-EM_32_0-minphase_True-twosides.npy diff --git a/pyroomacoustics/directivities/tests/data/LSPs_HATS_GuitarCabinets_Akustikmessplatz-Vibrolux_2x10inch-minphase_False-cardioid.npy b/tests/directivities/data/LSPs_HATS_GuitarCabinets_Akustikmessplatz-Vibrolux_2x10inch-minphase_False-cardioid.npy similarity index 100% rename from pyroomacoustics/directivities/tests/data/LSPs_HATS_GuitarCabinets_Akustikmessplatz-Vibrolux_2x10inch-minphase_False-cardioid.npy rename to tests/directivities/data/LSPs_HATS_GuitarCabinets_Akustikmessplatz-Vibrolux_2x10inch-minphase_False-cardioid.npy diff --git a/pyroomacoustics/directivities/tests/data/LSPs_HATS_GuitarCabinets_Akustikmessplatz-Vibrolux_2x10inch-minphase_False-oneside.npy b/tests/directivities/data/LSPs_HATS_GuitarCabinets_Akustikmessplatz-Vibrolux_2x10inch-minphase_False-oneside.npy similarity index 100% rename from pyroomacoustics/directivities/tests/data/LSPs_HATS_GuitarCabinets_Akustikmessplatz-Vibrolux_2x10inch-minphase_False-oneside.npy rename to tests/directivities/data/LSPs_HATS_GuitarCabinets_Akustikmessplatz-Vibrolux_2x10inch-minphase_False-oneside.npy diff --git a/pyroomacoustics/directivities/tests/data/LSPs_HATS_GuitarCabinets_Akustikmessplatz-Vibrolux_2x10inch-minphase_True-cardioid.npy b/tests/directivities/data/LSPs_HATS_GuitarCabinets_Akustikmessplatz-Vibrolux_2x10inch-minphase_True-cardioid.npy similarity index 100% rename from pyroomacoustics/directivities/tests/data/LSPs_HATS_GuitarCabinets_Akustikmessplatz-Vibrolux_2x10inch-minphase_True-cardioid.npy rename to tests/directivities/data/LSPs_HATS_GuitarCabinets_Akustikmessplatz-Vibrolux_2x10inch-minphase_True-cardioid.npy diff --git a/pyroomacoustics/directivities/tests/data/LSPs_HATS_GuitarCabinets_Akustikmessplatz-Vibrolux_2x10inch-minphase_True-oneside.npy b/tests/directivities/data/LSPs_HATS_GuitarCabinets_Akustikmessplatz-Vibrolux_2x10inch-minphase_True-oneside.npy similarity index 100% rename from pyroomacoustics/directivities/tests/data/LSPs_HATS_GuitarCabinets_Akustikmessplatz-Vibrolux_2x10inch-minphase_True-oneside.npy rename to tests/directivities/data/LSPs_HATS_GuitarCabinets_Akustikmessplatz-Vibrolux_2x10inch-minphase_True-oneside.npy diff --git a/pyroomacoustics/directivities/tests/test_cardioid_energy.py b/tests/directivities/test_cardioid_energy.py similarity index 100% rename from pyroomacoustics/directivities/tests/test_cardioid_energy.py rename to tests/directivities/test_cardioid_energy.py diff --git a/pyroomacoustics/directivities/tests/test_cardioid_energy_distribution.py b/tests/directivities/test_cardioid_energy_distribution.py similarity index 100% rename from pyroomacoustics/directivities/tests/test_cardioid_energy_distribution.py rename to tests/directivities/test_cardioid_energy_distribution.py diff --git a/pyroomacoustics/directivities/tests/test_directive_energy_rt_vs_ism.py b/tests/directivities/test_directive_energy_rt_vs_ism.py similarity index 100% rename from pyroomacoustics/directivities/tests/test_directive_energy_rt_vs_ism.py rename to tests/directivities/test_directive_energy_rt_vs_ism.py diff --git a/pyroomacoustics/directivities/tests/test_harmonics_directivities.py b/tests/directivities/test_harmonics_directivities.py similarity index 100% rename from pyroomacoustics/directivities/tests/test_harmonics_directivities.py rename to tests/directivities/test_harmonics_directivities.py diff --git a/pyroomacoustics/directivities/tests/test_measured_energy_distribution.py b/tests/directivities/test_measured_energy_distribution.py similarity index 100% rename from pyroomacoustics/directivities/tests/test_measured_energy_distribution.py rename to tests/directivities/test_measured_energy_distribution.py diff --git a/pyroomacoustics/directivities/tests/test_omni_global_delay.py b/tests/directivities/test_omni_global_delay.py similarity index 100% rename from pyroomacoustics/directivities/tests/test_omni_global_delay.py rename to tests/directivities/test_omni_global_delay.py diff --git a/pyroomacoustics/directivities/tests/test_sofa_directivities.py b/tests/directivities/test_sofa_directivities.py similarity index 100% rename from pyroomacoustics/directivities/tests/test_sofa_directivities.py rename to tests/directivities/test_sofa_directivities.py diff --git a/pyroomacoustics/directivities/tests/test_source_directivities.py b/tests/directivities/test_source_directivities.py similarity index 100% rename from pyroomacoustics/directivities/tests/test_source_directivities.py rename to tests/directivities/test_source_directivities.py diff --git a/pyroomacoustics/directivities/tests/test_source_directivities_nonshoebox.py b/tests/directivities/test_source_directivities_nonshoebox.py similarity index 100% rename from pyroomacoustics/directivities/tests/test_source_directivities_nonshoebox.py rename to tests/directivities/test_source_directivities_nonshoebox.py diff --git a/pyroomacoustics/directivities/tests/test_source_directivity_flipping.py b/tests/directivities/test_source_directivity_flipping.py similarity index 100% rename from pyroomacoustics/directivities/tests/test_source_directivity_flipping.py rename to tests/directivities/test_source_directivity_flipping.py diff --git a/pyroomacoustics/doa/tests/test_doa.py b/tests/doa/test_doa.py similarity index 100% rename from pyroomacoustics/doa/tests/test_doa.py rename to tests/doa/test_doa.py diff --git a/pyroomacoustics/doa/tests/test_grid.py b/tests/doa/test_grid.py similarity index 100% rename from pyroomacoustics/doa/tests/test_grid.py rename to tests/doa/test_grid.py diff --git a/pyroomacoustics/doa/tests/test_utils.py b/tests/doa/test_utils.py similarity index 100% rename from pyroomacoustics/doa/tests/test_utils.py rename to tests/doa/test_utils.py diff --git a/pyroomacoustics/experimental/tests/test_deconvolution.py b/tests/experimental/test_deconvolution.py similarity index 100% rename from pyroomacoustics/experimental/tests/test_deconvolution.py rename to tests/experimental/test_deconvolution.py diff --git a/pyroomacoustics/experimental/tests/test_measure_rt60.py b/tests/experimental/test_measure_rt60.py similarity index 100% rename from pyroomacoustics/experimental/tests/test_measure_rt60.py rename to tests/experimental/test_measure_rt60.py diff --git a/pyroomacoustics/tests/tests_libroom/test_ccw3p.py b/tests/libroom/test_ccw3p.py similarity index 100% rename from pyroomacoustics/tests/tests_libroom/test_ccw3p.py rename to tests/libroom/test_ccw3p.py diff --git a/pyroomacoustics/tests/tests_libroom/test_geometry_routines.py b/tests/libroom/test_geometry_routines.py similarity index 100% rename from pyroomacoustics/tests/tests_libroom/test_geometry_routines.py rename to tests/libroom/test_geometry_routines.py diff --git a/pyroomacoustics/tests/tests_libroom/test_is_inside_2d_polygon.py b/tests/libroom/test_is_inside_2d_polygon.py similarity index 100% rename from pyroomacoustics/tests/tests_libroom/test_is_inside_2d_polygon.py rename to tests/libroom/test_is_inside_2d_polygon.py diff --git a/pyroomacoustics/tests/tests_libroom/test_microphone.py b/tests/libroom/test_microphone.py similarity index 100% rename from pyroomacoustics/tests/tests_libroom/test_microphone.py rename to tests/libroom/test_microphone.py diff --git a/pyroomacoustics/tests/tests_libroom/test_ray_energy.py b/tests/libroom/test_ray_energy.py similarity index 100% rename from pyroomacoustics/tests/tests_libroom/test_ray_energy.py rename to tests/libroom/test_ray_energy.py diff --git a/pyroomacoustics/tests/tests_libroom/test_room_broadcast_n_bands.py b/tests/libroom/test_room_broadcast_n_bands.py similarity index 100% rename from pyroomacoustics/tests/tests_libroom/test_room_broadcast_n_bands.py rename to tests/libroom/test_room_broadcast_n_bands.py diff --git a/pyroomacoustics/tests/tests_libroom/test_room_construct.py b/tests/libroom/test_room_construct.py similarity index 100% rename from pyroomacoustics/tests/tests_libroom/test_room_construct.py rename to tests/libroom/test_room_construct.py diff --git a/pyroomacoustics/tests/tests_libroom/test_room_walls.py b/tests/libroom/test_room_walls.py similarity index 100% rename from pyroomacoustics/tests/tests_libroom/test_room_walls.py rename to tests/libroom/test_room_walls.py diff --git a/pyroomacoustics/tests/tests_libroom/test_sample_lambertian_reflection.py b/tests/libroom/test_sample_lambertian_reflection.py similarity index 100% rename from pyroomacoustics/tests/tests_libroom/test_sample_lambertian_reflection.py rename to tests/libroom/test_sample_lambertian_reflection.py diff --git a/pyroomacoustics/tests/tests_libroom/test_shoebox_simple.py b/tests/libroom/test_shoebox_simple.py similarity index 100% rename from pyroomacoustics/tests/tests_libroom/test_shoebox_simple.py rename to tests/libroom/test_shoebox_simple.py diff --git a/pyroomacoustics/tests/tests_libroom/test_wall_construct.py b/tests/libroom/test_wall_construct.py similarity index 100% rename from pyroomacoustics/tests/tests_libroom/test_wall_construct.py rename to tests/libroom/test_wall_construct.py diff --git a/pyroomacoustics/tests/tests_libroom/test_wall_intersection.py b/tests/libroom/test_wall_intersection.py similarity index 100% rename from pyroomacoustics/tests/tests_libroom/test_wall_intersection.py rename to tests/libroom/test_wall_intersection.py diff --git a/pyroomacoustics/tests/tests_libroom/test_wall_side_reflect.py b/tests/libroom/test_wall_side_reflect.py similarity index 100% rename from pyroomacoustics/tests/tests_libroom/test_wall_side_reflect.py rename to tests/libroom/test_wall_side_reflect.py diff --git a/pyroomacoustics/phase/tests/test_gl.py b/tests/phase/test_gl.py similarity index 94% rename from pyroomacoustics/phase/tests/test_gl.py rename to tests/phase/test_gl.py index 91d77699..039b5c70 100644 --- a/pyroomacoustics/phase/tests/test_gl.py +++ b/tests/phase/test_gl.py @@ -7,6 +7,7 @@ """ import unittest +from pathlib import Path import numpy as np from scipy.io import wavfile @@ -15,7 +16,9 @@ test_tol = 1e-2 -filename = "examples/input_samples/cmu_arctic_us_axb_a0004.wav" +filename = ( + Path(__file__).parent / "../../examples/input_samples/cmu_arctic_us_axb_a0004.wav" +) fs, audio = wavfile.read(filename) fft_size = 512 diff --git a/pyroomacoustics/random/tests/test_distributions.py b/tests/random/test_distributions.py similarity index 100% rename from pyroomacoustics/random/tests/test_distributions.py rename to tests/random/test_distributions.py diff --git a/pyroomacoustics/random/tests/test_rejection.py b/tests/random/test_rejection.py similarity index 100% rename from pyroomacoustics/random/tests/test_rejection.py rename to tests/random/test_rejection.py diff --git a/pyroomacoustics/random/tests/test_spherical.py b/tests/random/test_spherical.py similarity index 100% rename from pyroomacoustics/random/tests/test_spherical.py rename to tests/random/test_spherical.py diff --git a/pyroomacoustics/tests/test_air_absorption.py b/tests/test_air_absorption.py similarity index 100% rename from pyroomacoustics/tests/test_air_absorption.py rename to tests/test_air_absorption.py diff --git a/pyroomacoustics/tests/test_anechoic_room.py b/tests/test_anechoic_room.py similarity index 100% rename from pyroomacoustics/tests/test_anechoic_room.py rename to tests/test_anechoic_room.py diff --git a/pyroomacoustics/tests/test_angle_function.py b/tests/test_angle_function.py similarity index 100% rename from pyroomacoustics/tests/test_angle_function.py rename to tests/test_angle_function.py diff --git a/pyroomacoustics/tests/test_autocorr.py b/tests/test_autocorr.py similarity index 100% rename from pyroomacoustics/tests/test_autocorr.py rename to tests/test_autocorr.py diff --git a/pyroomacoustics/tests/test_bandpass_filterbank.py b/tests/test_bandpass_filterbank.py similarity index 100% rename from pyroomacoustics/tests/test_bandpass_filterbank.py rename to tests/test_bandpass_filterbank.py diff --git a/pyroomacoustics/tests/test_build_rir.py b/tests/test_build_rir.py similarity index 100% rename from pyroomacoustics/tests/test_build_rir.py rename to tests/test_build_rir.py diff --git a/pyroomacoustics/tests/test_create_noisy_signal.py b/tests/test_create_noisy_signal.py similarity index 99% rename from pyroomacoustics/tests/test_create_noisy_signal.py rename to tests/test_create_noisy_signal.py index 62da9972..f921bd8c 100644 --- a/pyroomacoustics/tests/test_create_noisy_signal.py +++ b/tests/test_create_noisy_signal.py @@ -13,7 +13,6 @@ signal_fp = os.path.join( os.path.dirname(__file__), "..", - "..", "examples", "input_samples", "cmu_arctic_us_aew_a0001.wav", diff --git a/pyroomacoustics/tests/test_from_corners_extrude.py b/tests/test_from_corners_extrude.py similarity index 100% rename from pyroomacoustics/tests/test_from_corners_extrude.py rename to tests/test_from_corners_extrude.py diff --git a/pyroomacoustics/tests/test_hpf.py b/tests/test_hpf.py similarity index 100% rename from pyroomacoustics/tests/test_hpf.py rename to tests/test_hpf.py diff --git a/pyroomacoustics/tests/test_issue_115.py b/tests/test_issue_115.py similarity index 100% rename from pyroomacoustics/tests/test_issue_115.py rename to tests/test_issue_115.py diff --git a/pyroomacoustics/tests/test_issue_162.py b/tests/test_issue_162.py similarity index 100% rename from pyroomacoustics/tests/test_issue_162.py rename to tests/test_issue_162.py diff --git a/pyroomacoustics/tests/test_issue_22.py b/tests/test_issue_22.py similarity index 100% rename from pyroomacoustics/tests/test_issue_22.py rename to tests/test_issue_22.py diff --git a/pyroomacoustics/tests/test_issue_235.py b/tests/test_issue_235.py similarity index 100% rename from pyroomacoustics/tests/test_issue_235.py rename to tests/test_issue_235.py diff --git a/pyroomacoustics/tests/test_issue_236.py b/tests/test_issue_236.py similarity index 100% rename from pyroomacoustics/tests/test_issue_236.py rename to tests/test_issue_236.py diff --git a/pyroomacoustics/tests/test_issue_313.py b/tests/test_issue_313.py similarity index 100% rename from pyroomacoustics/tests/test_issue_313.py rename to tests/test_issue_313.py diff --git a/pyroomacoustics/tests/test_issue_348.py b/tests/test_issue_348.py similarity index 100% rename from pyroomacoustics/tests/test_issue_348.py rename to tests/test_issue_348.py diff --git a/pyroomacoustics/tests/test_issue_353.py b/tests/test_issue_353.py similarity index 100% rename from pyroomacoustics/tests/test_issue_353.py rename to tests/test_issue_353.py diff --git a/pyroomacoustics/tests/test_issue_69.py b/tests/test_issue_69.py similarity index 100% rename from pyroomacoustics/tests/test_issue_69.py rename to tests/test_issue_69.py diff --git a/pyroomacoustics/tests/test_issue_87.py b/tests/test_issue_87.py similarity index 100% rename from pyroomacoustics/tests/test_issue_87.py rename to tests/test_issue_87.py diff --git a/pyroomacoustics/tests/test_materials.py b/tests/test_materials.py similarity index 100% rename from pyroomacoustics/tests/test_materials.py rename to tests/test_materials.py diff --git a/pyroomacoustics/tests/test_metrics.py b/tests/test_metrics.py similarity index 100% rename from pyroomacoustics/tests/test_metrics.py rename to tests/test_metrics.py diff --git a/pyroomacoustics/tests/test_microphone_array.py b/tests/test_microphone_array.py similarity index 100% rename from pyroomacoustics/tests/test_microphone_array.py rename to tests/test_microphone_array.py diff --git a/pyroomacoustics/tests/test_octave_bands.py b/tests/test_octave_bands.py similarity index 100% rename from pyroomacoustics/tests/test_octave_bands.py rename to tests/test_octave_bands.py diff --git a/pyroomacoustics/tests/test_rake_filters.py b/tests/test_rake_filters.py similarity index 100% rename from pyroomacoustics/tests/test_rake_filters.py rename to tests/test_rake_filters.py diff --git a/pyroomacoustics/tests/test_random_ism.py b/tests/test_random_ism.py similarity index 100% rename from pyroomacoustics/tests/test_random_ism.py rename to tests/test_random_ism.py diff --git a/pyroomacoustics/tests/test_resample.py b/tests/test_resample.py similarity index 100% rename from pyroomacoustics/tests/test_resample.py rename to tests/test_resample.py diff --git a/pyroomacoustics/tests/test_rir_hpf.py b/tests/test_rir_hpf.py similarity index 100% rename from pyroomacoustics/tests/test_rir_hpf.py rename to tests/test_rir_hpf.py diff --git a/pyroomacoustics/tests/test_room_add.py b/tests/test_room_add.py similarity index 100% rename from pyroomacoustics/tests/test_room_add.py rename to tests/test_room_add.py diff --git a/pyroomacoustics/tests/test_room_is_insided.py b/tests/test_room_is_insided.py similarity index 100% rename from pyroomacoustics/tests/test_room_is_insided.py rename to tests/test_room_is_insided.py diff --git a/pyroomacoustics/tests/test_room_mix.py b/tests/test_room_mix.py similarity index 100% rename from pyroomacoustics/tests/test_room_mix.py rename to tests/test_room_mix.py diff --git a/pyroomacoustics/tests/test_room_plot.py b/tests/test_room_plot.py similarity index 100% rename from pyroomacoustics/tests/test_room_plot.py rename to tests/test_room_plot.py diff --git a/pyroomacoustics/tests/test_room_scattering_rt60.py b/tests/test_room_scattering_rt60.py similarity index 100% rename from pyroomacoustics/tests/test_room_scattering_rt60.py rename to tests/test_room_scattering_rt60.py diff --git a/pyroomacoustics/tests/test_rt60.py b/tests/test_rt60.py similarity index 100% rename from pyroomacoustics/tests/test_rt60.py rename to tests/test_rt60.py diff --git a/pyroomacoustics/tests/test_rt_multiband_energy.py b/tests/test_rt_multiband_energy.py similarity index 100% rename from pyroomacoustics/tests/test_rt_multiband_energy.py rename to tests/test_rt_multiband_energy.py diff --git a/pyroomacoustics/tests/test_shoebox_constants.py b/tests/test_shoebox_constants.py similarity index 100% rename from pyroomacoustics/tests/test_shoebox_constants.py rename to tests/test_shoebox_constants.py diff --git a/pyroomacoustics/tests/test_soundsource.py b/tests/test_soundsource.py similarity index 100% rename from pyroomacoustics/tests/test_soundsource.py rename to tests/test_soundsource.py diff --git a/pyroomacoustics/tests/test_sync.py b/tests/test_sync.py similarity index 100% rename from pyroomacoustics/tests/test_sync.py rename to tests/test_sync.py diff --git a/pyroomacoustics/tests/test_visibility_shoebox2d.py b/tests/test_visibility_shoebox2d.py similarity index 100% rename from pyroomacoustics/tests/test_visibility_shoebox2d.py rename to tests/test_visibility_shoebox2d.py diff --git a/pyroomacoustics/tests/test_visibility_shoebox3d.py b/tests/test_visibility_shoebox3d.py similarity index 100% rename from pyroomacoustics/tests/test_visibility_shoebox3d.py rename to tests/test_visibility_shoebox3d.py diff --git a/pyroomacoustics/tests/test_visibility_uroom.py b/tests/test_visibility_uroom.py similarity index 100% rename from pyroomacoustics/tests/test_visibility_uroom.py rename to tests/test_visibility_uroom.py diff --git a/pyroomacoustics/tests/test_volume_area.py b/tests/test_volume_area.py similarity index 100% rename from pyroomacoustics/tests/test_volume_area.py rename to tests/test_volume_area.py diff --git a/pyroomacoustics/transform/tests/test_dft.py b/tests/transform/test_dft.py similarity index 100% rename from pyroomacoustics/transform/tests/test_dft.py rename to tests/transform/test_dft.py diff --git a/pyroomacoustics/transform/tests/test_dft_timing.py b/tests/transform/test_dft_timing.py similarity index 100% rename from pyroomacoustics/transform/tests/test_dft_timing.py rename to tests/transform/test_dft_timing.py diff --git a/pyroomacoustics/transform/tests/test_stft.py b/tests/transform/test_stft.py similarity index 100% rename from pyroomacoustics/transform/tests/test_stft.py rename to tests/transform/test_stft.py diff --git a/pyroomacoustics/transform/tests/test_stft_oneshot.py b/tests/transform/test_stft_oneshot.py similarity index 100% rename from pyroomacoustics/transform/tests/test_stft_oneshot.py rename to tests/transform/test_stft_oneshot.py diff --git a/pyroomacoustics/transform/tests/test_stft_timing.py b/tests/transform/test_stft_timing.py similarity index 100% rename from pyroomacoustics/transform/tests/test_stft_timing.py rename to tests/transform/test_stft_timing.py From d651b348892134fcfc369da65c84e2cb3c4d72c2 Mon Sep 17 00:00:00 2001 From: Robin Scheibler Date: Fri, 3 Apr 2026 23:25:22 +0900 Subject: [PATCH 6/9] Updates the CI to use cibuildwheel to build wheels for different architectures. --- .github/workflows/pythonpackage.yml | 196 ++++++++++++++++------------ setup.py | 134 +++++++++++++++---- 2 files changed, 223 insertions(+), 107 deletions(-) diff --git a/.github/workflows/pythonpackage.yml b/.github/workflows/pythonpackage.yml index c6e8e08b..e43c8574 100644 --- a/.github/workflows/pythonpackage.yml +++ b/.github/workflows/pythonpackage.yml @@ -1,102 +1,136 @@ -name: pyroomacoustics +name: samplerate -on: [push, pull_request] +on: + push: + branches: [master] + tags: ["v*", "test-*"] + pull_request: + types: [opened, synchronize, reopened, labeled] + workflow_dispatch: jobs: - build: + build_sdist: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v6 + with: + submodules: recursive + fetch-depth: 0 + - name: Set up Python + uses: actions/setup-python@v6 + with: + python-version: "3.9" + - name: Build sdist + run: | + python -m pip install --upgrade pip + pip install -U setuptools setuptools_scm build twine + python -m build --sdist + twine check dist/* + - uses: actions/upload-artifact@v6 + with: + name: sdist + path: dist/*.tar.gz + build_wheels_pr: + if: github.event_name == 'pull_request' && !contains(github.event.pull_request.labels.*.name, 'full-build') runs-on: ${{ matrix.os }} strategy: fail-fast: false - max-parallel: 12 matrix: - # switch temporarily to windows-2019 due to a breaking - # change making mutex constexpr - # a crash occurs if python and the extension are built - # with different versions - # over time, this should hopefully resolves itself - # ref: https://github.com/microsoft/STL/issues/4875 - os: [ubuntu-latest, macos-15-intel, macos-latest, windows-latest] - python-version: [3.9, "3.10", "3.11", "3.12", "3.13", "3.14"] - exclude: - - os: macos-latest - python-version: 3.8 - - os: macos-latest - python-version: 3.9 + include: + - os: ubuntu-latest + cibw_build: "cp314-manylinux_x86_64" + cibw_archs: "x86_64" + - os: macos-latest + cibw_build: "cp314-macosx_universal2" + cibw_archs: "universal2" + - os: windows-latest + cibw_build: "cp314-win_amd64" + cibw_archs: "AMD64" steps: - - uses: actions/checkout@v4 - # ➡️ CRITICAL FIX: Set a short temporary directory - - name: Set short temp path for Windows build - if: matrix.os == 'windows-latest' - shell: powershell - run: | - $short_temp = "C:\T" - mkdir $short_temp -Force - echo "TEMP=$short_temp" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append - echo "TMP=$short_temp" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append - - name: Checkout submodules - shell: bash - run: | - auth_header="$(git config --local --get http.https://github.com/.extraheader)" - git submodule sync --recursive - git -c "http.extraheader=$auth_header" -c protocol.version=2 submodule update --init --force --recursive --depth=1 - - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v4 + - uses: actions/checkout@v6 with: - python-version: ${{ matrix.python-version }} - - name: Upgrade pip - run: | - python -m pip install --upgrade pip - - name: Workaround for windows and python 3.8 - if: matrix.os == 'windows-latest' && matrix.python-version == 3.8 - run: | - pip install netCDF4<=1.5.8 - # There is no binary package of netCF4>=1.6.0 for windows and python 3.7 - # the largest supported version is 1.5.8 - - name: Install dependencies - run: | - pip install -r requirements.txt - - name: Build package - run: | - python -m pip install -e . - - name: Test with pytest - run: | - pip install -U pytest setuptools build wheel twine - pytest - - name: Test the universal wheels - if: matrix.os == 'ubuntu-latest' - run: | - python -m build --sdist - twine check dist/* - - name: Test the binary wheels - if: matrix.os != 'ubuntu-latest' - run: | - python -m build --wheel - twine check dist/* - - name: Publish sdist to pypi - if: github.event_name == 'push' && startsWith(github.event.ref, 'refs/tags/v') && matrix.os == 'ubuntu-latest' + submodules: recursive + fetch-depth: 0 + - uses: pypa/cibuildwheel@v3.4.0 env: - TWINE_USERNAME: ${{ secrets.PYPI_USERNAME }} - TWINE_PASSWORD: ${{ secrets.PYPI_PASSWORD }} + CIBW_BUILD: ${{ matrix.cibw_build }} + CIBW_ARCHS: ${{ matrix.cibw_archs }} + CIBW_MANYLINUX_X86_64_IMAGE: manylinux_2_28 + CIBW_TEST_REQUIRES: "pytest numpy scipy matplotlib python-sofa soxr" + CIBW_TEST_COMMAND: "pytest {project}/tests" + + build_wheels: + if: >- + github.event_name == 'push' || + github.event_name == 'workflow_dispatch' || + (github.event_name == 'pull_request' && contains(github.event.pull_request.labels.*.name, 'full-build')) + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + os: [ubuntu-latest, macos-latest, windows-latest] + steps: + - uses: actions/checkout@v6 + with: + submodules: recursive + fetch-depth: 0 + - name: Set up QEMU + if: runner.os == 'Linux' + uses: docker/setup-qemu-action@v3 + with: + platforms: all + - uses: pypa/cibuildwheel@v3.4.0 + env: + CIBW_BUILD: "cp39-* cp310-* cp311-* cp312-* cp313-* cp314-*" + CIBW_SKIP: "*-musllinux_* cp39-*aarch64 cp310-*aarch64" + CIBW_ARCHS_LINUX: "x86_64 aarch64" + CIBW_ARCHS_MACOS: "universal2" + CIBW_ARCHS_WINDOWS: "AMD64" + CIBW_MANYLINUX_X86_64_IMAGE: manylinux_2_28 + CIBW_MANYLINUX_AARCH64_IMAGE: manylinux_2_28 + CIBW_TEST_REQUIRES: "pytest numpy scipy matplotlib python-sofa soxr" + CIBW_TEST_COMMAND: "pytest {project}/tests" + CIBW_TEST_SKIP: "cp314-*" + - uses: actions/upload-artifact@v6 + with: + name: wheels-${{ matrix.os }} + path: wheelhouse/*.whl + + publish: + needs: [build_sdist, build_wheels] + runs-on: ubuntu-latest + if: github.event_name == 'push' && startsWith(github.event.ref, 'refs/tags/v') + steps: + - uses: actions/download-artifact@v8 + with: + path: dist + merge-multiple: true + - name: Validate wheels run: | - twine upload --skip-existing dist/* - - name: Publish bdist to pypi - if: github.event_name == 'push' && startsWith(github.event.ref, 'refs/tags/v') && matrix.os != 'ubuntu-latest' + pip install --upgrade twine packaging + twine check dist/* + - name: Publish to PyPI env: TWINE_USERNAME: ${{ secrets.PYPI_USERNAME }} TWINE_PASSWORD: ${{ secrets.PYPI_PASSWORD }} run: | twine upload --skip-existing dist/* - - name: Publish sdist to pypi-test - if: github.event_name == 'push' && startsWith(github.event.ref, 'refs/tags/test-') && matrix.os == 'ubuntu-latest' - env: - TWINE_USERNAME: ${{ secrets.PYPITEST_USERNAME }} - TWINE_PASSWORD: ${{ secrets.PYPITEST_PASSWORD }} - TWINE_REPOSITORY_URL: https://test.pypi.org/legacy/ + + publish-test: + needs: [build_sdist, build_wheels] + runs-on: ubuntu-latest + if: github.event_name == 'push' && startsWith(github.event.ref, 'refs/tags/test-') + steps: + - uses: actions/download-artifact@v8 + with: + path: dist + merge-multiple: true + - name: Validate wheels run: | - twine upload --skip-existing dist/* - - name: Publish bdist to pypi-test - if: github.event_name == 'push' && startsWith(github.event.ref, 'refs/tags/test-') && matrix.os != 'ubuntu-latest' + pip install --upgrade twine packaging + twine check dist/* + - name: Publish to PyPI Test env: TWINE_USERNAME: ${{ secrets.PYPITEST_USERNAME }} TWINE_PASSWORD: ${{ secrets.PYPITEST_PASSWORD }} diff --git a/setup.py b/setup.py index 43abc039..71a26dfc 100644 --- a/setup.py +++ b/setup.py @@ -1,5 +1,9 @@ +# Compile and install the python-samplerate package +# +# Script based on the cmake_example of pybind11 by Dean Moldovan +# https://github.com/pybind/cmake_example + import os -import platform import subprocess import sys from pathlib import Path @@ -7,7 +11,18 @@ from setuptools import Extension, setup from setuptools.command.build_ext import build_ext +# Convert distutils Windows platform specifiers to CMake -A arguments +PLAT_TO_CMAKE = { + "win32": "Win32", + "win-amd64": "x64", + "win-arm32": "ARM", + "win-arm64": "ARM64", +} + +# A CMakeExtension needs a sourcedir instead of a file list. +# The name must be the _single_ output extension from the CMake build. +# If you need multiple extensions, see scikit-build. class CMakeExtension(Extension): def __init__(self, name: str, sourcedir: str = "") -> None: super().__init__(name, sources=[]) @@ -28,43 +43,110 @@ def run(self): super().run() - def build_extension(self, ext): + def build_extension(self, ext: Extension) -> None: + # This is needed to build the Cython extension. if not isinstance(ext, CMakeExtension): super().build_extension(ext) return - extdir = os.path.abspath(os.path.dirname(self.get_ext_fullpath(ext.name))) + # Must be in this form due to bug in .resolve() only fixed in Python 3.10+ + ext_fullpath = Path.cwd() / self.get_ext_fullpath(ext.name) + extdir = ext_fullpath.parent.resolve() + + # Using this requires trailing slash for auto-detection & inclusion of + # auxiliary "native" libs + + debug = int(os.environ.get("DEBUG", 0)) if self.debug is None else self.debug + cfg = "Debug" if debug else "Release" + + # CMake lets you override the generator - we need to check this. + # Can be set with Conda-Build, for example. + cmake_generator = os.environ.get("CMAKE_GENERATOR", "") + + # EXAMPLE_VERSION_INFO shows you how to pass a value into the C++ code + # from Python. cmake_args = [ - "-DCMAKE_LIBRARY_OUTPUT_DIRECTORY=" + extdir, - "-DPYTHON_EXECUTABLE=" + sys.executable, + f"-DCMAKE_LIBRARY_OUTPUT_DIRECTORY={extdir}{os.sep}", + f"-DPython_EXECUTABLE={sys.executable}", + f"-DCMAKE_BUILD_TYPE={cfg}", # not used on MSVC, but no harm ] + build_args = [] + # Adding CMake arguments set as environment variable + # (needed e.g. to build for ARM OSx on conda-forge) + if "CMAKE_ARGS" in os.environ: + cmake_args += [item for item in os.environ["CMAKE_ARGS"].split(" ") if item] + + # In this example, we pass in the version to C++. You might not need to. + cmake_args += [f"-DPACKAGE_VERSION_INFO={self.distribution.get_version()}"] + + if self.compiler.compiler_type != "msvc": + # Using Ninja-build since it a) is available as a wheel and b) + # multithreads automatically. MSVC would require all variables be + # exported for Ninja to pick it up, which is a little tricky to do. + # Users can override the generator with CMAKE_GENERATOR in CMake + # 3.15+. + if not cmake_generator or cmake_generator == "Ninja": + try: + import ninja - cfg = "Debug" if self.debug else "Release" - build_args = ["--config", cfg] + ninja_executable_path = Path(ninja.BIN_DIR) / "ninja" + cmake_args += [ + "-GNinja", + f"-DCMAKE_MAKE_PROGRAM:FILEPATH={ninja_executable_path}", + ] + except ImportError: + pass - if platform.system() == "Windows": - cmake_args += [ - "-DCMAKE_LIBRARY_OUTPUT_DIRECTORY_{}={}".format(cfg.upper(), extdir) - ] - if sys.maxsize > 2**32: - cmake_args += ["-A", "x64"] - build_args += ["--", "/m"] else: - cmake_args += ["-DCMAKE_BUILD_TYPE=" + cfg] - build_args += ["--", "-j2"] + # Single config generators are handled "normally" + single_config = any(x in cmake_generator for x in {"NMake", "Ninja"}) - env = os.environ.copy() - env["CXXFLAGS"] = '{} -DVERSION_INFO=\\"{}\\"'.format( - env.get("CXXFLAGS", ""), self.distribution.get_version() - ) - if not os.path.exists(self.build_temp): - os.makedirs(self.build_temp) + # CMake allows an arch-in-generator style for backward compatibility + contains_arch = any(x in cmake_generator for x in {"ARM", "Win64"}) + + # Specify the arch if using MSVC generator, but only if it doesn't + # contain a backward-compatibility arch spec already in the + # generator name. + if not single_config and not contains_arch: + cmake_args += ["-A", PLAT_TO_CMAKE[self.plat_name]] + + # Multi-config generators have a different way to specify configs + if not single_config: + cmake_args += [ + f"-DCMAKE_LIBRARY_OUTPUT_DIRECTORY_{cfg.upper()}={extdir}" + ] + build_args += ["--config", cfg] + + # When building universal2 wheels, we need to set the architectures for CMake. + if "universal2" in self.plat_name: + cmake_args += ["-DCMAKE_OSX_ARCHITECTURES=arm64;x86_64"] + + # Set MACOSX_DEPLOYMENT_TARGET for macOS builds. + if ( + self.plat_name.startswith("macosx-") + and "MACOSX_DEPLOYMENT_TARGET" not in os.environ + ): + target_version = self.plat_name.split("-")[1] + os.environ["MACOSX_DEPLOYMENT_TARGET"] = target_version + + # Set CMAKE_BUILD_PARALLEL_LEVEL to control the parallel build level + # across all generators. + if "CMAKE_BUILD_PARALLEL_LEVEL" not in os.environ: + # self.parallel is a Python 3 only way to set parallel jobs by hand + # using -j in the build_ext call, not supported by pip or PyPA-build. + if hasattr(self, "parallel") and self.parallel: + # CMake 3.12+ only. + build_args += [f"-j{self.parallel}"] + + build_temp = Path(self.build_temp) / ext.name + if not build_temp.exists(): + build_temp.mkdir(parents=True) - subprocess.check_call( - ["cmake", ext.sourcedir] + cmake_args, cwd=self.build_temp, env=env + subprocess.run( + ["cmake", ext.sourcedir, *cmake_args], cwd=build_temp, check=True ) - subprocess.check_call( - ["cmake", "--build", "."] + build_args, cwd=self.build_temp + subprocess.run( + ["cmake", "--build", ".", *build_args], cwd=build_temp, check=True ) From 42a0489e0a66002f36fa620282d44c23704e9f3f Mon Sep 17 00:00:00 2001 From: Robin Scheibler Date: Fri, 3 Apr 2026 17:45:11 +0900 Subject: [PATCH 7/9] Updates the doc in CONTRIBUTING.rst for the new compile system. Adds doc, repo, issues links in pyproject.toml. CHANGELOG. --- CHANGELOG.rst | 9 +++- CONTRIBUTING.rst | 113 ++++++++++++++++++++--------------------------- pyproject.toml | 10 ++++- 3 files changed, 65 insertions(+), 67 deletions(-) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 9caa6361..4f758e86 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -11,7 +11,14 @@ adheres to `Semantic Versioning `_. `Unreleased`_ ------------- -Nothing yet. +Changed +~~~~~~~ + +- Modernized the build system to use ``pyproject.toml`` and ``CMake``. +- External dependencies (``Eigen``, ``nanoflann``, ``pybind11``) are now automatically managed via CMake's ``FetchContent``. +- Switched to dynamic versioning using ``setuptools_scm``. +- Relocated the test suite from the source package to a top-level ``tests/`` directory. +- Update the continuous integration to use cibuildwheel to handle all platforms including manylinux. `0.10.0`_ - 2026-04-01 diff --git a/CONTRIBUTING.rst b/CONTRIBUTING.rst index 47bcf57c..d25b0eec 100644 --- a/CONTRIBUTING.rst +++ b/CONTRIBUTING.rst @@ -24,16 +24,24 @@ We try to stick to `PEP8 `__ as much as possible. Variables, functions, modules and packages should be in lowercase with underscores. Class names in CamelCase. -We use `Black `__ to format the code and `isort `__ to sort the imports. -The format will be automatically checked when doing a pull request so it is -recommended to regularly run Black on the code. -Please format your code as follows prior to commiting. +We use `pre-commit `__ to manage code quality hooks, +including `Black `__ for formatting and +`isort `__ for sorting imports. +The hooks will be automatically checked during pull requests. + +To set up the pre-commit hooks locally: + +.. code-block:: shell + + pip install pre-commit + pre-commit install + +Once installed, the hooks will run automatically on every commit. +You can also run them manually on all files: .. code-block:: shell - pip install black isort - black . - isort --profile black . + pre-commit run --all-files Documentation ~~~~~~~~~~~~~ @@ -51,79 +59,56 @@ We recommend the following steps for generating the documentation: - Build and view the documentation locally with: ``make html`` - Open in your browser: ``docs/_build/html/index.html`` -Develop Locally -~~~~~~~~~~~~~~~ - -It can be convenient to develop and run tests locally. In contrast to only +It can be convenient to develop and run tests locally. In contrast to only using the package, you will then also need to compile the C++ extension for -that. On Mac and Linux, GCC is required, while Visual C++ 14.0 is necessary for -`windows `__. - -1. Get the source code. Use recursive close so that Eigen (a sub-module of this - repository) is also downloaded. - - .. code-block:: shell +that. - git clone --recursive git@github.com:LCAV/pyroomacoustics.git +Requirements: +- C++ compiler: GCC or Clang on Mac/Linux, Visual C++ 14.0+ on Windows. +- **CMake** (version 3.10 or higher). +- **pre-commit** - Alternatively, you can clone without the `--recursive` flag and directly - install the Eigen library. For macOS, you can find installation instruction - here: https://stackoverflow.com/a/35658421. After installation you can - create a symbolic link as such: - - .. code-block:: shell - - ln -s PATH_TO_EIGEN pyroomacoustics/libroom_src/ext/eigen/Eigen - -2. Install a few pre-requisites - - .. code-block:: shell - - pip install numpy Cython pybind11 - -3. Compile locally +1. Get the source code. .. code-block:: shell - python setup.py build_ext --inplace - - On recent Mac OS (Mojave), it is necessary in some cases to add a - higher deployment target + git clone git@github.com:LCAV/pyroomacoustics.git - .. code-block:: shell + External dependencies (`Eigen`, `nanoflann`, `pybind11`) are automatically + downloaded during the build process using CMake's `FetchContent`. - MACOSX_DEPLOYMENT_TARGET=10.9 python setup.py build_ext --inplace +2. Install in editable mode. -4. Update ``$PYTHONPATH`` so that python knows where to find the local package + Editable mode is recommended for local development as it correctly links + the source files and compiled extensions. .. code-block:: shell - # Linux/Mac - export PYTHONPATH=:$PYTHONPATH + pip install -U -e . - For windows, see `this question `__ - on stackoverflow. + The build process uses CMake to compile the C++ extensions. + The ``-U`` flag ensures that the package is upgraded to the latest version. + Make sure to re-run this command when changing the C++ code. -5. Install the dependencies listed in ``requirements.txt`` + On macOS, if necessary, you can set the deployment target: .. code-block:: shell - pip install -r requirements.txt + MACOSX_DEPLOYMENT_TARGET=11.0 pip install -U -e . -6. Now fire up ``python`` or ``ipython`` and check that the package can be - imported +3. Verify the installation. - .. code-block:: python + .. code-block:: shell - import pyroomacoustics as pra + python -c "import pyroomacoustics as pra; print(pra.__version__)" Unit Tests ~~~~~~~~~~ As much as possible, for every new function added to the code base, add -a short test script in ``pyroomacoustics/tests``. The names of the +a short test script in the top-level ``tests/`` directory. The names of the script and the functions running the test should be prefixed by -``test_``. The tests are started by running ``nosetests`` at the root of +``test_``. The tests are started by running ``pytest`` at the root of the package. How to make a clean pull request @@ -167,18 +152,18 @@ How to deploy a new version to pypi 1. git checkout pypi-release 2. git merge master -3. Change version number in ``pyroomacoustics/version.py`` to new version number vX.Y.Z -4. Edit ``CHANGELOG.rst`` as follows +3. Edit ``CHANGELOG.rst`` as follows - Add new title ``X.Y.Z_ - YEAR-MONTH-DAY`` under ``Unreleased``, add "Nothing yet" in the unreleased section. - - Edit appropriately the lists of links at the bottom of the file. -5. git commit -6. git tag vX.Y.Z -7. git push origin vX.Y.Z -8. git push -9. git checkout master -10. git merge pypi-release -11. git push origin master + - Edit appropriately the list of links at the bottom of the file. +4. git commit +5. Tag the new version (e.g., vX.Y.Z). Version strings are automatically + generated from git tags using `setuptools_scm`. +6. git push origin vX.Y.Z +7. git push +8. git checkout master +9. git merge pypi-release +10. git push origin master Reference --------- diff --git a/pyproject.toml b/pyproject.toml index e62e6438..06f1e253 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -19,21 +19,27 @@ classifiers = [ "Topic :: Scientific/Engineering :: Physics", "Topic :: Multimedia :: Sound/Audio :: Speech", "Topic :: Multimedia :: Sound/Audio :: Analysis", - "Programming Language :: Python :: 3.8", "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", + "Programming Language :: Python :: 3.13", + "Programming Language :: Python :: 3.14", ] dependencies = [ "Cython", "numpy>=1.13.0", "scipy>=0.18.0", ] -requires-python = ">=3.8" +requires-python = ">=3.9" [project.urls] Homepage = "https://github.com/LCAV/pyroomacoustics" +Repository = "https://github.com/LCAV/pyroomacoustics.git" +Documentation = "https://pyroomacoustics.readthedocs.io/en/pypi-release/" +Issues = "https://github.com/LCAV/pyroomacoustics/issues" +Changelog = "https://github.com/LCAV/pyroomacoustics/blob/master/CHANGELOG.rst" + [tool.setuptools.dynamic] readme = { file = ["README.rst"], content-type = "text/x-rst" } From 1da627dac5b07d664fff6887c6423090c2c1d724 Mon Sep 17 00:00:00 2001 From: Robin Scheibler Date: Sat, 4 Apr 2026 17:48:06 +0900 Subject: [PATCH 8/9] Removes the deprecated parameter from ShoeBox, Room.from_corners, and Room.extrude. --- CHANGELOG.rst | 6 +- pyroomacoustics/room.py | 179 +++++----------------------------------- 2 files changed, 26 insertions(+), 159 deletions(-) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 9caa6361..997af515 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -11,7 +11,11 @@ adheres to `Semantic Versioning `_. `Unreleased`_ ------------- -Nothing yet. +Changed +------- + +- Removed the deprecated ``absorption`` parameter from ``ShoeBox``, + ``Room.from_corners``, and ``Room.extrude``. `0.10.0`_ - 2026-04-01 diff --git a/pyroomacoustics/room.py b/pyroomacoustics/room.py index dc0b446c..83a7b0a9 100644 --- a/pyroomacoustics/room.py +++ b/pyroomacoustics/room.py @@ -1210,7 +1210,6 @@ def _wall_mapping(self): def from_corners( cls, corners, - absorption=None, fs=8000, t0=0.0, max_order=1, @@ -1227,9 +1226,6 @@ def from_corners( ---------- corners: (np.array dim 2xN, N>2) list of corners, must be antiClockwise oriented - absorption: float array or float - list of absorption factor for each wall or single value - for all walls (deprecated, use ``materials`` instead) fs: int, optional The sampling frequency in Hz. Default is 8000. t0: float, optional @@ -1266,37 +1262,7 @@ def from_corners( if libroom.area_2d_polygon(corners) <= 0: corners = corners[:, ::-1] - ############################ - # BEGIN COMPATIBILITY CODE # - ############################ - - if absorption is None: - absorption = 0.0 - absorption_compatibility_request = False - else: - absorption_compatibility_request = True - - absorption = np.array(absorption, dtype="float64") - if absorption.ndim == 0: - absorption = absorption * np.ones(n_walls) - elif absorption.ndim >= 1 and n_walls != len(absorption): - raise ValueError( - "Arg absorption must be the same size as corners or must be a single value." - ) - - ############################ - # BEGIN COMPATIBILITY CODE # - ############################ - if materials is not None: - if absorption_compatibility_request: - import warnings - - warnings.warn( - "Because materials were specified, deprecated absorption parameter is ignored.", - DeprecationWarning, - ) - if not isinstance(materials, list): materials = [materials] * n_walls @@ -1308,21 +1274,9 @@ def from_corners( materials[i], Material ), "Material not specified using correct class" - elif absorption_compatibility_request: - import warnings - - warnings.warn( - "Using absorption parameter is deprecated. In the future, use materials instead." - ) - - # Fix the absorption - # 1 - a1 == sqrt(1 - a2) <-- a1 is former incorrect absorption, a2 is the correct definition based on energy - # <=> a2 == 1 - (1 - a1) ** 2 - correct_absorption = 1.0 - (1.0 - absorption) ** 2 - materials = make_materials(*correct_absorption) - else: - # In this case, no material is provided, use totally reflective walls, no scattering + # In this case, no material is provided, use totally reflective + # walls, no scattering materials = [Material(0.0, 0.0)] * n_walls # Resample material properties at octave bands @@ -1362,7 +1316,8 @@ def from_corners( def extrude(self, height, v_vec=None, absorption=None, materials=None): """ Creates a 3D room by extruding a 2D polygon. - The polygon is typically the floor of the room and will have z-coordinate zero. The ceiling + The polygon is typically the floor of the room and will have + z-coordinate zero. The ceiling Parameters ---------- @@ -1372,15 +1327,11 @@ def extrude(self, height, v_vec=None, absorption=None, materials=None): A unit vector. An orientation for the extrusion direction. The ceiling will be placed as a translation of the floor with respect to this vector (The default is [0,0,1]). - absorption : float or array-like, optional - Absorption coefficients for all the walls. If a scalar, then all the walls - will have the same absorption. If an array is given, it should have as many elements - as there will be walls, that is the number of vertices of the polygon plus two. The two - last elements are for the floor and the ceiling, respectively. - It is recommended to use materials instead of absorption parameter. (Default: 1) - materials : dict - Absorption coefficients for floor and ceiling. This parameter overrides absorption. - (Default: {"floor": 1, "ceiling": 1}) + materials : dict[str, pyroomacoustics.Material] + Material properties of the floor and ceiling. + A single Material object will be repeated for both floor and ceiling. + Otherwise, a dict with keys 'floor' and 'ceiling' is expected. + (Default: {"floor": pra.Material(0, 0), "ceiling": pra.Material(0, 0)}) """ if self.dim != 2: @@ -1453,59 +1404,25 @@ def extrude(self, height, v_vec=None, absorption=None, materials=None): "Encountered a material with inconsistent number of bands." ) - ############################ - # BEGIN COMPATIBILITY CODE # - ############################ - if absorption is not None: - absorption = 0.0 - absorption_compatibility_request = True - else: - absorption_compatibility_request = False - ########################## - # END COMPATIBILITY CODE # - ########################## - if materials is not None: - if absorption_compatibility_request: - import warnings - - warnings.warn( - "Because materials were specified, " - "deprecated absorption parameter is ignored.", - DeprecationWarning, - ) - if not isinstance(materials, dict): materials = {"floor": materials, "ceiling": materials} - for mat in materials.values(): - assert isinstance( - mat, Material - ), "Material not specified using correct class" - - elif absorption_compatibility_request: - import warnings - - warnings.warn( - "absorption parameter is deprecated for Room.extrude", - DeprecationWarning, - ) - - absorption = np.array(absorption) - if absorption.ndim == 0: - absorption = absorption * np.ones(2) - elif absorption.ndim == 1 and absorption.shape[0] != 2: + if "floor" not in materials or "ceiling" not in materials: raise ValueError( - "The size of the absorption array must be 2 for extrude, " - "for the floor and ceiling" + "Expected to find keys 'floor' and 'ceiling' in materials." ) - materials = make_materials( - floor=(absorption[0], 0.0), ceiling=(absorption[0], 0.0) - ) + for mat in materials.values(): + if not isinstance(mat, Material): + raise TypeError( + "Expected materials of type pyroomacoustics.Material " + f"(got {type(mat)})." + ) else: - # In this case, no material is provided, use totally reflective walls, no scattering + # In this case, no material is provided, use totally reflective + # walls, no scattering. new_mat = Material(0.0, 0.0) materials = {"floor": new_mat, "ceiling": new_mat} @@ -1513,7 +1430,8 @@ def extrude(self, height, v_vec=None, absorption=None, materials=None): new_corners["floor"] = np.pad(floor_corners, ((0, 1), (0, 0)), mode="constant") new_corners["ceiling"] = (new_corners["floor"].T + height * v_vec).T - # we need the floor corners to ordered clockwise (for the normal to point outward) + # we need the floor corners to ordered clockwise + # (for the normal to point outward) new_corners["floor"] = np.fliplr(new_corners["floor"]) # Concatenate new walls param with old ones. @@ -2893,7 +2811,6 @@ def __init__( p, fs=8000, t0=0.0, - absorption=None, # deprecated max_order=1, sigma2_awgn=None, sources=None, @@ -2941,32 +2858,7 @@ def __init__( n_walls = len(self.wall_names) - ############################ - # BEGIN COMPATIBILITY CODE # - ############################ - - if absorption is None: - absorption_compatibility_request = False - absorption = 0.0 - else: - absorption_compatibility_request = True - - # copy over the absorption coefficient - if isinstance(absorption, float): - absorption = dict(zip(self.wall_names, [absorption] * n_walls)) - - ########################## - # END COMPATIBILITY CODE # - ########################## - if materials is not None: - if absorption_compatibility_request: - warnings.warn( - "Because `materials` were specified, deprecated " - "`absorption` parameter is ignored.", - DeprecationWarning, - ) - if isinstance(materials, Material): materials = dict(zip(self.wall_names, [materials] * n_walls)) elif not isinstance(materials, dict): @@ -2982,35 +2874,6 @@ def __init__( materials[w_name], Material ), "Material not specified using correct class" - elif absorption_compatibility_request: - warnings.warn( - "Using absorption parameter is deprecated. Use `materials` with " - "`Material` object instead.", - DeprecationWarning, - ) - - # order the wall absorptions - if not isinstance(absorption, dict): - raise ValueError( - "`absorption` must be either a scalar or a " - "2x dim dictionary with entries for each " - "wall, namely: 'east', 'west', 'north', " - "'south', 'ceiling' (3d), 'floor' (3d)." - ) - - materials = {} - for w_name in self.wall_names: - if w_name in absorption: - # Fix the absorption - # 1 - a1 == sqrt(1 - a2) <-- a1 is former incorrect absorption, a2 is the correct definition based on energy - # <=> a2 == 1 - (1 - a1) ** 2 - correct_abs = 1.0 - (1.0 - absorption[w_name]) ** 2 - materials[w_name] = Material(energy_absorption=correct_abs) - else: - raise KeyError( - "Absorption needs to have keys 'east', 'west', " - "'north', 'south', 'ceiling' (3d), 'floor' (3d)." - ) else: # In this case, no material is provided, use totally reflective # walls, no scattering From fd0f21103066e763ac8f40519892292b9ba08fae Mon Sep 17 00:00:00 2001 From: Robin Scheibler Date: Tue, 7 Apr 2026 00:07:09 +0900 Subject: [PATCH 9/9] CHANGELOG --- CHANGELOG.rst | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 0367ff39..6f0dffd4 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -14,6 +14,8 @@ adheres to `Semantic Versioning `_. Changed ~~~~~~~ +- Removed the deprecated ``absorption`` parameter from various methods + in ``Room`` and ``ShoeBox``. - Modernized the build system to use ``pyproject.toml`` and ``CMake``. - External dependencies (``Eigen``, ``nanoflann``, ``pybind11``) are now automatically managed via CMake's ``FetchContent``.