audiblelight.worldstate.WorldStateRLR#
- class audiblelight.worldstate.WorldStateRLR(mesh, sample_rate=44100, empty_space_around_mic=0.1, empty_space_around_emitter=0.2, empty_space_around_surface=0.2, empty_space_around_capsule=0.05, add_to_context=True, ensure_minimum_weighted_average_ray_length=False, minimum_weighted_average_ray_length=3.0, repair_threshold=None, waypoints_json=None, material=None, rlr_kwargs=None)#
Bases:
WorldStateA WorldState where audio propagation is simulated inside a 3D-scanned mesh using acoustic ray-tracing.
- Parameters:
mesh (str | Path)
sample_rate (int | float | complex | integer | floating | None)
empty_space_around_mic (int | float | complex | integer | floating | None)
empty_space_around_emitter (int | float | complex | integer | floating | None)
empty_space_around_surface (int | float | complex | integer | floating | None)
empty_space_around_capsule (int | float | complex | integer | floating | None)
add_to_context (bool | None)
ensure_minimum_weighted_average_ray_length (bool | None)
minimum_weighted_average_ray_length (int | float | complex | integer | floating | None)
repair_threshold (int | float | complex | integer | floating | None)
waypoints_json (str | Path | None)
material (str | None)
rlr_kwargs (dict | None)
- mesh#
The path to the mesh on the disk.
- Type:
str, Path
- microphones#
Position of the microphone in the mesh.
- Type:
np.array
- ctx#
The context for audio propagation simulation.
- Type:
rlr_audio_propagation.Context
- emitters#
relative positions of sound emitter
- Type:
np.array
- __init__(mesh, sample_rate=44100, empty_space_around_mic=0.1, empty_space_around_emitter=0.2, empty_space_around_surface=0.2, empty_space_around_capsule=0.05, add_to_context=True, ensure_minimum_weighted_average_ray_length=False, minimum_weighted_average_ray_length=3.0, repair_threshold=None, waypoints_json=None, material=None, rlr_kwargs=None)#
Initializes the WorldState with a mesh and sets up the audio context.
- Parameters:
mesh (str|Path) – The name of the mesh file. Units will be coerced to meters when loading
sample_rate (Numeric) – the sample rate to use in the RLR engine
empty_space_around_mic (float) – minimum meters new emitters/mics will be placed from center of other mics
empty_space_around_emitter (float) – minimum meters new emitters/mics will be placed from other emitters
empty_space_around_surface (float) – minimum meters new emitters/mics will be placed from mesh emitters
empty_space_around_capsule (float) – minimum meters new emitters/mics will be placed from mic capsules
add_to_context (bool) – if False, the ray-tracing context will ONLY be updated when running WorldState.simulate. This is ideal in large-scale data generation pipelines. If True, the state will be updated every time a new Microphone or Emitter is added. This is ideal for interactive use.
ensure_minimum_weighted_average_ray_length (bool) – if True, random points can only be sampled from within the mesh when they have a weighted average ray length of at least minimum_weighted_average_ray_length
minimum_weighted_average_ray_length (float) – value to consider when locating points in the mesh; only evaluated when ensure_minimum_weighted_average_ray_length is True
repair_threshold (float, optional) – when the proportion of broken faces on the mesh is below this value, repair the mesh and fill holes. If None, will never repair the mesh.
waypoints_json (str|Path, optional) – path pointing towards a JSON list containing waypoints for this mesh. If not provided, will attempt to infer from the filename and set to None if cannot be found.
material (str) – the name of a material to use, defaults to None (i.e., Default material)
rlr_kwargs (dict, optional) – additional keyword arguments to pass to the RLR audio propagation library.
Methods
__init__(mesh[, sample_rate, ...])Initializes the WorldState with a mesh and sets up the audio context.
add_emitter([position, alias, mic, ...])Add an emitter to the state.
add_emitters([positions, aliases, mics, ...])Add emitters to the mesh.
add_microphone([microphone_type, position, ...])Add a microphone to the space.
add_microphone_and_emitter([position, ...])Add both a microphone and emitter with specified relationship.
add_microphones([microphone_types, ...])Add multiple microphones to the mesh.
Estimate how spatially "open" a point is by computing the weighted average length of rays cast from that point.
clear_emitter(alias)Given an alias for an emitter, clear that emitter and update the state.
clear_emitters()Removes all current emitters.
clear_microphone(alias)Given an alias for a microphone, clear that microphone if it exists and update the state.
clear_microphones()Removes all current microphones.
Creates a matplotlib.Figure object corresponding to top-down and side-views of the scene
create_scene([mic_radius, emitter_radius])Creates a trimesh.Scene with the Space's mesh, microphone position, and emitters all added
define_trajectory(duration[, ...])Defines a trajectory for a moving sound event with specified spatial bounds and event duration.
from_dict(input_dict)Instantiate a WorldStateRLR from a dictionary.
get_emitter(alias[, emitter_idx])Given a valid alias and index, get a single Emitter object, as in self.emitters[alias][emitter_idx]
get_emitters(alias)Given a valid alias, get a list of associated Emitter objects, as in self.emitters[alias]
get_irs()Get the IRs from the ray-tracing context
get_microphone(alias)Given a valid alias, get an associated Microphone object, as in self.microphones[alias].
get_microphones()Get all microphones associated with the current State
get_random_point_inside_mesh([batch_size])Generates a random valid point inside the mesh.
Get a valid position to place an object inside the state
Generate a sphere with origin ref and radius r and sample a valid position from within its volume.
load_mesh_navigation_waypoints([waypoints_json])Load the navigation waypoints for this mesh from a JSON file.
path_exists_between_points(point_a, point_b)Returns True if a direct point exists between point_a and point_b in the mesh, False otherwise.
simulate()Simulates audio propagation in the state with the current listener and sound emitter positions.
to_dict()Returns metadata for this object as a dictionary
Attributes
irs(N_capsules, N_emitters, N_samples), mic001: (...)}
num_emittersReturns the number of emitters in the state.
- __str__()#
Returns a string representation of the WorldState
- Return type:
str
- add_emitter(position=None, alias=None, mic=None, keep_existing=False, ensure_direct_path=False, max_place_attempts=1000)#
Add an emitter to the state.
If position is provided, it must be in absolute (cartesian) terms. If mic is a key inside microphones, position is assumed to be relative to that microphone.
- Parameters:
position (list | ndarray | None) – Location to add the emitter, defaults to a random, valid location.
alias (str | None) – String reference to access the emitter inside the self.emitters dictionary.
mic (str | None) – String reference to a microphone inside self.microphones; when provided, position is interpreted as RELATIVE to the center of this microphone
keep_existing (optional) – Whether to keep existing emitters from the mesh or remove, defaults to keep
ensure_direct_path (bool | list | str | None) – Whether to ensure a direct line exists between the emitter and given microphone(s). If True, will ensure a direct line exists between the emitter and ALL microphone objects. If a list of strings, these should correspond to microphone aliases inside microphones; a direct line will be ensured with all of these microphones. If False, no direct line is required for an emitter.
max_place_attempts (Numeric) – the number of times to try and create the trajectory.
- Return type:
None
Examples
Create a state with a given mesh and add a microphone >>> spa = WorldStateRLR(mesh=…) >>> spa.add_microphone(alias=”tester”)
Add a single emitter with a random position >>> spa.add_emitter() >>> spa.get_emitter(“src000”) # access with default alias
Add emitter with given position and alias >>> spa.add_emitter(position=[0.5, 0.5, 0.5], alias=”custom”) >>> spa.get_emitter(“custom”) # access using given alias
Add emitter relative to microphone >>> spa.add_emitter(position=[0.1, 0.1, 0.1], alias=”custom”, mic=”tester”) >>> spa.get_emitter(“custom”)
Add emitter with a random position that is in a direct line with the microphone we placed above >>> spa.add_emitter(ensure_direct_path=”tester”)
- add_emitters(positions=None, aliases=None, mics=None, n_emitters=None, keep_existing=False, ensure_direct_path=False, raise_on_error=True)#
Add emitters to the mesh.
This function essentially takes in lists of the arguments expected by add_emitters. The raise_on_error command will skip over microphones that cannot be placed in the mesh and raise a warning in the console.
Additionally, n_emitters can be provided instead of positions to choose a number of emitters to add randomly.
- Parameters:
positions (list | ndarray | None) – Locations to add the emitters, defaults to a single random location.
aliases (list[str] | None) – String references to assign the emitters inside the emitters dictionary.
mics (list[str] | str | None) – String references to microphones inside the microphones dictionary.
keep_existing (optional) – whether to keep existing emitters from the mesh or remove, defaults to keep.
raise_on_error (optional) – if True, raises an error when unable to place emitter, otherwise skips to next.
n_emitters (int | None) – Number of emitters to add with random positions
ensure_direct_path (bool | list | str | None) – Whether to ensure a direct line exists between the emitter and given microphone(s). If True, will ensure a direct line exists between the emitter and ALL microphone objects. If a list of strings, these should correspond to microphone aliases inside microphones; a direct line will be ensured with all of these microphones. If False, no direct line is required for a emitter.
- Return type:
None
- add_microphone(microphone_type=None, position=None, alias=None, keep_existing=True)#
Add a microphone to the space.
- Parameters:
microphone_type (str | Type[MicArray] | None) – Type of microphone to add, defaults to a mono capsule.
position (list | ndarray | None) – Location to add the microphone in absolute cartesian units, defaults to a random, valid location.
alias (str | None) – String reference to access the microphone inside the self.microphones dictionary.
keep_existing (optional) – whether to keep existing microphones from the mesh or remove, defaults to keep
- Return type:
None
Examples
Create a state from a given mesh >>> spa = WorldStateRLR(mesh=…)
Add a AmbeoVR microphone with a random position and default alias >>> spa.add_microphone(“ambeovr”) >>> spa.microphones[“mic000”] # access with default alias
Alternative, using MicArray objects >>> from audiblelight.micarrays import AmbeoVR >>> spa.add_microphone(AmbeoVR)
Add AmbeoVR with given position and alias >>> spa.add_microphone(microphone_type=”ambeovr”, position=[0.5, 0.5, 0.5], alias=”ambeo”) >>> spa.microphones[“ambeo”] # access using given alias
- add_microphone_and_emitter(position=None, polar=True, microphone_type=None, mic_alias=None, emitter_alias=None, keep_existing_mics=True, keep_existing_emitters=True, ensure_direct_path=True, max_place_attempts=1000)#
Add both a microphone and emitter with specified relationship.
The microphone will be placed in a random, valid position. The emitter will then be placed relative to the microphone, either in Cartesian or spherical coordinates.
- Parameters:
position (np.ndarray) – Array of form [X, Y, Z]
polar (bool | None) – whether the coordinates are provided in spherical form. If True: - Azimuth (X) must be between 0 and 360 - Elevation (Y) must be between -90 and 90 - Radius (Z) must be a positive value, measured in the same units given by the mesh.
microphone_type (str | Type[MicArray] | None) – Type of microphone to add, defaults to mono capsule
mic_alias (str | None) – String reference for the microphone, auto-generated if None
emitter_alias (str | None) – String reference for the emitter, auto-generated if None
keep_existing_mics (bool | None) – Whether to keep existing microphones, defaults to True
keep_existing_emitters (bool | None) – Whether to keep existing emitters, defaults to True
ensure_direct_path (bool | None) – Whether to ensure line-of-sight between mic and emitter
max_place_attempts (int | None) – The number of times to try placing the microphone and emitter
- Raises:
ValueError – If unable to place microphone and emitter within the mesh
- Return type:
None
Examples
# Create a state with a given mesh >>> spa = WorldStateRLR(mesh=…)
# Place emitter 2 meters in front of microphone >>> spa.add_microphone_and_emitter(np.array([0, 0, 2.0]))
# Place emitter 1.5 meters to the left and slightly above >>> spa.add_microphone_and_emitter(np.array([90, 30, 1.5]), mic_alias=”main_mic”, emitter_alias=”left_source”)
# Place emitter behind and below >>> spa.add_microphone_and_emitter(np.array([180, -45, 1.0]))
- add_microphones(microphone_types=None, positions=None, aliases=None, keep_existing=True, raise_on_error=True)#
Add multiple microphones to the mesh.
This function essentially takes in lists of the arguments expected by add_microphone. The raise_on_error command will skip over microphones that cannot be placed in the mesh and raise a warning in the console.
- Parameters:
microphone_types (list[str | Type[MicArray]] | None) – Types of microphones to add, defaults to a single mono capsule.
positions (list[list | ndarray] | None) – Locations to add the microphones in absolute cartesian units, defaults to a single location.
aliases (list[str] | None) – String references to access the microphones inside the self.microphones dictionary.
keep_existing (optional) – whether to keep existing microphones from the mesh or remove, defaults to keep
raise_on_error (optional) – if True, will raise an error when unable to place a mic, otherwise skips to next
- Return type:
None
Examples
Create a state with a given mesh >>> spa = WorldStateRLR(mesh=…)
Add some AmbeoVRs with random positions >>> spa.add_microphones(microphone_types=[“ambeovr”, “ambeovr”, “ambeovr”]) >>> spa.microphones[“mic002”] # access with default alias
Add AmbeoVR and Eigenmike32 with given positions and aliases >>> spa.add_microphones( >>> microphone_types=[“ambeovr”, “eigenmike32”], >>> positions=[[0.5, 0.5, 0.5], [0.1, 0.1, 0.1]], >>> alias=[“ambeo”, “eigen”], >>> keep_existing=False, # removes microphones already added to the space >>> raise_on_error=True, # raises an error if any microphone cannot be placed >>> ) >>> spa.microphones[“eigen”] # access using given alias
- calculate_weighted_average_ray_length(point, num_rays=100)#
Estimate how spatially “open” a point is by computing the weighted average length of rays cast from that point.
Rays are emitted uniformly from the given point across 3D space. Each ray is traced until it intersects with the mesh surface. The distances of these intersections are squared and used as weights to calculate a weighted average, emphasising longer, unobstructed paths. This can be used as a heuristic for how suitable a point is within a mesh (e.g., avoiding corners)
If any rays fail to intersect the mesh (due to holes or open surfaces), those rays are ignored, and a warning is logged.
- Parameters:
point (np.ndarray) – a 3D coordinate (shape: (3,)) representing the origin of the rays.
num_rays (int) – number of random rays to cast from the point (default is 100).
- Returns:
The weighted average distance of ray intersections: higher values indicate more open surroundings
- Return type:
float
- create_plot()#
Creates a matplotlib.Figure object corresponding to top-down and side-views of the scene
- Returns:
The rendered figure that can be shown with e.g. plt.show()
- Return type:
plt.Figure
- create_scene(mic_radius=0.2, emitter_radius=0.1)#
Creates a trimesh.Scene with the Space’s mesh, microphone position, and emitters all added
- Returns:
The rendered scene, that can be shown in e.g. a notebook with the .show() command
- Return type:
trimesh.Scene
- Parameters:
mic_radius (int | float | complex | integer | floating | None)
emitter_radius (int | float | complex | integer | floating | None)
- define_trajectory(duration, starting_position=None, velocity=0.75, resolution=1.5, shape=None, max_place_attempts=1000, ensure_direct_path=False)#
Defines a trajectory for a moving sound event with specified spatial bounds and event duration.
This method calculates a series of XYZ coordinates that outline the path of a sound event, based on the specified trajectory shape, the confines of the mesh, and the duration of the event. It generates a starting point and an end point that comply with these conditions, and then interpolates between these points according to the trajectory’s shape.
Optionally, a custom starting position can also be provided, and a valid ending position will be randomly sampled. To provide a custom ENDING position, one possibility is to provide this as the starting position, then invert the trajectory array returned by this function. To use both a custom start AND end position, call the trajectory functions in utils.py directly.
- Parameters:
duration (Numeric) – the length of time it should take to traverse from starting to ending position
starting_position (np.ndarray) – the starting position for the trajectory. If not provided, a random valid position within the mesh will be selected.
velocity (Numeric) – the speed limit for the trajectory, in meters per second
resolution (Numeric) – the number of emitters created per second
shape (str) – the shape of the trajectory; “linear”, “semicircular”, “random”, “sawtooth”, “sine”
max_place_attempts (Numeric) – the number of times to try and create the trajectory.
ensure_direct_path (bool | list | str | None) – Whether to ensure a direct line exists between the emitter and given microphone(s). If True, will ensure a direct line exists between the emitter and ALL microphone objects. If a list of strings, these should correspond to microphone aliases inside microphones; a direct line will be ensured with all of these microphones. If False, no direct line is required for a emitter.
- Raises:
ValueError – if a trajectory cannot be defined after max_place_attempts
- Returns:
the sanitised trajectory, with shape (n_points, 3)
- Return type:
np.ndarray
- classmethod from_dict(input_dict)#
Instantiate a WorldStateRLR from a dictionary.
- Parameters:
input_dict (dict[str, Any]) – Dictionary that will be used to instantiate the WorldStateRLR.
- Returns:
WorldStateRLR instance.
- get_irs()#
Get the IRs from the ray-tracing context
By default, Context.get_audio expects all listeners to have the same channel layout type. If, however, e.g. some listeners have Mono and others have Ambisonics, Context.get_audio will fail due to numpy expecting all arrays to have equal dims.
Instead, we need to return a dictionary of numpy arrays. The output will have shape: {mic1: (N_capsules, N_emitters, N_samples), …}
- Return type:
OrderedDict[str, ndarray]
- get_random_point_inside_mesh(batch_size=10)#
Generates a random valid point inside the mesh.
N positions will be generated in batches and a whole batch will be checked at once to take advantage of numpy vectorisation. In initial experiments, using a batch size of 10 resulted in a speed-up of 1.5x over a batch size of 1 (i.e., no batching). Improvements level off and decrease after, with a batch size of 100 resulting in 2x worse performance than no batching.
- Parameters:
batch_size (int) – number of points to generate in a single batch, defaults to 10
- Returns:
A valid point within the mesh in XYZ format
- Return type:
np.array
- get_valid_position()#
Get a valid position to place an object inside the state
If ensure_minimum_weighted_average_ray_length is enabled, this function attempts to find a position that meets the minimum openness criteria, measured by the weighted average ray length from the candidate point. It will try up to MAX_PLACE_ATTEMPTS times before returning the last attempted position.
- Returns:
the random position to place an object inside the mesh
- Return type:
np.ndarray
- get_valid_position_with_max_distance(ref, r, n=1000)#
Generate a sphere with origin ref and radius r and sample a valid position from within its volume.
- Parameters:
ref (np.ndarray) – the reference point, treated as the origin of the sphere
r (custom_types.Numeric) – the maximum distance for the sampled point from ref
n (custom_types.Numeric) – the number of points to create on the sphere. Only the first valid point will be returned
- Raises:
ValueError – if a valid point from within n samples cannot be found
- Return type:
ndarray
Load the navigation waypoints for this mesh from a JSON file.
Default filepath is <project-root>/resources/waypoints/gibson/<mesh-name>.json.
- Parameters:
waypoints_json (str | Path | None)
- Return type:
list[ndarray]
- name = 'RLR'#
- path_exists_between_points(point_a, point_b)#
Returns True if a direct point exists between point_a and point_b in the mesh, False otherwise.
- Parameters:
point_a (ndarray)
point_b (ndarray)
- Return type:
bool
- simulate()#
Simulates audio propagation in the state with the current listener and sound emitter positions.
Note that returned IRs are NOT NORMALIZED: this occurs inside synthesize.render_event_audio.
- Return type:
None
- to_dict()#
Returns metadata for this object as a dictionary
- Return type:
dict