Skip to content

Commit fb3c590

Browse files
author
corrooli
committed
README and streamlining
1 parent 0a2f07b commit fb3c590

File tree

5 files changed

+226
-33
lines changed

5 files changed

+226
-33
lines changed

README.md

+170-1
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,171 @@
1+
12
# pytARD
2-
(Python-) based ARD reverberation experimentation repository
3+
**pytARD** is a free and open source Python room impulse response generator using Adaptive Rectangular Decomposition (ARD) for auralization and visualization of wave distribution and reverberation inside rooms.
4+
5+
![pytARD Logo-modified](https://user-images.githubusercontent.com/61276147/156866082-d0380c16-df85-4a09-a2e0-a3d37db26dc4.jpg)
6+
## Prerequisites
7+
- **OS:** Developed and tested on GNU/Linux systems (Ubuntu 20.04 LTS), macOS (10.14 and 11.6) and Windows 10
8+
- **Python:** Python 3 required. Developed and tested on version 3.8.2 and 3.9.2. Older as well as newer versions should run with no problems.
9+
- **Required Python packages**: For core functionality, matplotlib 3.3.4, numpy 1.20.1, scipy 1.6.2 and 4.64.0 needs to be installed.
10+
## Installation
11+
You can use `git pull` to pull this repository on your hard drive, alternative download this repository as a .zip file.
12+
## License
13+
This software is subject to the [GNocchi Alfredo AGPL-3.0 license](https://www.gnu.org/licenses/agpl-3.0.en.html). This software comes with no warranty.
14+
## Acknowledgements
15+
If you find this software helpful, feel free to cite us.
16+
17+
> [1] Corrodi, O., Fürbringer & S., Smailov, N. pytARD: A free and open
18+
> source Python room impulse response generator using Adaptive
19+
> Rectangular Decomposition (ARD).
20+
21+
@thesis{pytard2022,
22+
author = {Fürbringer, Severin and Corrodi, Oliver and Smailov, Nikita},
23+
title = {GPU-based Time Domain Solver for Acoustic Wave Equation},
24+
school = {ZHAW School of Engineering},
25+
year = {2022},
26+
month = {July},
27+
type = {Bachelor's Thesis}
28+
}
29+
# Documentation
30+
pytARD comes with three different implementation to simulate 1D, 2D and 3D spaces.
31+
## SimulationParameters
32+
Data container for defining simulation parameters.
33+
```
34+
sim_param = SimulationParameters(
35+
max_simulation_frequency=250, # Highest frequency
36+
T=1, # Simulation duration
37+
spatial_samples_per_wave_length=6 # Discrete points per single wave length
38+
c=343, # Speed of sound
39+
Fs=8000, # Sample rate
40+
verbose=True # Console output
41+
visualize=True # Visualization of wave distribution
42+
)
43+
```
44+
## Impulses
45+
Impulses can be used as source signal, which is passed off into a certain partition.
46+
### Common parameters
47+
Following parameters are shared between all types of impulse.
48+
- `amplitude`: How loud the impulse is.
49+
- `impulse_location`: Position of the impulse. Numpy array with coordinates according to which dimensional implementation of pytARD was used, for example `impulse_location = np.array([[2], [2], [2])` for 3D.
50+
51+
### Impulse types
52+
There are three different types of impulses in pytARD.
53+
- **Unit impulse:** This is the standard impulse in RIR generators. `impulse = Unit(sim_param, impulse_location, amplitude)`
54+
- **Gaussian impulse:** `impulse = Gaussian(sim_param, impulse_location, amplitude)`
55+
- **Wave file:** `impulse = WaveFile(sim_param, impulse_location, 'path_to_file.wav', amplitude)`
56+
57+
## Partitions
58+
Partitions make up parts of the domain (the room to be simulated). Those partitions can be connected via interfaces to allow travel of sound waves from partition to partition.
59+
### Air Partitions
60+
An air partition resembles an empty space in which sound can travel through. Air partitions reflect acoustic waves indefinitely without loss in amplitude.
61+
#### 1D example
62+
```
63+
room_width = 5
64+
partition = AirPartition1D(room_width, sim_param, impulse)
65+
```
66+
#### 2D example
67+
```
68+
air_partition = AirPartition2D(
69+
np.array([[4.0], [4.0]]), # Room width and height
70+
sim_param, # SimulationParameters object
71+
impulse=impulse) # Impulse object
72+
)
73+
```
74+
#### 3D example
75+
```
76+
air_partition = AirPartition3D(np.array([
77+
[4], # X, width of partition
78+
[4], # Y, depth of partition
79+
[2] # Z, height of partition
80+
]),
81+
sim_param, # SimulationParameters object
82+
impulse=impulse # Impulse object
83+
)
84+
```
85+
### PML Partitions
86+
Perfectly Matched Layer (PML) partitions absorb sound energy depending on the damping profile and its reflection coefficient.
87+
```
88+
pml_partition = PMLPartition2D(
89+
np.array([[1.0], [4.0]]), # Partition width and height
90+
sim_param, # SimulationParameters object
91+
dp)
92+
)
93+
```
94+
#### Damping Profile
95+
Determines how intense the reflections of the PML partition are, or how much sound energy is absorbed. Be sure to pass the width of the partition as the first parameter.
96+
```
97+
room_width = 4
98+
reflection_coefficient = 1e-8
99+
dp = DampingProfile(room_width, c, reflection_coefficient)
100+
```
101+
## Interfaces
102+
Interfaces are used for connecting partitions with each other. Interfaces allow for the passing of sound waves between two partitions. To define interfaces, the helper class `Interface` is used.
103+
### Example
104+
It is required for all partitions to be collected in a `List` first. The indices of this list is referenced in the interface creation later.
105+
```
106+
domain = [
107+
air_partition_1, # Index 0
108+
air_partition_2, # Index 1
109+
... # Index n
110+
]
111+
```
112+
An interface is defined by referencing above mentioned List indices. To connect the first two air partition together, their `List` indices are passed as parameters. Since interfaces need a direction (or axis) to travel through, the third parameter is either `Direction.X`, `Direction.Y` or `Direction.Z`
113+
```
114+
interface_1_0 = Interface(1, 0, Direction.X)
115+
```
116+
## Microphone
117+
To auralize the simulation and create RIRs, virtual microphones are to be created and placed inside a partition within the domain. The `position` parameter needs to be adjusted to the according dimension and position.
118+
119+
Just like [interfaces](##Interfaces), the microphones needs to be mapped to the according partition `List` indices.
120+
```
121+
mic = Mic(
122+
0, # Parition number (partition list index)
123+
[ # Positioning of microphone.
124+
1, # X coordinate of partition
125+
1, # Y coordinate of partition
126+
1 # Z coordinate of partition
127+
],
128+
sim_param,
129+
"RIR" # Name of resulting wave file
130+
)
131+
```
132+
## ARDSimulator
133+
Room Impulse Responses (RIRs) simulation using the Adaptive Rectangular Decomposition (ARD). For further details see [\[1\]](#Acknowledgements).
134+
```
135+
sim = ARDSimulator3D( # Can also be 2D and 1D
136+
sim_param, # SimulationParameters object
137+
partitions, # List of Partition objects
138+
interface_data=interfaces, # List of Interface objects
139+
mics=mics # List of Microphone objects
140+
)
141+
sim.preprocessing() # Start preprocessing
142+
sim.simulation() # Start the simulation
143+
```
144+
## Plotter
145+
To visualize the wave distribution, a `Plotter` class is provided.
146+
147+
To ensure correct display of each partition of the domain, the `plot_structure` variable needs to be adjusted according to following graph:
148+
![99_komplexe_raumformen_plot](https://user-images.githubusercontent.com/61276147/172880366-7030c07c-f857-4e09-ad14-5f6304cb4651.jpg)
149+
To ensure correct representation of the setup above, `plot_structure` needs to be configured as following:
150+
```
151+
plot_structure = [
152+
# Structure: [Height of domain, width of domain, index of partition to plot on the graph]
153+
[2, 3, 1],
154+
[2, 3, 2],
155+
[2, 3, 3],
156+
[2, 3, 5]
157+
]
158+
```
159+
To start plotting, use following instructions:
160+
```
161+
plotter = Plotter()
162+
plotter.set_data_from_simulation(sim_param, partitions, mics, plot_structure)
163+
plotter.plot()
164+
```
165+
## Serializer
166+
As the ARD method is resource heavy, a means to save simulation data to disk, as well as post-generation visualization and auralization is provided. Be sure to call serializer after the simulation was completed fully. **Necessary parameters** are `SimulationParameters` and a `List` of `Partition` objects, **optional parameters** are a `List` of `Microphone` objects for auralization and `plot_structure` for visualization.
167+
```
168+
# Instantiation serializer for reading and writing simulation state data
169+
serializer = Serializer()
170+
serializer.dump(sim_param, partitions, mics, plot_structure)
171+
```

example_1D.py

+11-5
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
from pytARD_1D.ard import ARDSimulator1D
22
from pytARD_1D.partition import AirPartition1D
3-
from pytARD_1D.interface import InterfaceData1D
3+
from pytARD_1D.interface import InterfaceData1D as Interface
44

55
from common.parameters import SimulationParameters
66
from common.impulse import Gaussian, Unit, WaveFile
@@ -19,7 +19,7 @@
1919
spatial_samples_per_wave_length = 6
2020

2121
# Procedure parameters
22-
auralize = False
22+
auralize = True
2323
verbose = True
2424
visualize = True
2525

@@ -47,13 +47,13 @@
4747
partitions.append(AirPartition1D(np.array([c / 2]), sim_param))
4848

4949
interfaces = []
50-
interfaces.append(InterfaceData1D(0, 1))
50+
interfaces.append(Interface(0, 1))
5151

5252

5353
# Microphones. Add and remove microphones here by copying or deleting mic objects.
5454
# Only gets used if the auralization option is enabled.
55+
mics = []
5556
if auralize:
56-
mics = []
5757
mics.append(Mic(
5858
0, # Parition number
5959
# Position
@@ -64,7 +64,13 @@
6464

6565

6666
# Instantiating and executing simulation
67-
sim = ARDSimulator1D(sim_param, partitions, 1, interfaces)
67+
sim = ARDSimulator1D(
68+
sim_param,
69+
partitions,
70+
normalization_factor=1,
71+
interface_data=interfaces,
72+
mics=mics
73+
)
6874
sim.preprocessing()
6975
sim.simulation()
7076

example_2D.py

+18-10
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
from pytARD_2D.ard import ARDSimulator2D
22
from pytARD_2D.partition import AirPartition2D, PMLPartition2D, DampingProfile
3-
from pytARD_2D.interface import InterfaceData2D, Direction2D
3+
from pytARD_2D.interface import InterfaceData2D as Interface
4+
from pytARD_2D.interface import Direction2D as Direction
5+
46

57
from common.parameters import SimulationParameters
68
from common.impulse import Gaussian, Unit, WaveFile
@@ -59,21 +61,21 @@
5961

6062
# Interfaces of the room. Interfaces connect the partitions together
6163
interfaces = []
62-
interfaces.append(InterfaceData2D(1, 0, Direction2D.X))
63-
interfaces.append(InterfaceData2D(2, 0, Direction2D.X))
64-
interfaces.append(InterfaceData2D(3, 0, Direction2D.Y))
65-
interfaces.append(InterfaceData2D(4, 0, Direction2D.Y))
64+
interfaces.append(Interface(1, 0, Direction.X))
65+
interfaces.append(Interface(2, 0, Direction.X))
66+
interfaces.append(Interface(3, 0, Direction.Y))
67+
interfaces.append(Interface(4, 0, Direction.Y))
6668

6769
# Microphones. Add and remove microphones here by copying or deleting mic objects.
6870
# Only gets used if the auralization option is enabled.
69-
if auralize:
70-
mics = []
71+
mics = []
72+
if auralize:
7173
mics.append(Mic(
7274
0, # Parition number
7375
# Position
7476
[
75-
int(1),
76-
int(1)
77+
1,
78+
1
7779
],
7880
sim_param,
7981
# Name of resulting wave file
@@ -84,7 +86,13 @@
8486
serializer = Serializer()
8587

8688
# Instantiating and executing simulation
87-
sim = ARDSimulator2D(sim_param, partitions, 1, interfaces, mics)
89+
sim = ARDSimulator2D(
90+
sim_param,
91+
partitions,
92+
normalization_factor=1,
93+
interface_data=interfaces,
94+
mics=mics
95+
)
8896
sim.preprocessing()
8997
sim.simulation()
9098

example_3D.py

+21-17
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
from pytARD_3D.ard import ARDSimulator3D
22
from pytARD_3D.partition import AirPartition3D, PMLPartition3D, DampingProfile
3-
from pytARD_3D.interface import InterfaceData3D, Direction3D
3+
from pytARD_3D.interface import InterfaceData3D as Interface
4+
from pytARD_3D.interface import Direction3D as Direction
5+
46

57
from common.parameters import SimulationParameters
68
from common.impulse import Gaussian, Unit, WaveFile
@@ -36,45 +38,41 @@
3638
visualize=visualize
3739
)
3840

39-
SCALE = 80 # Scale of room. Gets calculated by speed of sound divided by SCALE
40-
4141
# Define impulse location
4242
impulse_location = np.array([
43-
[int((c / SCALE) / 2)], # X, width
44-
[int((c / SCALE) / 2)], # Y, depth
45-
[int((c / SCALE) / 2)] # Z, height
43+
[2], # X, width
44+
[2], # Y, depth
45+
[2] # Z, height
4646
])
4747

4848
# Define impulse location that gets emitted into the room
4949
# impulse = Gaussian(sim_param, impulse_location, 1)
5050
impulse = Unit(sim_param, impulse_location, 1, upper_frequency_limit - 1)
5151
# impulse = WaveFile(sim_param, impulse_location, 'clap.wav', 100) # Uncomment for wave file injection
5252

53-
room_width = int(c / SCALE)
54-
5553
# Damping profile with according Zetta value (how much is absorbed)
56-
dp = DampingProfile(room_width, c, 1e-3)
54+
dp = DampingProfile(4, c, 1e-3)
5755

5856
partitions = []
5957
# Paritions of the room. Can be 1..n. Add or remove partitions here.
6058
# This example is two air partitions connected by an interface.
6159
# Also, you may provide impulse in the partition(s) of your choosing
6260
# as the last, optinal parameter.
6361
partitions.append(AirPartition3D(np.array([
64-
[room_width], # X, width
65-
[room_width], # Y, depth
66-
[room_width] # Z, height
62+
[4], # X, width
63+
[4], # Y, depth
64+
[2] # Z, height
6765
]), sim_param, impulse=impulse))
6866

6967
partitions.append(AirPartition3D(np.array([
70-
[room_width], # X, width
71-
[room_width], # Y, depth
72-
[room_width] # Z, height
68+
[4], # X, width
69+
[4], # Y, depth
70+
[2] # Z, height
7371
]), sim_param))
7472

7573
# Interfaces of the room. Interfaces connect the room together
7674
interfaces = []
77-
interfaces.append(InterfaceData3D(0, 1, Direction3D.X))
75+
interfaces.append(Interface(0, 1, Direction.X))
7876

7977
# Initialize & position mics.
8078
mics = []
@@ -93,7 +91,13 @@
9391
serializer = Serializer()
9492

9593
# Instantiating and executing simulation (don't change this)
96-
sim = ARDSimulator3D(sim_param, partitions, 1, interfaces, mics)
94+
sim = ARDSimulator3D(
95+
sim_param,
96+
partitions,
97+
normalization_factor=1,
98+
interface_data=interfaces,
99+
mics=mics
100+
)
97101
sim.preprocessing()
98102
sim.simulation()
99103

requirements.txt

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
audioread==2.1.9
2+
graphviz==0.20
3+
matplotlib==3.3.4
4+
numpy==1.20.1
5+
scipy==1.6.2
6+
tqdm==4.64.0

0 commit comments

Comments
 (0)