From b26d1fd2b03faf2b52f88d7094d8ac744bb395c8 Mon Sep 17 00:00:00 2001 From: Robin Scheibler Date: Wed, 22 Nov 2023 23:57:11 +0900 Subject: [PATCH 01/11] Fixes #191, a bug when setting air abosrption coefficients manually. Improves the doc. --- CHANGELOG.rst | 2 + docs/pyroomacoustics.bss.rst | 2 +- docs/pyroomacoustics.doa.normmusic.rst | 4 +- docs/pyroomacoustics.doa.rst | 1 + pyroomacoustics/room.py | 55 ++++++++++++++++++-- pyroomacoustics/tests/test_air_absorption.py | 37 +++++++++++++ 6 files changed, 94 insertions(+), 7 deletions(-) create mode 100644 pyroomacoustics/tests/test_air_absorption.py diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 75cdc20f..a3b08602 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -33,6 +33,8 @@ Bugfix - Fixes a bug when using randomized image source model with a 2D room (#315) by @hrosseel +- Fixes a bug when setting the air absorption coefficients to custom values (#191), + adds a test, and more details in the doc `0.7.3`_ - 2022-12-05 diff --git a/docs/pyroomacoustics.bss.rst b/docs/pyroomacoustics.bss.rst index 03778cb0..734f0a82 100644 --- a/docs/pyroomacoustics.bss.rst +++ b/docs/pyroomacoustics.bss.rst @@ -20,4 +20,4 @@ Algorithms pyroomacoustics.bss.sparseauxiva pyroomacoustics.bss.common pyroomacoustics.bss.fastmnmf - pytoomacoustics.bss.fastmnmf2 + pyroomacoustics.bss.fastmnmf2 diff --git a/docs/pyroomacoustics.doa.normmusic.rst b/docs/pyroomacoustics.doa.normmusic.rst index d5e6ee40..55e4c3b4 100644 --- a/docs/pyroomacoustics.doa.normmusic.rst +++ b/docs/pyroomacoustics.doa.normmusic.rst @@ -1,5 +1,5 @@ -pyroomacoustics.doa.normmusic module -==================================== +NormMUSIC +========= .. automodule:: pyroomacoustics.doa.normmusic :members: diff --git a/docs/pyroomacoustics.doa.rst b/docs/pyroomacoustics.doa.rst index accce30b..d7db3e66 100644 --- a/docs/pyroomacoustics.doa.rst +++ b/docs/pyroomacoustics.doa.rst @@ -17,6 +17,7 @@ Algorithms pyroomacoustics.doa.cssm pyroomacoustics.doa.frida pyroomacoustics.doa.music + pyroomacoustics.doa.normmusic.rst pyroomacoustics.doa.srp pyroomacoustics.doa.tops pyroomacoustics.doa.waves diff --git a/pyroomacoustics/room.py b/pyroomacoustics/room.py index 95db0cac..a0c9dea4 100644 --- a/pyroomacoustics/room.py +++ b/pyroomacoustics/room.py @@ -482,6 +482,36 @@ - ``floor``/``ceiling`` for the walls int x-y plane with small/large z coordinates, respectively +Air Absorption +-------------- + +The absorption of sound energy by air is frequency dependent. +The absorption is described the frequency dependent coefficient :math:`\\alpha(f)` and the energy decreases with distance as :math:`e^{-\\alpha(f) d}`. +This can be turned simply by providing the keyword argument ``air_absorption=True`` to the room constructor or calling ``set_absorption()`` on an existing room. +The coefficients are also temperature and humidity dependent and the default values are as follows. + +========= ======== ====== ====== ====== ===== ===== ===== ===== ===== +Temp. (C) Hum. (%) 125 Hz 250 Hz 500 Hz 1 kHz 2 kHz 4 kHz 8 kHz +========= ======== ====== ====== ====== ===== ===== ===== ===== ===== +10 30-50 0.1 0.2 0.5 1.1 2.7 9.4 29.0 x1e-3 +10 50-70 0.1 0.2 0.5 0.8 1.8 5.9 21.1 x1e-3 +10 70-90 0.1 0.2 0.5 0.7 1.4 4.4 15.8 x1e-3 +20 30-50 0.1 0.3 0.6 1.0 1.9 5.8 20.3 x1e-3 +20 50-70 0.1 0.3 0.6 1.0 1.7 4.1 13.5 x1e-3 +20 70-90 0.1 0.3 0.6 1.1 1.7 3.5 10.6 x1e-3 +========= ======== ====== ====== ====== ===== ===== ===== ===== ===== + +It is possible to set custom coefficients by providing a lists of coefficients and corresponding frequencies. +If the frequencies are not provided, the default values of 125 Hz to 8 kHz octave bands are assumed. +Note, that the number of octave bands will depend on the sampling frequency used. +For 16 kHz, there will be 7 octave bands. +If less than 7 coefficients are provided, or if the center frequencies do not correspond, a simple interpolation is used to fill the missing values. +Missing values at end of the array are simply replicated from the last value. + +.. code-block:: python + + room.set_air_absorption([0.1, 0.2, 0.4, 1.3, 2.8, 9.4, 23.0]) + Controlling the signal-to-noise ratio ------------------------------------- @@ -1128,14 +1158,29 @@ def unset_ray_tracing(self): self.simulator_state["rt_needed"] = False self._update_room_engine_params() - def set_air_absorption(self, coefficients=None): + def set_air_absorption( + self, coefficients=None, center_freqs=None, interp_kind="linear" + ): """ Activates or deactivates air absorption in the simulation. Parameters ---------- - coefficients: list of float - List of air absorption coefficients, one per octave band + coefficients: list of float, optional + Optional list of air absorption coefficients, one per octave band. + If not provided, values corresponding to the temperature and humidity + of the room are used. + center_freqs: list, optional + The optional list of center frequencies for the octave bands. + If not provided, the values of the default ocatave bands are used. + interp_kind: str + Specifies the kind of interpolation as a string (‘linear’, + ‘nearest’, ‘zero’, ‘slinear’, ‘quadratic’, ‘cubic’, ‘previous’, + ‘next’, where ‘zero’, ‘slinear’, ‘quadratic’ and ‘cubic’ refer to a + spline interpolation of zeroth, first, second or third order; + ‘previous’ and ‘next’ simply return the previous or next value of + the point) or as an integer specifying the order of the spline + interpolator to use. Default is ‘linear’. """ self.simulator_state["air_abs_needed"] = True @@ -1143,7 +1188,9 @@ def set_air_absorption(self, coefficients=None): self.air_absorption = self.octave_bands(**self.physics.get_air_absorption()) else: # ignore temperature and humidity if coefficients are provided - self.air_absorption = self.physics().get_air_absorption() + self.air_absorption = self.octave_bands( + coeffs=coefficients, center_freqs=center_freqs, interp_kind=interp_kind + ) def unset_air_absorption(self): """Deactivates air absorption in the simulation""" diff --git a/pyroomacoustics/tests/test_air_absorption.py b/pyroomacoustics/tests/test_air_absorption.py new file mode 100644 index 00000000..da7ca553 --- /dev/null +++ b/pyroomacoustics/tests/test_air_absorption.py @@ -0,0 +1,37 @@ +import pyroomacoustics as pra +import numpy as np +import pytest + + +@pytest.fixture +def room(): + fs = 16000 + c = pra.constants.get("c") + room_dim = np.array([c, c, 2 * c]) + src = np.ones(3) * 0.5 * c + mic = np.array([0.5, 0.5, 1.5]) * c + mat = pra.Material(energy_absorption="hard_surface") + room = ( + pra.ShoeBox(room_dim, fs, max_order=1, materials=mat) + .add_source(src) + .add_microphone(mic) + ) + return room + + +def test_set_air_absorption_1(room): + air_abs = 0.5 + room.set_air_absorption([air_abs]) + + air_abs_coeffs = room.air_absorption + assert len(air_abs_coeffs) == 7 + assert all([a == air_abs for a in air_abs_coeffs]) + + +def test_set_air_absorption_2(room): + air_abs = [0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7] + room.set_air_absorption(air_abs) + + air_abs_coeffs = room.air_absorption + assert len(air_abs_coeffs) == 7 + assert all(air_abs == air_abs_coeffs) From 3576e1ac396e02ddb18d9545d154b87309b9de2a Mon Sep 17 00:00:00 2001 From: Robin Scheibler Date: Tue, 23 Apr 2024 09:23:39 +0900 Subject: [PATCH 02/11] Updates the package build workflow --- .github/workflows/pythonpackage.yml | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/.github/workflows/pythonpackage.yml b/.github/workflows/pythonpackage.yml index ff9bca0b..5b5add37 100644 --- a/.github/workflows/pythonpackage.yml +++ b/.github/workflows/pythonpackage.yml @@ -10,8 +10,13 @@ jobs: fail-fast: false max-parallel: 12 matrix: - os: [ubuntu-latest, macos-latest, windows-latest] - python-version: [3.7, 3.8, 3.9, "3.10"] + os: [ubuntu-latest, macos-13, macos-latest, windows-latest] + python-version: [3.8, 3.9, "3.10", "3.11", "3.12"] + exclude: + - os: macos-latest + python-version: 3.8 + - os: macos-latest + python-version: 3.9 steps: - uses: actions/checkout@v3 - name: Checkout submodules From 001bcd3ad43e685e627df5f7ed7693701b5cdbbf Mon Sep 17 00:00:00 2001 From: Robin Scheibler Date: Tue, 23 Apr 2024 09:30:07 +0900 Subject: [PATCH 03/11] Updates python version in setup.py. Removes broken image link in README. --- README.rst | 2 -- setup.py | 17 +++++++---------- 2 files changed, 7 insertions(+), 12 deletions(-) diff --git a/README.rst b/README.rst index 8ccf797e..f6a5653d 100755 --- a/README.rst +++ b/README.rst @@ -5,8 +5,6 @@ ------------------------------------------------------------------------------ -.. image:: https://travis-ci.org/LCAV/pyroomacoustics.svg?branch=pypi-release - :target: https://travis-ci.org/LCAV/pyroomacoustics .. image:: https://readthedocs.org/projects/pyroomacoustics/badge/?version=pypi-release :target: http://pyroomacoustics.readthedocs.io/en/pypi-release/ :alt: Documentation Status diff --git a/setup.py b/setup.py index 8acd3a6f..a91a0746 100644 --- a/setup.py +++ b/setup.py @@ -188,8 +188,8 @@ def build_extensions(self): ], cmdclass={"build_ext": BuildExt}, # taken from pybind11 example zip_safe=False, - test_suite="nose.collector", - tests_require=["nose"], + test_suite="pytest", + tests_require=["pytest"], classifiers=[ # How mature is this project? Common values are # 3 - Alpha @@ -207,14 +207,11 @@ def build_extensions(self): "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 :: 2", - "Programming Language :: Python :: 2.7", - "Programming Language :: Python :: 3", - #'Programming Language :: Python :: 3.3', - #'Programming Language :: Python :: 3.4', - "Programming Language :: Python :: 3.5", - "Programming Language :: Python :: 3.6", - "Programming Language :: Python :: 3.7", + "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", From a4a29aa76610ba9792b44a6d047fd78207c75d4f Mon Sep 17 00:00:00 2001 From: Robin Scheibler Date: Tue, 23 Apr 2024 09:45:48 +0900 Subject: [PATCH 04/11] switches to build package in action. fix rt60 test. --- .github/workflows/pythonpackage.yml | 6 +++--- pyroomacoustics/tests/test_rt60.py | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/pythonpackage.yml b/.github/workflows/pythonpackage.yml index 5b5add37..eebae48f 100644 --- a/.github/workflows/pythonpackage.yml +++ b/.github/workflows/pythonpackage.yml @@ -45,17 +45,17 @@ jobs: python -m pip install -e . - name: Test with pytest run: | - pip install -U pytest setuptools wheel twine + pip install -U pytest setuptools build wheel twine pytest - name: Test the universal wheels if: matrix.os == 'ubuntu-latest' run: | - python setup.py sdist + python -m build --sdist twine check dist/* - name: Test the binary wheels if: matrix.os != 'ubuntu-latest' run: | - python setup.py bdist_wheel + python -m build bdist_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' diff --git a/pyroomacoustics/tests/test_rt60.py b/pyroomacoustics/tests/test_rt60.py index 56d698fd..bb700ad4 100644 --- a/pyroomacoustics/tests/test_rt60.py +++ b/pyroomacoustics/tests/test_rt60.py @@ -2,7 +2,7 @@ import pyroomacoustics as pra -eps = 1e-10 +eps = 1e-6 room_dim = [10, 7.5, 3.5] # meters fs = 16000 From 19a8efab4cb20afb0276291a9679e65ce6f5f3ae Mon Sep 17 00:00:00 2001 From: Robin Scheibler Date: Tue, 23 Apr 2024 09:51:53 +0900 Subject: [PATCH 05/11] fixes bdist_wheel build --- .github/workflows/pythonpackage.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/pythonpackage.yml b/.github/workflows/pythonpackage.yml index eebae48f..1bf62e2d 100644 --- a/.github/workflows/pythonpackage.yml +++ b/.github/workflows/pythonpackage.yml @@ -55,7 +55,7 @@ jobs: - name: Test the binary wheels if: matrix.os != 'ubuntu-latest' run: | - python -m build bdist_wheel + 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' From c971e1f30c73427f4d08ba6b8328b346ded0c194 Mon Sep 17 00:00:00 2001 From: Robin Scheibler Date: Tue, 23 Apr 2024 11:01:30 +0900 Subject: [PATCH 06/11] corrects ray energy test --- pyroomacoustics/tests/tests_libroom/test_ray_energy.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/pyroomacoustics/tests/tests_libroom/test_ray_energy.py b/pyroomacoustics/tests/tests_libroom/test_ray_energy.py index 440b7815..f9f18a85 100644 --- a/pyroomacoustics/tests/tests_libroom/test_ray_energy.py +++ b/pyroomacoustics/tests/tests_libroom/test_ray_energy.py @@ -157,8 +157,9 @@ def test_square_room(self): histogram_rt_poly = np.array(h_poly[0])[: len(histogram_gt)] histogram_rt_cube = np.array(h_cube[0])[: len(histogram_gt)] - self.assertTrue(np.allclose(histogram_rt_poly, histogram_gt)) - self.assertTrue(np.allclose(histogram_rt_cube, histogram_gt)) + # print(abs(histogram_rt_poly - histogram_gt).max()) + self.assertTrue(np.allclose(histogram_rt_poly, histogram_gt, atol=1e-6)) + self.assertTrue(np.allclose(histogram_rt_cube, histogram_gt, atol=1e-6)) if __name__ == "__main__": From a3d942c0b56c890135ba3546816bb5d5a9c7ca33 Mon Sep 17 00:00:00 2001 From: Robin Scheibler Date: Tue, 23 Apr 2024 11:30:26 +0900 Subject: [PATCH 07/11] removes scaling of pra logo in readme --- README.rst | 1 - 1 file changed, 1 deletion(-) diff --git a/README.rst b/README.rst index f6a5653d..a4831685 100755 --- a/README.rst +++ b/README.rst @@ -1,5 +1,4 @@ .. image:: https://github.com/LCAV/pyroomacoustics/raw/master/logo/pyroomacoustics_logo_horizontal.png - :scale: 80 % :alt: Pyroomacoustics logo :align: left From 9aac335343d6e87c0c056eae19656eee71d0e2f3 Mon Sep 17 00:00:00 2001 From: Robin Scheibler Date: Tue, 23 Apr 2024 11:32:00 +0900 Subject: [PATCH 08/11] tries to fix ray energy test --- pyroomacoustics/tests/tests_libroom/test_ray_energy.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/pyroomacoustics/tests/tests_libroom/test_ray_energy.py b/pyroomacoustics/tests/tests_libroom/test_ray_energy.py index f9f18a85..107147cc 100644 --- a/pyroomacoustics/tests/tests_libroom/test_ray_energy.py +++ b/pyroomacoustics/tests/tests_libroom/test_ray_energy.py @@ -157,9 +157,10 @@ def test_square_room(self): histogram_rt_poly = np.array(h_poly[0])[: len(histogram_gt)] histogram_rt_cube = np.array(h_cube[0])[: len(histogram_gt)] - # print(abs(histogram_rt_poly - histogram_gt).max()) - self.assertTrue(np.allclose(histogram_rt_poly, histogram_gt, atol=1e-6)) - self.assertTrue(np.allclose(histogram_rt_cube, histogram_gt, atol=1e-6)) + print(abs(histogram_rt_poly - histogram_gt).max()) + print(abs(histogram_rt_cube - histogram_gt).max()) + self.assertTrue(np.allclose(histogram_rt_poly, histogram_gt, atol=1e-5)) + self.assertTrue(np.allclose(histogram_rt_cube, histogram_gt, atol=1e-5)) if __name__ == "__main__": From d38ef8a1a0768c85e0cbc68dffd4b18e29b843ad Mon Sep 17 00:00:00 2001 From: Robin Scheibler Date: Thu, 25 Apr 2024 17:41:52 +0900 Subject: [PATCH 09/11] Replaces the brittle crossing-based point-in-polygon (2D) algorithm by Dan Sunday's robust winding based algorithm. --- pyroomacoustics/libroom_src/geometry.cpp | 117 +++++++++--------- pyroomacoustics/libroom_src/room.cpp | 3 +- .../test_is_inside_2d_polygon.py | 14 +-- .../tests/tests_libroom/test_ray_energy.py | 10 +- 4 files changed, 68 insertions(+), 76 deletions(-) diff --git a/pyroomacoustics/libroom_src/geometry.cpp b/pyroomacoustics/libroom_src/geometry.cpp index 1c612278..26bdb098 100644 --- a/pyroomacoustics/libroom_src/geometry.cpp +++ b/pyroomacoustics/libroom_src/geometry.cpp @@ -233,20 +233,55 @@ Eigen::Vector3f cross(Eigen::Vector3f v1, Eigen::Vector3f v2) return v1.cross(v2); } +bool on_segment(const Eigen::Vector2f &c1, const Eigen::Vector2f &c2, const Eigen::Vector2f &p) { + // Given three collinear points c1, c2, p, the function checks if + // point p lies on line segment c1 <-> c2 + + float x_down, x_up, y_down, y_up; + x_down = fminf(c1.coeff(0), c2.coeff(0)); + x_up = fmaxf(c1.coeff(0), c2.coeff(0)); + y_down = fminf(c1.coeff(1), c2.coeff(1)); + y_up = fmaxf(c1.coeff(1), c2.coeff(1)); + return (x_down <= p.coeff(0) && p.coeff(0) <= x_up && y_down <= p.coeff(1) && p.coeff(1) <= y_up); +} -int is_inside_2d_polygon(const Eigen::Vector2f &p, +inline int is_left(const Eigen::Vector2f &P0, const Eigen::Vector2f &P1, const Eigen::Vector2f &P2) +{ + // isLeft(): tests if a point is Left|On|Right of an infinite line. + // on the line is defined as within epsilon + // Input: three points P0, P1, and P2 + // Return: >0 for P2 left of the line through P0 and P1 + // =0 for P2 on the line + // <0 for P2 right of the line + // See: Algorithm 1 "Area of Triangles and Polygons" + float test_value = ( + (P1.coeff(0) - P0.coeff(0)) * (P2.coeff(1) - P0.coeff(1)) + - (P2.coeff(0) - P0.coeff(0)) * (P1.coeff(1) - P0.coeff(1)) + ); + + if (fabsf(test_value) < libroom_eps) + return 0; + else if (test_value > 0) + return 1; + else + return -1; +} + +int is_inside_2d_polygon(const Eigen::Vector2f &p_test, const Eigen::Matrix &corners) { /* - Checks if a given point is inside a given polygon in 2D. + Checks if a given point is inside a given polygon in 2D using the winding + number method. This function checks if a point (defined by its coordinates) is inside a polygon (defined by an array of coordinates of its corners) by counting - the number of intersections between the borders and a segment linking - the given point with a computed point outside the polygon. - A boolean is also returned to indicate if a point is on a border of the - polygon (the point is still considered inside), which can be useful for - limit cases computations. + the number of left intersections between the edges and a half-line starting from + the test point. + + This is Dave Sunday's winding number algorithm described here: + http://profs.ic.uff.br/~anselmo/cursos/CGI/slidesNovos/Inclusion%20of%20a%20Point%20in%20a%20Polygon.pdf + It was modified to explicitely check for points on the edges of the polygon. p: (array size 2) coordinates of the point corners: (array size 2xN, N>2) coordinates of the corners of the polygon @@ -257,67 +292,35 @@ int is_inside_2d_polygon(const Eigen::Vector2f &p, 0 : the point is inside 1 : the point is on the boundary */ - - bool is_inside = false; // initialize point not in the polygon - int c1c2p, c1c2p0, pp0c1, pp0c2; int n_corners = corners.cols(); - - // find a point outside the polygon - int i_min; - corners.row(0).minCoeff(&i_min); - Eigen::Vector2f p_out; - p_out.resize(2); - p_out.coeffRef(0) = corners.coeff(0,i_min) - 1; - p_out.coeffRef(1) = p.coeff(1); - - // Now count intersections + int wn = 0; // the winding number counter + // loop through all edges of the polygon for (int i = 0, j = n_corners-1 ; i < n_corners ; j=i++) { - - // Check first if the point is on the segment - // We count the border as inside the polygon - c1c2p = ccw3p(corners.col(i), corners.col(j), p); - if (c1c2p == 0) + if (ccw3p(corners.col(j), corners.col(i), p_test) == 0 && on_segment(corners.col(j), corners.col(i), p_test)) { - // Here we know that p is co-linear with the two corners - float x_down, x_up, y_down, y_up; - x_down = fminf(corners.coeff(0,i), corners.coeff(0,j)); - x_up = fmaxf(corners.coeff(0,i), corners.coeff(0,j)); - y_down = fminf(corners.coeff(1,i), corners.coeff(1,j)); - y_up = fmaxf(corners.coeff(1,i), corners.coeff(1,j)); - if (x_down <= p.coeff(0) && p.coeff(0) <= x_up && y_down <= p.coeff(1) && p.coeff(1) <= y_up) - return 1; + // point is on the edge, we consider it inside + return 1; } - - // Now check intersection with standard method - c1c2p0 = ccw3p(corners.col(i), corners.col(j), p_out); - if (c1c2p == c1c2p0) // no intersection - continue; - - pp0c1 = ccw3p(p, p_out, corners.col(i)); - pp0c2 = ccw3p(p, p_out, corners.col(j)); - if (pp0c1 == pp0c2) // no intersection - continue; - - // at this point we are sure there is an intersection - - // the second condition takes care of horizontal edges and intersection on vertex - float c_max = fmaxf(corners.coeff(1,i), corners.coeff(1,j)); - if (p.coeff(1) + libroom_eps < c_max) - { - is_inside = !is_inside; + if (corners.coeff(1,j) <= p_test.coeff(1)) { // start y <= P.y + if (corners.coeff(1,i) > p_test.coeff(1)) { // an upward crossing + if (is_left(corners.col(j), corners.col(i), p_test) > 0) // P left of edge + ++wn; // have a valid up intersect + } + } else { // start y > P.y (no test needed) + if (corners.coeff(1, i) <= p_test.coeff(1)) { // a downward crossing + if (is_left(corners.col(j), corners.col(i), p_test) < 0) // P right of edge + --wn; // have a valid down intersect + } } - } - // for a odd number of intersections, the point is in the polygon - if (is_inside) - return 0; // point strictly inside + if (wn == 0) + return -1; else - return -1; // point is outside + return 0; } - float area_2d_polygon(const Eigen::Matrix &corners) { /* diff --git a/pyroomacoustics/libroom_src/room.cpp b/pyroomacoustics/libroom_src/room.cpp index 3f9431af..540ccc85 100644 --- a/pyroomacoustics/libroom_src/room.cpp +++ b/pyroomacoustics/libroom_src/room.cpp @@ -729,7 +729,7 @@ std::tuple < Vectorf, int, float > Room::next_wall_hit( Wall & w = scattered_ray ? walls[obstructing_walls[i]] : walls[i]; // To store the result of this iteration - Vectorf temp_hit; + Vectorf temp_hit = Vectorf::Zero(); // As a side effect, temp_hit gets a value (VectorXf) here int ret = w.intersection(start, end, temp_hit); @@ -948,6 +948,7 @@ void Room::simul_ray( auto p_hit = (1 - sqrt(1 - mic_radius_sq / std::max(mic_radius_sq, r_sq))); energy = transmitted / (r_sq * p_hit); // energy = transmitted / (travel_dist_at_mic - sqrtf(fmaxf(0.f, travel_dist_at_mic * travel_dist_at_mic - mic_radius_sq))); + microphones[k].log_histogram(travel_dist_at_mic, energy, start); } } diff --git a/pyroomacoustics/tests/tests_libroom/test_is_inside_2d_polygon.py b/pyroomacoustics/tests/tests_libroom/test_is_inside_2d_polygon.py index 8841eb15..3a6a8a63 100644 --- a/pyroomacoustics/tests/tests_libroom/test_is_inside_2d_polygon.py +++ b/pyroomacoustics/tests/tests_libroom/test_is_inside_2d_polygon.py @@ -30,18 +30,8 @@ polygons = [ np.array( [ # this one is clockwise - [ - 0, - 4, - 4, - 0, - ], - [ - 0, - 0, - 4, - 4, - ], + [0, 4, 4, 0], + [0, 0, 4, 4], ] ), np.array( diff --git a/pyroomacoustics/tests/tests_libroom/test_ray_energy.py b/pyroomacoustics/tests/tests_libroom/test_ray_energy.py index 107147cc..7189c92f 100644 --- a/pyroomacoustics/tests/tests_libroom/test_ray_energy.py +++ b/pyroomacoustics/tests/tests_libroom/test_ray_energy.py @@ -83,10 +83,10 @@ def test_square_room(self): print("Creating the python room (polyhedral)") walls_corners = [ - np.array([[0, 2, 2, 0], [0, 0, 0, 0], [0, 0, 2, 2]]), # left - np.array([[0, 0, 2, 2], [2, 2, 2, 2], [0, 2, 2, 0]]), # right - np.array([[0, 0, 0, 0], [0, 2, 2, 0], [0, 0, 2, 2]]), # front` - np.array([[2, 2, 2, 2], [0, 0, 2, 2], [0, 2, 2, 0]]), # back + np.array([[0, 2, 2, 0], [0, 0, 0, 0], [0, 0, 2, 2]]), # front + np.array([[0, 0, 2, 2], [2, 2, 2, 2], [0, 2, 2, 0]]), # back + np.array([[0, 0, 0, 0], [0, 2, 2, 0], [0, 0, 2, 2]]), # left` + np.array([[2, 2, 2, 2], [0, 0, 2, 2], [0, 2, 2, 0]]), # right np.array( [ [0, 2, 2, 0], @@ -157,8 +157,6 @@ def test_square_room(self): histogram_rt_poly = np.array(h_poly[0])[: len(histogram_gt)] histogram_rt_cube = np.array(h_cube[0])[: len(histogram_gt)] - print(abs(histogram_rt_poly - histogram_gt).max()) - print(abs(histogram_rt_cube - histogram_gt).max()) self.assertTrue(np.allclose(histogram_rt_poly, histogram_gt, atol=1e-5)) self.assertTrue(np.allclose(histogram_rt_cube, histogram_gt, atol=1e-5)) From 9e779293c9c66db51dcb8a7b1554b4d691449bfe Mon Sep 17 00:00:00 2001 From: Robin Scheibler Date: Thu, 25 Apr 2024 22:37:16 +0900 Subject: [PATCH 10/11] Fixes a bug with releasing the GIL and python 3.12 in the rir builder part of libroom. Related to https://github.com/wjakob/nanobind/issues/377 --- pyroomacoustics/libroom_src/libroom.cpp | 9 +++------ pyroomacoustics/libroom_src/rir_builder.cpp | 21 ++++++++++++++++----- pyroomacoustics/libroom_src/rir_builder.hpp | 4 ++-- pyroomacoustics/tests/test_build_rir.py | 2 +- 4 files changed, 22 insertions(+), 14 deletions(-) diff --git a/pyroomacoustics/libroom_src/libroom.cpp b/pyroomacoustics/libroom_src/libroom.cpp index 300a5395..444c6be2 100644 --- a/pyroomacoustics/libroom_src/libroom.cpp +++ b/pyroomacoustics/libroom_src/libroom.cpp @@ -282,10 +282,7 @@ PYBIND11_MODULE(libroom, m) { m.def("dist_line_point", &dist_line_point, "Computes the distance between a point and an infinite line"); - m.def("rir_builder", &rir_builder, "RIR builder", - py::call_guard()); - m.def("delay_sum", &delay_sum, "Delay and sum", - py::call_guard()); - m.def("fractional_delay", &fractional_delay, "Fractional delays", - py::call_guard()); + m.def("rir_builder", &rir_builder, "RIR builder"); + m.def("delay_sum", &delay_sum, "Delay and sum"); + m.def("fractional_delay", &fractional_delay, "Fractional delays"); } diff --git a/pyroomacoustics/libroom_src/rir_builder.cpp b/pyroomacoustics/libroom_src/rir_builder.cpp index 5b599aa3..36cf0821 100644 --- a/pyroomacoustics/libroom_src/rir_builder.cpp +++ b/pyroomacoustics/libroom_src/rir_builder.cpp @@ -44,11 +44,12 @@ constexpr T get_pi() { template void threaded_rir_builder_impl( py::array_t rir, - const py::array_t time, - const py::array_t alpha, + const py::array_t &time, + const py::array_t &alpha, const py::array_t - visibility, + &visibility, int fs, size_t fdl, size_t lut_gran, size_t num_threads) { + auto pi = get_pi(); // accessors for the arrays @@ -110,6 +111,9 @@ void threaded_rir_builder_impl( std::vector rir_out(num_threads * rir_len); size_t block_size = size_t(std::ceil(double(n_times) / double(num_threads))); + // relase the GIL from here on + py::gil_scoped_release release; + // build the RIR ThreadPool pool(num_threads); std::vector> results; @@ -166,9 +170,10 @@ void threaded_rir_builder_impl( for (auto &&result : sum_results) result.get(); } -void rir_builder(py::buffer rir, const py::buffer time, const py::buffer alpha, - const py::buffer visibility, int fs, size_t fdl, +void rir_builder(py::buffer rir, const py::buffer &time, const py::buffer &alpha, + const py::buffer &visibility, int fs, size_t fdl, size_t lut_gran, size_t num_threads) { + // dispatch to correct implementation depending on input type auto buf = pybind11::array::ensure(rir); if (py::isinstance>(buf)) { @@ -214,6 +219,9 @@ void threaded_delay_sum_impl( std::vector out_buffers(num_threads * out_len, 0); size_t block_size = size_t(std::ceil(double(n_irs) / double(num_threads))); + // release the GIL from here on + py::gil_scoped_release release; + // build the RIR ThreadPool pool(num_threads); std::vector> results; @@ -315,6 +323,9 @@ void threaded_fractional_delay_impl( // divide into equal size blocks for thread processing size_t block_size = size_t(std::ceil(double(n_times) / double(num_threads))); + // relase the GIL from here on + py::gil_scoped_release release; + // build the RIR ThreadPool pool(num_threads); std::vector> results; diff --git a/pyroomacoustics/libroom_src/rir_builder.hpp b/pyroomacoustics/libroom_src/rir_builder.hpp index f665123a..f4802a4c 100644 --- a/pyroomacoustics/libroom_src/rir_builder.hpp +++ b/pyroomacoustics/libroom_src/rir_builder.hpp @@ -7,8 +7,8 @@ namespace py = pybind11; -void rir_builder(py::buffer rir, const py::buffer time, const py::buffer alpha, - const py::buffer visibility, int fs, size_t fdl, +void rir_builder(py::buffer rir, const py::buffer &time, const py::buffer &alpha, + const py::buffer &visibility, int fs, size_t fdl, size_t lut_gran, size_t num_threads); void delay_sum(const py::buffer irs, const py::buffer delays, py::buffer output, diff --git a/pyroomacoustics/tests/test_build_rir.py b/pyroomacoustics/tests/test_build_rir.py index b1afc709..c28c7e14 100644 --- a/pyroomacoustics/tests/test_build_rir.py +++ b/pyroomacoustics/tests/test_build_rir.py @@ -285,7 +285,7 @@ def measure_runtime(dtype=np.float32, num_threads=4): td = np.round(tt).astype(np.int32) tf = (tt - td).astype(dtype) irs = np.zeros((tt.shape[0], fdl), dtype=dtype) - fractional_delay(irs, tf, 20, num_threads) + libroom.fractional_delay(irs, tf, 20, num_threads) irs *= alpha[:, None] libroom.delay_sum(irs, td, rir, num_threads) tock_2steps = (time.perf_counter() - tick) / n_repeat From 779d776270ee2900e38813d4787a4672b807ef88 Mon Sep 17 00:00:00 2001 From: Robin Scheibler Date: Thu, 25 Apr 2024 23:01:36 +0900 Subject: [PATCH 11/11] lint with black and isort --- .github/workflows/lint.yml | 2 +- examples/adaptive_filter_stft_domain.py | 1 + examples/adaptive_filters.py | 1 + examples/beamforming_delay_and_sum.py | 1 + examples/bss_example.py | 1 + examples/datasets.py | 1 + examples/raytracing.py | 1 + examples/room_L_shape_3d_rt.py | 1 + examples/room_complex_wall_materials.py | 1 + examples/room_from_rt60.py | 1 + examples/room_from_stl.py | 1 + pyroomacoustics/adaptive/lms.py | 1 + pyroomacoustics/adaptive/subband_lms.py | 1 - pyroomacoustics/beamforming.py | 1 - pyroomacoustics/datasets/cmu_arctic.py | 1 + pyroomacoustics/datasets/tests/test_corpus_base.py | 1 + pyroomacoustics/doa/grid.py | 1 + pyroomacoustics/doa/plotters.py | 1 + pyroomacoustics/doa/utils.py | 1 + pyroomacoustics/experimental/signals.py | 1 + pyroomacoustics/tests/test_bandpass_filterbank.py | 1 + pyroomacoustics/tests/test_issue_22.py | 1 + pyroomacoustics/tests/test_materials.py | 1 + pyroomacoustics/tests/test_random_ism.py | 1 + pyroomacoustics/tests/test_room_mix.py | 1 + pyroomacoustics/tests/tests_libroom/test_shoebox_simple.py | 1 + pyroomacoustics/transform/__init__.py | 1 - 27 files changed, 24 insertions(+), 4 deletions(-) diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index 83cf6f89..48f9c0d4 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -26,7 +26,7 @@ jobs: - name: Set up Python uses: actions/setup-python@v4 with: - python-version: 3.8 + python-version: 3.9 - name: Install Python dependencies run: pip install black flake8 isort diff --git a/examples/adaptive_filter_stft_domain.py b/examples/adaptive_filter_stft_domain.py index 256fe1ef..be64e57f 100644 --- a/examples/adaptive_filter_stft_domain.py +++ b/examples/adaptive_filter_stft_domain.py @@ -5,6 +5,7 @@ In this example, we will run adaptive filters for system identification, but in the frequeny domain. """ + from __future__ import division, print_function import matplotlib.pyplot as plt diff --git a/examples/adaptive_filters.py b/examples/adaptive_filters.py index ab27296d..e4b0df0e 100644 --- a/examples/adaptive_filters.py +++ b/examples/adaptive_filters.py @@ -4,6 +4,7 @@ In this example, we will run adaptive filters for system identification. """ + from __future__ import division, print_function import matplotlib.pyplot as plt diff --git a/examples/beamforming_delay_and_sum.py b/examples/beamforming_delay_and_sum.py index 4a83f1ec..9e882af2 100644 --- a/examples/beamforming_delay_and_sum.py +++ b/examples/beamforming_delay_and_sum.py @@ -1,6 +1,7 @@ """ This example shows how to create delay and sum beamformers """ + from __future__ import division, print_function import matplotlib.pyplot as plt diff --git a/examples/bss_example.py b/examples/bss_example.py index 22ead9b2..f16eb099 100755 --- a/examples/bss_example.py +++ b/examples/bss_example.py @@ -54,6 +54,7 @@ This script requires the `mir_eval` to run, and `tkinter` and `sounddevice` packages for the GUI option. """ + import time import numpy as np diff --git a/examples/datasets.py b/examples/datasets.py index 2e747359..ab9e1644 100644 --- a/examples/datasets.py +++ b/examples/datasets.py @@ -2,6 +2,7 @@ Example of the basic operations with ``pyroomacoustics.datasets.Dataset`` and ``pyroomacoustics.datasets.Sample`` classes """ + from pyroomacoustics.datasets import Dataset, Sample # Prepare a few artificial samples diff --git a/examples/raytracing.py b/examples/raytracing.py index f29410bb..59c414e5 100644 --- a/examples/raytracing.py +++ b/examples/raytracing.py @@ -2,6 +2,7 @@ This example program demonstrates the use of ray tracing for the simulation of rooms of different sizes. """ + from __future__ import print_function import argparse diff --git a/examples/room_L_shape_3d_rt.py b/examples/room_L_shape_3d_rt.py index 396be3d8..089acaa8 100644 --- a/examples/room_L_shape_3d_rt.py +++ b/examples/room_L_shape_3d_rt.py @@ -7,6 +7,7 @@ The simulation is done using the hybrid ISM/RT simulator. """ + from __future__ import print_function import time diff --git a/examples/room_complex_wall_materials.py b/examples/room_complex_wall_materials.py index fc9a2359..41bb1905 100644 --- a/examples/room_complex_wall_materials.py +++ b/examples/room_complex_wall_materials.py @@ -4,6 +4,7 @@ 2022 (c) @noahdeetzers, @fakufaku """ + import matplotlib.pyplot as plt import numpy as np diff --git a/examples/room_from_rt60.py b/examples/room_from_rt60.py index b97681b8..d33141e0 100644 --- a/examples/room_from_rt60.py +++ b/examples/room_from_rt60.py @@ -4,6 +4,7 @@ The simulation is pure image source method. The audio sample with the reverb added is saved back to `examples/samples/guitar_16k_reverb.wav`. """ + import argparse import matplotlib.pyplot as plt diff --git a/examples/room_from_stl.py b/examples/room_from_stl.py index 12d49086..12063d94 100644 --- a/examples/room_from_stl.py +++ b/examples/room_from_stl.py @@ -5,6 +5,7 @@ The STL file was kindly provided by Diego Di Carlo (@Chutlhu). """ + import argparse import os from pathlib import Path diff --git a/pyroomacoustics/adaptive/lms.py b/pyroomacoustics/adaptive/lms.py index a36446b2..dac915b8 100644 --- a/pyroomacoustics/adaptive/lms.py +++ b/pyroomacoustics/adaptive/lms.py @@ -5,6 +5,7 @@ Implementations of adaptive filters from the LMS class. These algorithms have a low complexity and reliable behavior with a somewhat slower convergence. """ + from __future__ import absolute_import, division, print_function import numpy as np diff --git a/pyroomacoustics/adaptive/subband_lms.py b/pyroomacoustics/adaptive/subband_lms.py index d9c0479c..70c0cf92 100644 --- a/pyroomacoustics/adaptive/subband_lms.py +++ b/pyroomacoustics/adaptive/subband_lms.py @@ -26,7 +26,6 @@ class SubbandLMS: - """ Frequency domain implementation of LMS. Adaptive filter for each subband. diff --git a/pyroomacoustics/beamforming.py b/pyroomacoustics/beamforming.py index 2cf0846b..f5d5fdf6 100644 --- a/pyroomacoustics/beamforming.py +++ b/pyroomacoustics/beamforming.py @@ -348,7 +348,6 @@ def circular_microphone_array_xyplane( class MicrophoneArray(object): - """Microphone array class.""" def __init__(self, R, fs, directivity=None): diff --git a/pyroomacoustics/datasets/cmu_arctic.py b/pyroomacoustics/datasets/cmu_arctic.py index a3e42e96..f9836197 100644 --- a/pyroomacoustics/datasets/cmu_arctic.py +++ b/pyroomacoustics/datasets/cmu_arctic.py @@ -29,6 +29,7 @@ URL: http://www.festvox.org/cmu_arctic/ """ + import os import numpy as np diff --git a/pyroomacoustics/datasets/tests/test_corpus_base.py b/pyroomacoustics/datasets/tests/test_corpus_base.py index 4d356b24..b49a7b27 100644 --- a/pyroomacoustics/datasets/tests/test_corpus_base.py +++ b/pyroomacoustics/datasets/tests/test_corpus_base.py @@ -2,6 +2,7 @@ Example of the basic operations with ``pyroomacoustics.datasets.Dataset`` and ``pyroomacoustics.datasets.Sample`` classes """ + from pyroomacoustics.datasets import Dataset, Sample diff --git a/pyroomacoustics/doa/grid.py b/pyroomacoustics/doa/grid.py index 6cf965ce..09d1e22b 100644 --- a/pyroomacoustics/doa/grid.py +++ b/pyroomacoustics/doa/grid.py @@ -1,6 +1,7 @@ """ Routines to perform grid search on the sphere """ + from __future__ import absolute_import, division, print_function from abc import ABCMeta, abstractmethod diff --git a/pyroomacoustics/doa/plotters.py b/pyroomacoustics/doa/plotters.py index 8b1846bc..764f3d24 100644 --- a/pyroomacoustics/doa/plotters.py +++ b/pyroomacoustics/doa/plotters.py @@ -1,6 +1,7 @@ """ A collection of functions to plot maps and points on circles and spheres. """ + import numpy as np diff --git a/pyroomacoustics/doa/utils.py b/pyroomacoustics/doa/utils.py index e738c96a..3bb4af9a 100644 --- a/pyroomacoustics/doa/utils.py +++ b/pyroomacoustics/doa/utils.py @@ -2,6 +2,7 @@ This module contains useful functions to compute distances and errors on on circles and spheres. """ + from __future__ import division import numpy as np diff --git a/pyroomacoustics/experimental/signals.py b/pyroomacoustics/experimental/signals.py index e6659af0..24359110 100644 --- a/pyroomacoustics/experimental/signals.py +++ b/pyroomacoustics/experimental/signals.py @@ -1,6 +1,7 @@ """ A few test signals like sweeps and stuff. """ + from __future__ import division, print_function import numpy as np diff --git a/pyroomacoustics/tests/test_bandpass_filterbank.py b/pyroomacoustics/tests/test_bandpass_filterbank.py index 07c9c94d..1522d2bb 100644 --- a/pyroomacoustics/tests/test_bandpass_filterbank.py +++ b/pyroomacoustics/tests/test_bandpass_filterbank.py @@ -1,6 +1,7 @@ """ This tests the construction of a bank of octave filters """ + import numpy as np from scipy.signal import sosfreqz diff --git a/pyroomacoustics/tests/test_issue_22.py b/pyroomacoustics/tests/test_issue_22.py index 76f3cb66..31df1b8a 100644 --- a/pyroomacoustics/tests/test_issue_22.py +++ b/pyroomacoustics/tests/test_issue_22.py @@ -14,6 +14,7 @@ If the C module is not installed (pure python fallback version), then nothing is done. """ + import numpy as np import pyroomacoustics diff --git a/pyroomacoustics/tests/test_materials.py b/pyroomacoustics/tests/test_materials.py index 141d1d3d..a0954c35 100644 --- a/pyroomacoustics/tests/test_materials.py +++ b/pyroomacoustics/tests/test_materials.py @@ -2,6 +2,7 @@ Just run the Material command with a bunch of inputs to make sure it works as expected """ + import pyroomacoustics as pra scat_test = { diff --git a/pyroomacoustics/tests/test_random_ism.py b/pyroomacoustics/tests/test_random_ism.py index bcb809a4..43bc6743 100644 --- a/pyroomacoustics/tests/test_random_ism.py +++ b/pyroomacoustics/tests/test_random_ism.py @@ -5,6 +5,7 @@ Script to test removal of sweeping echoes with randomized image method """ + import pyroomacoustics as pra from pyroomacoustics import metrics as met diff --git a/pyroomacoustics/tests/test_room_mix.py b/pyroomacoustics/tests/test_room_mix.py index 336ebb3c..6cac9989 100644 --- a/pyroomacoustics/tests/test_room_mix.py +++ b/pyroomacoustics/tests/test_room_mix.py @@ -1,6 +1,7 @@ """ Tests the mixing function of ``Room.simulate`` """ + import unittest import numpy as np diff --git a/pyroomacoustics/tests/tests_libroom/test_shoebox_simple.py b/pyroomacoustics/tests/tests_libroom/test_shoebox_simple.py index e0110d05..10d94b47 100644 --- a/pyroomacoustics/tests/tests_libroom/test_shoebox_simple.py +++ b/pyroomacoustics/tests/tests_libroom/test_shoebox_simple.py @@ -7,6 +7,7 @@ It was created to address Issue #293 on github. https://github.com/LCAV/pyroomacoustics/issues/293 """ + import numpy as np import pytest diff --git a/pyroomacoustics/transform/__init__.py b/pyroomacoustics/transform/__init__.py index 00559f93..e11fb411 100644 --- a/pyroomacoustics/transform/__init__.py +++ b/pyroomacoustics/transform/__init__.py @@ -15,7 +15,6 @@ """ - from . import stft from .dft import DFT from .stft import STFT