Skip to content

Commit 20e4106

Browse files
committed
merge master
2 parents 3984eaa + fa064c2 commit 20e4106

24 files changed

+351
-242
lines changed

.github/workflows/lint.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ jobs:
2626
- name: Set up Python
2727
uses: actions/setup-python@v4
2828
with:
29-
python-version: 3.8
29+
python-version: 3.9
3030

3131
- name: Install Python dependencies
3232
run: pip install black flake8 isort

.github/workflows/pythonpackage.yml

+10-5
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,13 @@ jobs:
1010
fail-fast: false
1111
max-parallel: 12
1212
matrix:
13-
os: [ubuntu-latest, macos-latest, windows-latest]
14-
python-version: [3.7, 3.8, 3.9, "3.10"]
13+
os: [ubuntu-latest, macos-13, macos-latest, windows-latest]
14+
python-version: [3.8, 3.9, "3.10", "3.11", "3.12"]
15+
exclude:
16+
- os: macos-latest
17+
python-version: 3.8
18+
- os: macos-latest
19+
python-version: 3.9
1520
steps:
1621
- uses: actions/checkout@v3
1722
- name: Checkout submodules
@@ -41,17 +46,17 @@ jobs:
4146
python -m pip install -e .
4247
- name: Test with pytest
4348
run: |
44-
pip install -U pytest setuptools wheel twine
49+
pip install -U pytest setuptools build wheel twine
4550
pytest
4651
- name: Test the universal wheels
4752
if: matrix.os == 'ubuntu-latest'
4853
run: |
49-
python setup.py sdist
54+
python -m build --sdist
5055
twine check dist/*
5156
- name: Test the binary wheels
5257
if: matrix.os != 'ubuntu-latest'
5358
run: |
54-
python setup.py bdist_wheel
59+
python -m build --wheel
5560
twine check dist/*
5661
- name: Publish sdist to pypi
5762
if: github.event_name == 'push' && startsWith(github.event.ref, 'refs/tags/v') && matrix.os == 'ubuntu-latest'

.readthedocs.yaml

+35
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
# Read the Docs configuration file for Sphinx projects
2+
# See https://docs.readthedocs.io/en/stable/config-file/v2.html for details
3+
4+
# Required
5+
version: 2
6+
7+
# Set the OS, Python version and other tools you might need
8+
build:
9+
os: ubuntu-22.04
10+
tools:
11+
python: "3.12"
12+
# You can also specify other tool versions:
13+
# nodejs: "20"
14+
# rust: "1.70"
15+
# golang: "1.20"
16+
17+
# Build documentation in the "docs/" directory with Sphinx
18+
sphinx:
19+
configuration: docs/conf.py
20+
# You can configure Sphinx to use a different builder, for instance use the dirhtml builder for simpler URLs
21+
# builder: "dirhtml"
22+
# Fail on all warnings to avoid broken references
23+
# fail_on_warning: true
24+
25+
# Optionally build your docs in additional formats such as PDF and ePub
26+
# formats:
27+
# - pdf
28+
# - epub
29+
30+
# Optional but recommended, declare the Python requirements required
31+
# to build your documentation
32+
# See https://docs.readthedocs.io/en/stable/guides/reproducible-builds.html
33+
python:
34+
install:
35+
- requirements: docs/requirements.txt

CHANGELOG.rst

+37-12
Original file line numberDiff line numberDiff line change
@@ -14,16 +14,6 @@ adheres to `Semantic Versioning <http://semver.org/spec/v2.0.0.html>`_.
1414
Added
1515
~~~~~
1616

17-
- Simulation with measured directivity responses in SOFA format (limited file types) is
18-
possible with the image source model.
19-
- New implementation of fast RIR builder function in the ``libroom`` C++
20-
extentsion to replace the current cython code. Advantages are: 1) only one
21-
compiled extension, 2) multithreading support
22-
- New global parameter ``sinc_lut_granularity`` that controls the number of points used
23-
in the look-up table for the sinc interpolation. Accessible via ``parameters.constants.get``.
24-
- New global parameter ``num_threads`` that controls the number of threads used in
25-
multi-threaded code (rir builder only at the moment). The number of threads can also
26-
be controlled via the environement variable ``PRA_NUM_THREADS``
2717
- New global parameters to control the octave bands used for simulation.
2818
* ``octave_bands_base_freq``: the base frequency used for the octave bands (default ``125 ``),
2919
note that together with the sampling frequency this will determine the number of sub-bands
@@ -37,15 +27,49 @@ Added
3727
Changed
3828
~~~~~~~
3929

40-
- Removed the broken ``get_rir`` method of the class ``SoundSource``
4130
- In ray tracing, the histograms are now linearly interpolated between
4231
the bins to obtain smoother RIR
4332

33+
`0.7.4`_ - 2024-04-25
34+
---------------------
35+
36+
Added
37+
~~~~~
38+
39+
- Simulation with measured directivity responses in SOFA format (limited file types) is
40+
possible with the image source model.
41+
- New implementation of fast RIR builder function in the ``libroom`` C++
42+
extension to replace the current cython code. Advantages are: 1) only one
43+
compiled extension, 2) multithreading support
44+
- New global parameter ``sinc_lut_granularity`` that controls the number of
45+
points used in the look-up table for the sinc interpolation. Accessible via
46+
``parameters.constants.get``.
47+
- New global parameter ``num_threads`` that controls the number of threads
48+
used in multi-threaded code (rir builder only at the moment). The number of
49+
threads can also be controlled via the environement variable
50+
``PRA_NUM_THREADS``
51+
- Adds package build support for Python 3.11 and 3.12. - Adds package build for
52+
new Apple M1 architecture
53+
54+
Changed
55+
~~~~~~~
56+
57+
- Removed the broken ``get_rir`` method of the class ``SoundSource``
58+
- Removes package build support for Python 3.7 (EOL)
59+
4460
Bugfix
4561
~~~~~~
4662

4763
- Fixes a bug when using randomized image source model with a 2D room (#315) by
4864
@hrosseel
65+
- Fixes a bug when setting the air absorption coefficients to custom values (#191),
66+
adds a test, and more details in the doc
67+
- Fixes a bug in the utilities.angle_function in the calculation of the
68+
colatitude (#329) by @fabiodimarco
69+
- Replaces the crossing-based point-in-polygon algorithm in the C++ code with
70+
the more robust winding number algorithm (#345)
71+
- Fixes usage of deprecated hann window with new version of scipy in
72+
`metrics.py` (#344) by @mattpitkin
4973

5074
`0.7.3`_ - 2022-12-05
5175
---------------------
@@ -551,7 +575,8 @@ Changed
551575
``pyroomacoustics.datasets.timit``
552576

553577

554-
.. _Unreleased: https://github.com/LCAV/pyroomacoustics/compare/v0.7.3...master
578+
.. _Unreleased: https://github.com/LCAV/pyroomacoustics/compare/v0.7.4...master
579+
.. _0.7.4: https://github.com/LCAV/pyroomacoustics/compare/v0.7.3...v0.7.4
555580
.. _0.7.3: https://github.com/LCAV/pyroomacoustics/compare/v0.7.2...v0.7.3
556581
.. _0.7.2: https://github.com/LCAV/pyroomacoustics/compare/v0.7.1...v0.7.2
557582
.. _0.7.1: https://github.com/LCAV/pyroomacoustics/compare/v0.7.0...v0.7.1

README.rst

+5-3
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,9 @@
11
.. image:: https://github.com/LCAV/pyroomacoustics/raw/master/logo/pyroomacoustics_logo_horizontal.png
2-
:scale: 80 %
32
:alt: Pyroomacoustics logo
43
:align: left
54

65
------------------------------------------------------------------------------
76

8-
.. image:: https://travis-ci.org/LCAV/pyroomacoustics.svg?branch=pypi-release
9-
:target: https://travis-ci.org/LCAV/pyroomacoustics
107
.. image:: https://readthedocs.org/projects/pyroomacoustics/badge/?version=pypi-release
118
:target: http://pyroomacoustics.readthedocs.io/en/pypi-release/
129
:alt: Documentation Status
@@ -218,6 +215,11 @@ A comprehensive set of examples covering most of the functionalities
218215
of the package can be found in the ``examples`` folder of the `GitHub
219216
repository <https://github.com/LCAV/pyroomacoustics/tree/master/examples>`_.
220217

218+
A `video introduction to pyroomacoustics <https://www.youtube.com/watch?v=c3DTtc--_F4>`_.
219+
220+
A `video tutorial series on using pyroomacoustics <https://youtube.com/playlist?list=PL6QnpHKwdPYgxLV_Ijr6K_3Gdyfhk0SHg&si=gsSuUm9Yw2_sjYhr>`_
221+
covering many advanced topics.
222+
221223
Authors
222224
-------
223225

docs/pyroomacoustics.bss.rst

+1-1
Original file line numberDiff line numberDiff line change
@@ -20,4 +20,4 @@ Algorithms
2020
pyroomacoustics.bss.sparseauxiva
2121
pyroomacoustics.bss.common
2222
pyroomacoustics.bss.fastmnmf
23-
pytoomacoustics.bss.fastmnmf2
23+
pyroomacoustics.bss.fastmnmf2

docs/pyroomacoustics.doa.normmusic.rst

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
pyroomacoustics.doa.normmusic module
2-
====================================
1+
NormMUSIC
2+
=========
33

44
.. automodule:: pyroomacoustics.doa.normmusic
55
:members:

docs/pyroomacoustics.doa.rst

+1
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ Algorithms
1717
pyroomacoustics.doa.cssm
1818
pyroomacoustics.doa.frida
1919
pyroomacoustics.doa.music
20+
pyroomacoustics.doa.normmusic.rst
2021
pyroomacoustics.doa.srp
2122
pyroomacoustics.doa.tops
2223
pyroomacoustics.doa.waves

pyroomacoustics/libroom_src/geometry.cpp

+60-57
Original file line numberDiff line numberDiff line change
@@ -233,20 +233,55 @@ Eigen::Vector3f cross(Eigen::Vector3f v1, Eigen::Vector3f v2)
233233
return v1.cross(v2);
234234
}
235235

236+
bool on_segment(const Eigen::Vector2f &c1, const Eigen::Vector2f &c2, const Eigen::Vector2f &p) {
237+
// Given three collinear points c1, c2, p, the function checks if
238+
// point p lies on line segment c1 <-> c2
239+
240+
float x_down, x_up, y_down, y_up;
241+
x_down = fminf(c1.coeff(0), c2.coeff(0));
242+
x_up = fmaxf(c1.coeff(0), c2.coeff(0));
243+
y_down = fminf(c1.coeff(1), c2.coeff(1));
244+
y_up = fmaxf(c1.coeff(1), c2.coeff(1));
245+
return (x_down <= p.coeff(0) && p.coeff(0) <= x_up && y_down <= p.coeff(1) && p.coeff(1) <= y_up);
246+
}
236247

237-
int is_inside_2d_polygon(const Eigen::Vector2f &p,
248+
inline int is_left(const Eigen::Vector2f &P0, const Eigen::Vector2f &P1, const Eigen::Vector2f &P2)
249+
{
250+
// isLeft(): tests if a point is Left|On|Right of an infinite line.
251+
// on the line is defined as within epsilon
252+
// Input: three points P0, P1, and P2
253+
// Return: >0 for P2 left of the line through P0 and P1
254+
// =0 for P2 on the line
255+
// <0 for P2 right of the line
256+
// See: Algorithm 1 "Area of Triangles and Polygons"
257+
float test_value = (
258+
(P1.coeff(0) - P0.coeff(0)) * (P2.coeff(1) - P0.coeff(1))
259+
- (P2.coeff(0) - P0.coeff(0)) * (P1.coeff(1) - P0.coeff(1))
260+
);
261+
262+
if (fabsf(test_value) < libroom_eps)
263+
return 0;
264+
else if (test_value > 0)
265+
return 1;
266+
else
267+
return -1;
268+
}
269+
270+
int is_inside_2d_polygon(const Eigen::Vector2f &p_test,
238271
const Eigen::Matrix<float,2,Eigen::Dynamic> &corners)
239272
{
240273
/*
241-
Checks if a given point is inside a given polygon in 2D.
274+
Checks if a given point is inside a given polygon in 2D using the winding
275+
number method.
242276
243277
This function checks if a point (defined by its coordinates) is inside
244278
a polygon (defined by an array of coordinates of its corners) by counting
245-
the number of intersections between the borders and a segment linking
246-
the given point with a computed point outside the polygon.
247-
A boolean is also returned to indicate if a point is on a border of the
248-
polygon (the point is still considered inside), which can be useful for
249-
limit cases computations.
279+
the number of left intersections between the edges and a half-line starting from
280+
the test point.
281+
282+
This is Dave Sunday's winding number algorithm described here:
283+
http://profs.ic.uff.br/~anselmo/cursos/CGI/slidesNovos/Inclusion%20of%20a%20Point%20in%20a%20Polygon.pdf
284+
It was modified to explicitely check for points on the edges of the polygon.
250285
251286
p: (array size 2) coordinates of the point
252287
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,
257292
0 : the point is inside
258293
1 : the point is on the boundary
259294
*/
260-
261-
bool is_inside = false; // initialize point not in the polygon
262-
int c1c2p, c1c2p0, pp0c1, pp0c2;
263295
int n_corners = corners.cols();
264-
265-
// find a point outside the polygon
266-
int i_min;
267-
corners.row(0).minCoeff(&i_min);
268-
Eigen::Vector2f p_out;
269-
p_out.resize(2);
270-
p_out.coeffRef(0) = corners.coeff(0,i_min) - 1;
271-
p_out.coeffRef(1) = p.coeff(1);
272-
273-
// Now count intersections
296+
int wn = 0; // the winding number counter
297+
// loop through all edges of the polygon
274298
for (int i = 0, j = n_corners-1 ; i < n_corners ; j=i++)
275299
{
276-
277-
// Check first if the point is on the segment
278-
// We count the border as inside the polygon
279-
c1c2p = ccw3p(corners.col(i), corners.col(j), p);
280-
if (c1c2p == 0)
300+
if (ccw3p(corners.col(j), corners.col(i), p_test) == 0 && on_segment(corners.col(j), corners.col(i), p_test))
281301
{
282-
// Here we know that p is co-linear with the two corners
283-
float x_down, x_up, y_down, y_up;
284-
x_down = fminf(corners.coeff(0,i), corners.coeff(0,j));
285-
x_up = fmaxf(corners.coeff(0,i), corners.coeff(0,j));
286-
y_down = fminf(corners.coeff(1,i), corners.coeff(1,j));
287-
y_up = fmaxf(corners.coeff(1,i), corners.coeff(1,j));
288-
if (x_down <= p.coeff(0) && p.coeff(0) <= x_up && y_down <= p.coeff(1) && p.coeff(1) <= y_up)
289-
return 1;
302+
// point is on the edge, we consider it inside
303+
return 1;
290304
}
291-
292-
// Now check intersection with standard method
293-
c1c2p0 = ccw3p(corners.col(i), corners.col(j), p_out);
294-
if (c1c2p == c1c2p0) // no intersection
295-
continue;
296-
297-
pp0c1 = ccw3p(p, p_out, corners.col(i));
298-
pp0c2 = ccw3p(p, p_out, corners.col(j));
299-
if (pp0c1 == pp0c2) // no intersection
300-
continue;
301-
302-
// at this point we are sure there is an intersection
303-
304-
// the second condition takes care of horizontal edges and intersection on vertex
305-
float c_max = fmaxf(corners.coeff(1,i), corners.coeff(1,j));
306-
if (p.coeff(1) + libroom_eps < c_max)
307-
{
308-
is_inside = !is_inside;
305+
if (corners.coeff(1,j) <= p_test.coeff(1)) { // start y <= P.y
306+
if (corners.coeff(1,i) > p_test.coeff(1)) { // an upward crossing
307+
if (is_left(corners.col(j), corners.col(i), p_test) > 0) // P left of edge
308+
++wn; // have a valid up intersect
309+
}
310+
} else { // start y > P.y (no test needed)
311+
if (corners.coeff(1, i) <= p_test.coeff(1)) { // a downward crossing
312+
if (is_left(corners.col(j), corners.col(i), p_test) < 0) // P right of edge
313+
--wn; // have a valid down intersect
314+
}
309315
}
310-
311316
}
312317

313-
// for a odd number of intersections, the point is in the polygon
314-
if (is_inside)
315-
return 0; // point strictly inside
318+
if (wn == 0)
319+
return -1;
316320
else
317-
return -1; // point is outside
321+
return 0;
318322
}
319323

320-
321324
float area_2d_polygon(const Eigen::Matrix<float, 2, Eigen::Dynamic> &corners)
322325
{
323326
/*

pyroomacoustics/libroom_src/libroom.cpp

+3-6
Original file line numberDiff line numberDiff line change
@@ -282,10 +282,7 @@ PYBIND11_MODULE(libroom, m) {
282282
m.def("dist_line_point", &dist_line_point,
283283
"Computes the distance between a point and an infinite line");
284284

285-
m.def("rir_builder", &rir_builder, "RIR builder",
286-
py::call_guard<py::gil_scoped_release>());
287-
m.def("delay_sum", &delay_sum, "Delay and sum",
288-
py::call_guard<py::gil_scoped_release>());
289-
m.def("fractional_delay", &fractional_delay, "Fractional delays",
290-
py::call_guard<py::gil_scoped_release>());
285+
m.def("rir_builder", &rir_builder, "RIR builder");
286+
m.def("delay_sum", &delay_sum, "Delay and sum");
287+
m.def("fractional_delay", &fractional_delay, "Fractional delays");
291288
}

0 commit comments

Comments
 (0)