Changelog¶
All notable changes to this project will be documented in this file.
The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.
Unreleased¶
Added¶
Changed¶
Removed¶
[0.1.0] 2026-11-05¶
Migration from the previous main¶
Nothing is kept under its old name. What replaces what:
| Before | Now |
|---|---|
Simulator(scene).rir_from_ray_tracing(image_source_order=, n_rays=, air_absorption=, random_seed=) |
scene.simulate(Mono(ray_tracing=True, image_source_order=, n_rays=, air_absorption=, seed=)) |
Simulator(scene).rir_from_image_source_method(order) |
scene.simulate(Mono(image_source_order=order, ray_tracing=False)) |
simulator.sampling_rate = fs |
Scene(name, sampling_rate=fs) or scene.sampling_rate = fs |
simulator.trace_rays(...) |
RayField.from_scene(scene, ...) |
Ray(..., sound_speed=), RayField(..., sound_speed=) |
removed; travel times are at Defaults().DEFAULT_SPEED_OF_SOUND |
compas_acoustics.ray, compas_acoustics.specular |
compas_acoustics.rays (or the classes from compas_acoustics) |
compas_acoustics.acoustic_material, compas_acoustics.materials_library |
compas_acoustics.materials (or the classes from compas_acoustics) |
compas_acoustics.geometry |
compas_acoustics.acoustic_object (or the classes from compas_acoustics) |
compas_acoustics.numerical.fitness |
compas_acoustics.fitness, or compas_acoustics.regularized_fitness |
compas_acoustics.pra_converter |
compas_acoustics.room.pra (no longer an attribute of compas_acoustics) |
JSON with "dtype": "compas_acoustics.<module>/<Class>" |
"dtype": "compas_acoustics/<Class>"; files saved before do not load |
simulator.rirs, simulator.results |
the responses returned by scene.simulate, or scene.impulse_responses(method_name), scene.get_impulse_response(source, receiver) |
Scene(simulation_platform=), ImpulseResponse(simulation_platform=) |
removed; ir.generation names the method |
AcousticObjectsGroup; scene.iter_acoustic_objects() |
scene.add_acoustic_objects(shoebox_room(...)) (a list); scene.acoustic_objects |
scene.acoustic_materials = {...} |
shoebox_room(materials=...), AcousticObject.from_obj(path, materials=...): a material per object |
AcousticAnalyser.compute_rt, compute_clarity_definition, compute_drr, compute_sti, compute_stage_support, compute_strength |
reverberation_times, clarity_definition, direct_to_reverberant_ratio, speech_transmission_index, stage_support, sound_strength |
stage_acoustics.compute_ST1, compute_ST2, compute_G_stage, compute_ITDG |
Analyser.stage_support() |
Auraliser.multiconvolution(), Auraliser.convolve(ir, sound) |
Auraliser(...).convolve(), in the format of the impulse responses |
Auraliser.decode_stereo_simple(), Auraliser.resample_signal() |
no decoder: binaural responses from Binaural(), ambisonic ones from Ambisonics() saved for a decoder elsewhere; pyfar.dsp.resample |
Auraliser(output_format=...) |
removed: the signal has the format of its impulse responses, Auraliser.format (mono, binaural or ambisonic) |
sample_rate (ImpulseResponse, Auraliser, AudioPlayer.play, receivers_to_pra_mic_array) |
sampling_rate |
pra_converter.material_to_pra, mesh_to_pra_wall, acoustic_object_to_pra_walls |
RoomGeometry.to_pra, compas_acoustics.room.pra.scene_to_pra_room |
Tools |
removed |
compas_acoustics.orchestra_layouts; the Orchestra Layout component |
removed |
AcousticAnalyser |
Analyser |
Auraliser, AcousticAnalyser, MaterialsLibrary as Data |
plain classes: no guid, copy() or compas.json_dumps |
Grasshopper: Simulate Ray Tracing, with a sample_rate input |
Simulate Mono; the rate is the sampling_rate input of Acoustic Scene |
| Grasshopper: Analyse IR output STI | no longer an output, with G: they need pyrato >= 1.0 |
Source direction default +z; shoebox_room(materials={'left_wall': ..., 'right_wall': ...}) with left_wall at y = 0 |
direction default +x, as the Grasshopper Source component has it; left_wall is at y = width and right_wall at y = 0, the sides of a receiver at the centre facing +x (swap the two keys to keep a room) |
scene.simulate(hybrid), then hybrid.binaural(hrtf) |
scene.simulate(Binaural(hrtf=...)), stored under 'binaural' |
Hybrid() returning ambisonic responses |
Ambisonics(sh_order=...), stored under 'ambisonics'; the same seed gives both methods the same late field |
synthesize(...), binaural_rir(result, hrtf, filters) |
core = prepare(...), then render_ambisonics(core, sh_order, normalization) or render_binaural(core, hrtf): each delay line through its own HRIR |
ir.generation["output"] |
ir.format ('mono', 'binaural' or 'ambisonic'), read from the channels |
shape_late(late, early, reference, fs) |
shape_late(signals, early_w, late_w, reference, fs) |
Grasshopper: Simulate Ray Tracing output ir_graph |
Simulate Mono output mono_ir_graph |
compas_acoustics.worker.proxy(), a compas.rpc server with COMPAS JSON and WAV files in temporary folders |
compas_acoustics.worker.client(), an HTTP service with compas_pb payloads |
Grasshopper: the hybrid's reference given as a method name on the scene |
the mono_ir_graph wire of Simulate Mono only |
Installer input worker (hybrid) |
not used: the installer always sets up the worker |
Grasshopper: Simulate Hybrid output ir_graph (ambisonic) |
Simulate Binaural (hrtf, output binaural_ir_graph) and Simulate Ambisonics (the SH order dropdown, output ambisonic_ir_graph) |
Grasshopper: Plot 2D with plane, x_size, y_size, settings and text, point and polyline outputs |
Plot 2D draws its charts on the canvas: input data (a list), no outputs |
| Grasshopper: Plot 2D Settings | removed, with Plot 2D's geometry |
| Grasshopper: Material Library's nested right-click menu | a picker dialog with search and a chart, opened from the body or "Choose material..." |
| Grasshopper: value lists and buttons placed next to new components | controls drawn on the component body |
Grasshopper: Analyse IR input time_scale |
right-click the time output: Time scale dB or linear |
Grasshopper: Auralise input level, Acoustic Object input role, Deconstruct Parameter input select |
the switch on the body only |
| Grasshopper: Room Scene | Acoustic Scene (nickname Scene), the same inputs and output |
Grasshopper: Receiver input plane |
removed: position and direction, the receiver upright (+z up) |
Grasshopper: Auralise inputs save, save_file_path, save_file_name |
the Save button on its body: a dialog chooses the folder and the name |
Grasshopper: the Directivity component; Source input directivity |
the directivity dropdown on Source |
Grasshopper: Audio Player input normalize (default True) |
the Norm toggle on its body, off by default |
Grasshopper: Audio Player inputs play, stop; Visualise Billiard output t |
the Play, Pause and Stop buttons on the body; removed |
Grasshopper: Audio Player input sampling_rate (unused) |
removed: a signal carries its rate |
Grasshopper: Visualise Billiard input speed in ms of sound per second |
a factor of the default (the whole field in 10 s), the Speed dropdown 0.25x to 4x |
| Grasshopper: Visualise Energy Map Play and Rewind | removed: the map shows the levels |
Grasshopper: ism_order value list 0 to 6 |
the ISM order slider, 0 to 10 |
Grasshopper: Simulate Ambisonics input sh_order (1 to 3) |
the SH order dropdown on its body, 1 to 7 |
Grasshopper: Acoustic Scene input sampling_rate |
the Sampling rate dropdown on its body (44.1, 48, 96 kHz) |
Grasshopper: Simulate Mono input time_thres |
duration, the length of the responses, as Mono(duration=) names it |
RayField.from_scene(time_thres=) |
RayField.from_scene(duration=) |
Grasshopper: Visualise Energy Map inputs n_rays, image_source_order, seed |
fixed: the rays from the room's volume, order 1, seed 42; the input spacing meshes the surface given |
The Grasshopper components are now a compiled plugin (CompasAcoustics.gha) instead of CPython script components: a definition saved
with the old components does not open with the new ones. Delete the old components in it and place the new ones from the toolbar; they have
the same names, inputs and outputs.
Added¶
- Added
Scene.simulate(method)as the one way to simulate, with the methodsMono(image_source_order=3, ray_tracing=True, ...),Binaural(...)andAmbisonics(...)incompas_acoustics.simulation_methods. - Added
Scene.sampling_rate(default 48000, serialised with the scene), the rate at which every method runs; the Acoustic Scene component has the input. - Added
BinauralandAmbisonics, binaural and ambisonic room impulse responses from one hybrid engine: image sources, optional edge diffraction (diffraction="udfa"or"btms") and a directional feedback delay network tail; needs Python >= 3.11.Binaural(hrtf=None)takes"kemar"(the default) or a SOFA file and records it ingeneration["hrtf"];Ambisonics(sh_order=1, normalization="SN3D")gives ACN channels. Responses are stored under'binaural'and'ambisonics'. - Added
tail=...toBinauralandAmbisonics, with a reference response, the method's own ray-traced reference, or an Eyring or Sabine prediction as decay target. - Added edge diffraction at the edges of the room's own boundary (re-entrant corners) to the hybrid engine.
- Added
scattering=FalsetoBinauralandAmbisonics, surface scattering after Ewert et al. (2025): each image's specular part, the product ofsqrt(1 - s)over its reflections, stays in the early field, and its scattered part, the product ofsqrt(s), enters the delay lines on its own hemisphere through an allpass cascade. Off by default. - Added
compas_acoustics.hybrid.synthesis.prepare,compas_acoustics.hybrid.ambisonics.render_ambisonics(withambisonic_output_matrixanddefault_num_delays, moved fromhybrid.late) andcompas_acoustics.hybrid.binaural.render_binaural(HrtfSet): one format-neutral core per pair (its early paths and delay lines,HybridCore) and two renderers; the binaural one filters every path and every delay line with the HRIR measured nearest its direction. Each renderer evaluates every path's spectrum, and every UDFA transfer function, once, and applies the late envelope (HybridCore.shaped_lines) against its own omnidirectional channel. - Added
HybridResult.late_linesandHybridCore.line_directions: the late field per delay line before it is rendered, and each line's direction in the receiver frame. - Added
Mono(ray_tracing=True), ray tracing on top of the image sources (n_rays,energy_thres,duration,receiver_radius,seed). - Added
AirConditions(temperature=20, humidity=50, pressure=101325): speed of sound after Cramer (1993) and air absorption after ISO 9613-1, for every method, recorded inImpulseResponse.generation. - Added
RoomGeometry, the scene's surfaces as one validated room (welded, outward oriented, closedness checked, coplanar faces merged), withto_pra()andis_inside(). - Added
AcousticObject(role="boundary" | "obstacle")andAcousticObject.from_obj(path, materials=None), a list with one object per OBJ group, every one a boundary; groupsmaterialsdoes not cover have none until they are assigned, andAcousticObject.materialaccepts only anAcousticMaterialorNone. - Added
AcousticObject.assign_roles_by_group(objects, roles)andassign_materials_by_group(objects, materials)to mark OBJ groups as obstacles and give them materials after reading, andAcousticObject.group, the OBJ group an object was read from (not serialised); a group name that none of the objects has raises aValueError. - Added
Receiver.up,rotation,frameandReceiver.from_frame(): the orientation sets the axes of ambisonic responses (x facing, y left, z up). - Added
compas_acoustics.room.diffuse(predicted_rt,mean_absorption,DiffuseFieldWarning): Sabine and Eyring reverberation times. - Added JSON serialisation (
compas.json_dumps) forScene,AcousticObject,Source,Receiver,ImpulseResponse,Ray,ScatteredRayandRayField; the IR graph is not serialised. - Added
Scene.impulse_responses(attribute),Scene.simulation_methods()andattribute=on the IR graph methods: each method stores its responses under its own name. - Added
ImpulseResponsewith peak normalization (denormalized(),normalization_factor,level_db),to_wav(),from_wav(),generation,resampled(),ambisonic_order,normalization,channel_orderanddirect_sound_index(). - Added
Analyser.stage_support()(ST_early and ST_late, ISO 3382-1 Annex C) andsound_strength()(G, needs pyrato >= 1.0), andGin the analysis tables. - Added
Analyseraccepting anImpulseResponse, analysed at its raw level and sampling rate. - Added
Auraliser.format(mono,binauralorambisonic, that of its impulse responses, which must share one),level(relative,individual,none),headroom_db, and lists of impulse responses and sound files as input. - Added
RayField,RayandScatteredRayfor traced rays (RayField.from_scene), with filtering, truncation and viewer display. - Added
Source.directivity,Source.sound_file(theAuralisermixes a multichannel file down to mono when it reads it),Receiver.direction,radiusandreceived_spls. - Added
AcousticMaterial.categoryanddata_origin,MaterialsLibrary.materials_by_category, preset materials, andtools/import_backend_materials.py. - Added
AudioPlayerfor.wavfiles and numpy arrays, withnormalize=True. - Added
compas_acoustics.regularized_fitness, exported from the package. - Added a warning before an image source search that will take long (
simulation_methods.image_source_search_seconds()). - Added
compas_acoustics.worker: runs compas_acoustics in a second Python 3.12, for the Grasshopper plugin and for Rhino 8's Python 3.9: a local HTTP service on a free port with a token per session that takes and returnscompas_pbpayloads (client(),simulate,analyse, and inworker.apithe functions the components call). - Added
compas_acoustics.pb, a compas_pb plugin: the Data types as protobuf messages (pb/protobuf_defs/.../acoustics.proto), their Python bindings, converters registered through thecompas_pb.pluginsentry point,SCHEMA(which the plugin checks the worker against) andplain();compas_pb >= 1.2is a requirement.invoke generateregenerates the Python and C# bindings. - Added
anechoic_soundfiles_library(bassoon, female and male speech, viola) andhrtfs_library(KEMAR, free-field compensated, 48 kHz), with READMEs that credit the sources and licences. - Added
Defaults.HRTF_LIBRARY_PATH,ANECHOIC_SOUNDFILES_LIBRARY_PATHandPARAM_NAMES. - Added
pyFDNandmultislopeto the requirements on Python >= 3.11, for the hybrid method (pyFDN brings flamo and PyTorch), and a CI job for it: onepip install compas_acoustics, no extra. - Added the Grasshopper plugin
CompasAcoustics.gha(C#,src/compas_acoustics_ghcsharp/CompasAcoustics.sln,invoke build-gha): typed parameters for the compas_acoustics data, value lists and buttons next to unconnected inputs, and the worker as the only Python it runs, so STI, G and the hybrid method are always available. It needs Rhino 8 and the .NET 8 SDK to build. - Added the components Simulate Binaural and Simulate Ambisonics (with a
runbutton, their last input), Simulate Mono (was Simulate Ray Tracing), Material Library categories, and the inputslevel(Auralise),normalize(Audio Player, Simulate),method(Deconstruct IR Graph),directionandplane(Receiver, which outputs the receiver only and keeps it upright: +z up, no roll). - Added the input
role(a value list:boundaryorobstacle) to the Grasshopper Acoustic Object component; a wrong role or material is an error on the component instead of a traceback. - Added the IR graph of the Grasshopper plugin: Simulate Mono, Simulate Binaural and Simulate Ambisonics output the responses of their method (
pyroomacoustics,binaural,ambisonics), and Deconstruct IR Graph reads them, so it has nomethodinput. - Changed the Grasshopper Check Channel component to count the channels of any sound: an audio file path, an impulse response, a Source with a sound file, or an array of samples such as the signals of Auralise (the input is now
sound). - Changed the Grasshopper Auralise component to also show the warnings of building the auraliser (impulse responses resampled to a common rate, sounds mixed down to mono), and the Audio Player to remark at which rate it plays an array that comes without one.
- Changed the image source order input of the Grasshopper Simulate Mono (
max_order), Simulate Binaural and Simulate Ambisonics (image_source_order) components toism_order; the Simulate Mono description gave the default as 6, the method's default is 3. - Changed the order of the inputs of the Grasshopper Auralise component to
irs,anechoic_sounds,level,mix_sources,save_file_path,save_file_name,save. - Changed the Grasshopper Auralise component to keep the format of its impulse responses, without the inputs
output_formatandhrtf: mono responses (Simulate Mono) give mono signals, binaural ones binaural, ambisonic ones the undecoded ACN/SN3D signal, and the file saved has that many channels. Binaural signals come from Simulate Binaural and itshrtf. Withmix_sources, responses of different formats at one receiver are an error. It convolves in Rhino's Python, without the worker. - Changed the Grasshopper Audio Player component to warn for a signal of more than two channels (an ambisonic one) instead of an error, and
AudioPlayer.play_arrayto play a mono signal on both channels. - Renamed the input
soundof the Grasshopper Audio Player component toaudioand ordered its inputsaudio,sampling_rate,normalize,play,stop. - Renamed the inputs
signalsandmulticonvolveof the Grasshopper Auralise component toanechoic_soundsandmix_sources:mix_sourcessums the sources at each receiver into one signal. - Changed the order of the inputs of the Grasshopper Source component to
position,name,direction,directivity,sound_file. - Changed
AudioPlayerto read the audio devices again before it plays an array, so that headphones connected after Rhino started are the output. - Added the input
select(a value list:allor one of EDT, T20, T30, C50, C80, D50, DRR, Ts, STI, G) to the Grasshopper Deconstruct Parameter component: a selected parameter comes out as plain lists,allas one branch per parameter. Itsparametersinput takes the list. - Renamed the Grasshopper Simulate Pyroomacoustics component to Simulate Mono, after the method it runs,
Mono(pyroomacoustics; graph attribute and worker method'mono','data'as before) (the same GUID: saved definitions load it) and its outputir_graphtomono_ir_graph, after the format of its responses, as Simulate Binaural'sbinaural_ir_graphand Simulate Ambisonics'ambisonic_ir_graph. - Added the input
source_idxto the Grasshopper Deconstruct IR Graph component (inputsir_graph,source_idx,receiver_idx): withreceiver_idxit picks source-receiver combinations, e.g. a single impulse response for Analyse Impulse Response. Sources are indexed in the order of the scene's sources; an index out of range is a warning. - Added
compas_acoustics.analyser.time_response(level in dB re the peak of all channels, or amplitude) andfrequency_response(fractional-octave smoothed, in dB re the peak), per channel; levels are floored 100 dB below the peak. - Added the outputs
timeandfrequencyand the inputtime_scale(a value list:dborlinear) to the Grasshopper Analyse Impulse Response component: the impulse response and its 1/3-octave smoothed frequency response from 20 Hz to 20 kHz, of the W channel of an ambisonic response and both ears of a binaural one. Plot 2D draws them as a curve per channel, on a time axis or a log frequency axis with ticks at 50, 100, 200, 500 ... 20000 Hz, reduced to the minimum and maximum of 1000 bins; a level in dB runs from its floor on the x axis to its peak, an amplitude is symmetric about zero with the x axis at zero. The time output inlinearends 5 % after its Schroeder curve falls 60 dB, indbit is the whole response, (time_response(..., trim_db=...)); the parameters and the frequency output use the whole response.AcousticParameter.from_time_responseandfrom_frequency_responsereplacefrom_ir, which Plot 2D could not draw. - Renamed the output
paramsof the Grasshopper Analyse Impulse Response component toparameters, and the inputparamof Deconstruct Parameter toparameters. - Renamed the input
paramof the Grasshopper Plot 2D component todata: it takes a parameter, or the time or frequency output of Analyse Impulse Response. - Changed the
hrtfinput of the Grasshopper Simulate Binaural component to get no value list: empty is KEMAR, the HRTF... button on the body chooses the user's own SOFA set (right-click "Use KEMAR" goes back), and a File Path connected to it wins; the component's message names the set in use. - Changed the
seedinput of the Grasshopper Simulate Mono, Simulate Binaural and Simulate Ambisonics components: left empty, each run draws a new random seed (shown in the component's message) instead of the methods' fixed default 42; a number gives repeatable responses.Mono,BinauralandAmbisonicsthemselves still default to 42. - Added a warning to the Grasshopper Simulate Binaural and Simulate Ambisonics components when
tailisreferenceand nothing is connected toreference; it shows before Run, and Run does not start the simulation. - Added the output
parameterto the Grasshopper Deconstruct Parameter component: the selected AcousticParameter, which Plot 2D plots and a panel prints (AcousticParameter.__str__gives the name, unit, value per band and overall value). - Added STI and G to the Grasshopper Analyse IR component, computed in the worker (they need pyrato >= 1.0, which Rhino 8's Python 3.9 cannot install); G only for a simulated response.
- Changed the Grasshopper Plot 2D chart to leave half a band's spacing between the y axis and the first frequency band.
- Changed
compas_acoustics.worker.simulateto store every format of a run on the scene's graph under its attribute and return the first, asScene.simulatedoes; it used to store whatever came back under the method's name. - Added a status to the Grasshopper Simulate Binaural and Simulate Ambisonics components for when they are not running: it says
donewith the tail (and the ambisonic order) of the last result on the graph,settings changed, press Runwhen its inputs differ from that result, andpress Runonly when there is none. - Added the Grasshopper installer for Rhino 8 (
installer/compas_acoustics_installer.gh),invoke installer-bundle, andcompas-dev/compas-actionsv1 workflows (build, release,prepare release), with tests on Python 3.9. - Changed the example scripts' names:
demo.pyisshoebox.py,demo_obj.pyisobj.py,demo_rays.pyisrays.py,demo_scattered_rays.pyisscattered_rays.py; addedspecular_reflections.py. - Changed the example script
shoebox.pytosimple_simulation_shoebox.py; its Grasshopper twin isexamples/simple_simulation_shoebox.ghx. - Added the documentation for the first public release: concepts, installation, a Python and a Grasshopper track, a Grasshopper component reference generated from the plugin's specs (
docs/_ext/ghcomponents, also run byinvoke docs-components), and build checks that fail when the committed images indocs/_images/are out of date. - Added a getting-started section to the README, and
Examplessections thatinvoke testdocscan run in the docstrings ofScene,Mono,Binaural,Ambisonics,AnalyserandAuraliser. - Added
SpecularReflections.from_scene(scene, max_order=3), the image-source paths from each source to each receiver for inspection, withfilter(source, ray_index, receiver_index, order). It is aRayFieldofSpecularPathrays, so it plays in a billiard (particles) and displays like traced rays. - Added source directivity to
RayField.from_scene, and so to Visualise Rays and Visualise Billiard: each source emits its rays with the density of its radiated energy, (p + (1 - p) cos theta)^2 about itsdirection(a deterministic lattice; a cardioid sends nothing straight back, a figure-of-eight nothing sideways); within a spotlight cone the cone's lattice is thinned to that density. An omnidirectional source emits the same rays as before. - Added
RayField.particles(time_ms)andRayField.duration_ms, the rays' positions at a time, for a billiard view. - Added a directional ray-tracing histogram to the hybrid's late envelope (
late_envelope=TrueofBinauralandAmbisonics, the default with a reference or ray-tracing tail): pyroomacoustics keeps one histogram per receiver direction (raytracing.directional_histograms, one bin per delay line, binned by the direction a ray arrives from; Lentz, Schroeder and Vorlaender 2007; Schroeder 2011, Sec. 5.3.4), and after the level per band follows the reference, each delay line is scaled by the square root of its direction's share of the rays' energy over its share of the lines' energy (hybrid.envelope.directional_shares,shape_late_directional), both pooled over neighbouring lines with the weights of Kirsch et al. (2023, Eq. 4), so the total is kept and a direction facing an absorbing wall decays as the rays say it does; the shares are held once the rays have decayed 60 dB. The histogram is traced by the method itself, also with a given reference: one more ray-tracing run per scene. The omnidirectional level and decay are unchanged. - Added the Grasshopper Visualise Reflections component: image-source paths of a source-receiver pair by order, output as
reflections, which Visualise Billiard plays. - Added the Grasshopper Visualise Rays component: rays traced from one source (
source_idx) or all, for display and Visualise Billiard. - Added the Grasshopper Visualise Billiard component: the rays of a ray field as particles (points only) at a time, played with a button or scrubbed with a slider.
- Added colours by reflection order to Visualise Reflections, Visualise Rays and Visualise Billiard: each component draws its paths, ray segments or particles itself (round particles, order 0 black, one colour per order);
RayField.particlesreturns each particle's reflection order too. - Added controls on the Grasshopper component bodies: switches and dropdowns under a title naming their input (Tail, Role), showing the chosen value; Simulate Binaural and Simulate Ambisonics
tail,diffractionandrunand Visualise Billiardplaykeep inputs, hidden on a new component and shown by "Show control inputs" in the right-click menu (a wired one always shows) for parameter studies; Auralise's level, Acoustic Object's role, Deconstruct Parameter's select and Source's directivity are chosen on the body only; Analyse IR's time scale on itstimeoutput's right-click menu; buttons (Run, Cancel; Play, reading Pause while playing (Play goes on from the paused frame), and Rewind on Visualise Billiard and Visualise Rays, whose rays are drawn only as Play makes them grow with the sound; a Preview toggle on the three Visualise components, which draw themselves and not their outputs). A wired value wins and greys the control; every choice is saved with the document; a control's tooltip says what its input means and its default. - Added body controls for the remaining choices of the Grasshopper components, each hidden behind "Show control inputs" like the others and starting at its input's default: the ISM order slider (whole orders 0 to 10, set when the knob is let go) on Simulate Mono, Simulate Binaural and Simulate Ambisonics; toggles and buttons set as far from the control above as a title is; on/off toggles sharing a row, Ray tracing, Air and Normalize on Simulate Mono, Late envelope and Normalize on the hybrid ones; the SH order dropdown (1 to 7, default 1) on Simulate Ambisonics, a choice of the component without an input; the sampling rate dropdown (44.1, 48, 96 kHz, default 48) on Acoustic Scene, a choice of the component without an input; Mix sources on Auralise; the Sound file... button on Source (its file's name on the button, right-click "Clear sound file"); the category dropdown on Construct Material; Source and Receiver dropdowns (All, or each by its index and name, from the wired scene or graph) on Visualise Rays, Visualise Reflections, Visualise Energy Map and Deconstruct IR Graph; Scattering and a slider of the drawn reflection order on Visualise Rays; the reflection orders 0 to 4 on Visualise Reflections, several lit at once; the time slider (it follows playback, and the particles follow the knob while it is dragged) and the Speed dropdown on Visualise Billiard; Play/Stop and Rewind side by side as icon buttons on Visualise Rays and Visualise Billiard; the Frequency bands dropdown (Full (A-weighted), or 125 Hz to 8 kHz) on Visualise Energy Map, its input renamed
frequency_bands(wasband). Controls on integer, number and bool inputs keep their values hidden and in the file, as text ones did. - Added the Audio Player's waveform, and impulse responses as its input (e.g. the
iroutput of Deconstruct IR Graph): the transport and volume right of the icon and below the inputs a lane per channel across the whole component, at one height, the component growing down with the channels, a Play/Pause and a Stop button with their usual glyphs, a playhead, Play, Pause, Stop, seeking by a click on the waveform, a volume slider, and a body that resizes. - Added runs that do not block the canvas: Simulate Mono, Simulate Binaural, Simulate Ambisonics, Analyse IR, Auralise, Visualise Rays and Visualise Reflections call the worker on a background thread, keep their last outputs meanwhile, show
running m:ssand the worker's stage, and offer Cancel; a change of inputs cancels the run and starts another (Simulate Binaural and Simulate Ambisonics wait for Run). - Added viewport previews of sources (a loudspeaker: a cube turned with the source and a driver on its front face, the direction, the directivity lobe), receivers (a head: a sphere with its ears, the facing), acoustic objects and scenes (their edges only, as Rhino draws a surface's: outlines and folds, not the triangulation; no surfaces over the Rhino geometry they come from; obstacles with orange edges). IR graphs draw nothing. The Acoustic Object's message gives its total area, the Acoustic Scene's the room's volume and total area on two lines, its tooltip also the objects, sources, receivers and sampling rate. Sources and receivers are drawn only by the Source and Receiver components, under their preview toggles: not again by a scene, by Deconstruct IR Graph (which previews nothing, its source-to-receiver
edgesincluded), or by a Merge or Data they pass through. Components preview only their outputs, not the geometry, points or planes wired into them; Acoustic Object and Acoustic Scene start with their preview off, their surfaces being in Rhino already. Every component is drawn light and offers Preview, also those with nothing to draw (Grasshopper would draw them in the dark preview-off grey); those draw nothing: every output has a type (text, number, integer, point), none the generic one that draws whatever passes through it. - Added defaults and named values on the Grasshopper ports (
ism_order3,n_rays20000 and 500,max_order10,energy_thres1e-7,k1.0,sampling_rate48000), and the scene's sources and receivers as named values ofsource_idxandreceiver_idx. - Added the material picker (search, Custom above the categories, Name, Category and Origin, a chart of absorption and scattering on which a click or drag sets a band, and a box per band for each to type a value, of a library material or of a custom one whose name and category are editable), opened from the Material Library's strip or by a double-click on it; its inputs
absorptionandscatteringreplace the chosen coefficients; its strip, sized as Deconstruct Parameter's, draws absorption and scattering as the picker's paired bars over the bands, 0 and 1 beside; its message says when scattering is the 0.05 default; a custom material left without absorption warns. A material's tooltip gives its mean absorption. - Added Plot 2D on the canvas: a chart per parameter, a dot per band joined by a line or a line per channel, resizable at a fixed aspect ratio, STI on a 0 to 1 axis and the reverberation times (EDT, T20, T30) from 0, with the values under the mouse in the tooltip and a Save button that writes the charts as a PNG; Deconstruct Parameter shows its bands as a small line chart, the bands below and the lowest, middle and highest value beside.
- Added the Worker component (state, Python, version and schema, Restart, Open log), "Open worker log" in every component's menu, the worker's log file, and message strips of at most 40 characters with the whole text in the tooltip; Acoustic Scene's message counts objects, sources, receivers, the rate and the volume.
- Added one generated icon set for the components (
tools/icons.py). - Added the worker routes
POST /cancel/<call id>andGET /progress/<call id>and the request headerX-Call-Id(compas_acoustics.worker.jobs); a cancelled call answers 499. - Added LF and LFC (ISO 3382-1 early lateral energy fractions) to
Analyser.analyse(), Analyse IR and Deconstruct Parameter, from the first-order channels of an ambisonic response. - Added
Source.splas the source's level (SPL on axis at 1 m, per band; one value or seven) and SPL per band with its A-weighted total toAnalyser(sound_pressure_level,analyse) and Analyse IR. - Added the
splinput to the Grasshopper Source component (the source's level at 1 m per band, or one value). - Added
EnergyMap(compas_acoustics.EnergyMap): the SPL of a scene's sources over many points, per band, A-weighted and over time, from pyroomacoustics' ray tracer with a microphone per point plus first-order image sources, and the worker'senergy_map. - Added the Visualise Energy Map component: a surface (a mesh, a surface or a Brep, meshed into faces no larger than
spacing, default 0.25 m; the rays keep a 1 m grid) coloured by the full A-weighted or one band's SPL at its face centres (the Frequency bands dropdown), drawn a centimetre above the surface so that the two do not flicker. Its settings are fixed: image sources up to order 1, seed 42, and the rays ofenergy_map.map_rays, Vorlaender's (2008) Eq. 11.12 over the response length for 16000 hits per point (20000 to 250000 rays).EnergyMap.from_scene(n_rays=None)uses the same count. The rays are traced to one receiver per 1 m cell of the map (ray_spacing=1.0, the cell's point nearest its centre) and their energy is interpolated to the other points by inverse squared distance; the image sources stay exact at every point. pyroomacoustics checks every receiver for every ray segment, so fewer receivers trace faster, and none lies partly outside the room next to a wall. The worker'senergy_maptakes non_rays,image_source_orderorseed, returns the rays traced asn_rays, and returns no frames of the energy arriving;EnergyMap.frames()gives them.
Changed¶
- Changed the worker's playback API:
play(sound, normalize=True, gain_db=0.0, start=0.0)returns the duration;pause,resume,stop,playback_statusandsound_samplesare new. - Changed how the hybrid's early renderer and late envelope compute, not what:
early.render_imagesandrender_transfersaccumulate the paths' spectra as matrix products over chunks of paths instead of one outer product per path, andenvelope.octave_band_splitapplies pyfar's reconstructing bank as one FFT per channel and one inverse FFT per band (zero-padded past the filter length, the linear convolution pyfar computes) with the bank designed once per sampling rate. Responses are unchanged to rounding. - Changed
Analyser.speech_transmission_indexto reduce the modulation transfer function it already has with pyrato's_sti_calc(IEC 60268-16:2020 Annex A.2.1) instead of callingspeech_transmission_index_indirect, which computes the same MTF again; without_sti_calcthe indirect method is called. - Changed the ray tracer's defaults:
energy_thres1e-7 (pyroomacoustics' own), sincelibroomstops a ray on the product of its wall reflections while the receiver logs that energy over the squared distance; andduration1.5 times the longest Eyring reverberation time within 0.5 to 12 s. The runsBinauralandAmbisonicsmake themselves, a ray-traced reference and the directional histogram, take the same default. A reference shorter than 2/3 of its reverberation time warns, since pyFDN's decay fit shrinks its range to what the response has. A ray-traced response that ends before 2/3 of its own longest T30 warns with the length to rerun with: a room whose absorption is concentrated on a few surfaces can decay far slower than Eyring predicts. A room whose Eyring prediction hits the 12 s cap also warns (raytracing.default_duration): its rays live for hundreds of reflections, usually from a material with no low-frequency absorption on a large surface. - Changed the JSON data type of every
compas_acousticsclass tocompas_acoustics/<ClassName>(wascompas_acoustics.<module>/<ClassName>), so that saved data no longer depends on the module a class lives in; the materials library is stored that way. Files saved before do not load. - Changed the modules:
rayandspecularare one module,rays;acoustic_materialandmaterials_libraryarematerials;geometryisacoustic_object;numerical.fitnessisfitness;room_geometry,air,diffuse,pra_converter,raytracingand_pyfar_constantsare the subpackageroom(room.geometry,room.air,room.diffuse,room.pra,room.raytracing,room._pyfar_constants), which exportsRoomGeometryandAirConditions;room.raytracingholds both pyroomacoustics runs (image_source_rirs,ray_traced_rirs) and the directional histograms, the backend ofMonoand of the binaural and ambisonics methods;binauralishybrid.binaural. The classes are exported fromcompas_acousticsas before. - Changed
Defaults().DEFAULT_SPEED_OF_SOUNDto the speed of sound ofAirConditions()(343.98 m/s at 20 C and 50 %, was 343), the air every simulation runs in without one given.Ray,ScatteredRayandRayFieldno longer take or store asound_speed(it was 343 on its own and was serialised with every ray): their travel times are at this default; ray fields saved with it still load. - Changed the hybrid engine to use 96 delay lines up to third ambisonic order, and always for
Binaural(hybrid.late.MIN_DELAYS): the count Kirsch, Wendt and van de Par (2023) run their network with whatever the number of rendered directions, so that the tail's character does not change with the count, and the one they name for offline simulation. In the binaural response each line is heard through its own HRIR. The methods take nonum_delaysordrive_gain: the count follows fromsh_order(Ambisonics) or is 96 (Binaural), and the input gainsqrt(num_delays)offsets the output's1 / sqrt(num_delays), so the late level does not depend on the count;hybrid.synthesis.preparekeeps both. The delay-line grid depends on the count alone (fdn_directions(num_delays)); whether it carries an ambisonic order isambisonic_output_matrix's check. - Changed how the last rendered image-source order drives the hybrid's network: each image enters the delay lines of the hemisphere opposite its arrival, weighted by the cosine to each line (
hybrid.scattering.scattering_weights(..., opposite=True)), the mapping of Ewert et al. (2025, Sec. 3.1.2). - Changed the spelling of identifiers to
normalizationandnormalizedeverywhere, includingImpulseResponse.normalization(the ambisonic convention) and its JSON metadata key. - Changed
AudioPlayerto raise errors andwarnings.warninstead of printing them; the Audio Player component shows them as messages. An unsupported sound type raises aTypeError. - Changed
seedofMono,BinauralandAmbisonics(was 0) toTrueby default, which seeds with 42 (DEFAULT_SEED); an int seeds with that value;False(orNone) leaves the run unseeded, a new draw each run, recorded asgeneration["seed"] = None. Anything else raises aValueError.RayField.from_scene(random_seed=)follows the same rule. - Changed the hybrid engine to drive the network with the last rendered image source order, as Wendt et al. (2014) and Ewert et al. (2025) do. The search stops at
image_source_order. - Changed
BinauralandAmbisonicswithout a reference to runMono(ray_tracing=True, image_source_order=3, n_rays=100000, seed=42)and fit the decay to it. - Changed the hybrid's 35 Hz high-pass to a causal filter, which removes pre-arrival ringing in the diffracted paths.
- Changed every simulation to run in
AirConditions()(20 C, 50 %, 343.98 m/s) unlessair=is given; before, the methods used different air tables. - Changed simulated impulse responses to 48000 Hz (were always 16000 Hz) and to the package scale (free-field direct sound
1 / (4 pi r)). - Changed
AcousticObject.__init__to take onematerial;AcousticObjectsLibrary.shoebox_room()returns a list of six single-material objects and takesmaterials. - Changed
Source.directionaltoSource.directivity, andSource.directionandReceiver.directiontocompas.geometry.Vector. - Changed
sample_ratetosampling_rateeverywhere:ImpulseResponse,Auraliser,AudioPlayer.play,receivers_to_pra_mic_array,HrtfSetand the Grasshopper components. - Changed the
Analysermethods to name the quantity:reverberation_times,clarity_definition,direct_to_reverberant_ratio,speech_transmission_index,stage_support,sound_strength. - Changed the analyser to apply octave filters zero-phase or after cutting the time windows, which removes a delay of 18 ms at 250 Hz and 35 ms at 125 Hz from EDT, Ts, C50, C80, D50 and DRR.
- Changed
direct_to_reverberant_ratioto 5 ms of direct sound,DRR_overallfrom a 700-4000 Hz band. - Changed
reverberation_timesto Schroeder integration without noise compensation; truncate a measured response with a noise floor before analysing it. - Changed the direct sound detection to the start of ISO 3382-1: the last sample more than 20 dB below the maximum before it.
- Changed
Auraliser.convolve()to return the signal in the format of its impulse responses, without decoding, summing the impulse responses of one sound first. - Changed
Auraliserto auralise at the raw level, export peak at -1 dBFS, and use an impulse response's ownchannel_orderandnormalization. - Changed
Auraliserto read and resample with pyfar (polyphase) and to default to the rate of the first impulse response. - Changed
Analyser,AuraliserandMaterialsLibraryto plain classes; they are services, not data. - Changed the default HRTF to
KEMAR_FreeFieldComp_48kHz.sofa. - Changed a material without scattering coefficients to scatter 0.05 in every band in every pyroomacoustics simulation.
- Changed
AcousticMaterialso that a coefficient of 0 is a value, and material names may have spaces and capitals; library files are named byAcousticMaterial.slugand carry no backend suffix. - Changed sources and receivers outside the room to raise a
UserWarning, and surfaces withrole="obstacle"to reflect from both sides. - Changed
Scene.acoustic_materialsto read-only; assigning it raises anAttributeErrorthat says how materials are assigned. - Changed the hybrid engine to accept
diffractionwithout obstacles, and to report non-closed obstacle components in one warning per call. - Changed the package to run on Python 3.9 (Rhino 8) without Rhino, Grasshopper or .NET; the Grasshopper plugin is in
src/compas_acoustics_ghcsharp/. - Changed
pyrato >= 1.0to be required from Python 3.11 (was 3.12), and removed the upper bounds on pyroomacoustics and pyFDN. - Changed the Analyse IR component to leave out STI and G, which need pyrato >= 1.0; they stay in
Analyserwhere pyrato allows. - Changed
matplotlibto be imported by theAnalyserplot methods only when they run. - Changed the Grasshopper installer to copy the plugin (the bundle's
gha/folder) into Grasshopper's Libraries folder, remove the CPython components of earlier versions, and always set up the worker; itsworkerinput is not used. - Changed
librosaout of the dependencies andsounddeviceinto them;spharpyis required on Python >= 3.11. - Changed
RoomGeometryto weld vertices at 1 mm (WELD_TOLERANCE, was 1 micrometre), as Rhino's tolerance in a metre model: a wall corner 10 micrometres off no longer leaves the room open. A vertex still lies on an edge only within 1e-6 of the model's extent. Faces with a corner more than 10 micrometres off their plane are triangulated (pyroomacoustics raised aRuntimeErroron them), faces that collapse in welding are dropped with a warning, a model more than 500 units across warns that coordinates are read as metres, and a wall pyroomacoustics refuses is aValueErrornaming the face. - Changed
AcousticObject.from_objto read OBJ files with its own parser:o(object) lines are the groups of a file withoutglines, faces may usei/j/kand negative indices, vertices are no longer rounded (RoomGeometrywelds them), and faces with fewer than three distinct vertices are skipped. - Changed the default
Sourcedirection to +x (was +z), the direction the Grasshopper Source component already defaults to and the receiver's facing direction. - Changed
AcousticObjectsLibrary.shoebox_roomso thatleft_wallis the surface at y = width andright_wallthe one at y = 0, matching the receiver frame (x facing, y left); the two names were swapped. The surfaces come in the order floor, right, front, left and back wall, ceiling. - Changed the error of
Receiverfor a name with spaces or capitals to sayReceiver(it saidSource). - Changed
RoomGeometry.surfaces,surface_area,area_normalsandvolumeto count the air side only: a closed obstacle counts one side and its volume is subtracted, and obstacle faces lying on the boundary are left out, so Eyring and Sabine times, the default ray-tracing duration and the hybrid's delays and scattering decay time move in scenes with closed obstacles or obstacles on the boundary; a free panel still counts both sides. - Changed
AcousticMaterialto validate coefficients on creation: any iterable (including Grasshopper'sList[Double]) and scalars become lists of float, a single value (a scalar or a one-element list) applies to every band, an empty list means no coefficients, and another length than 7 or a value outside [0, 1] raisesValueError. - Changed the hybrid's binaural late field to be rendered per delay line, each line
/ sqrt(N)through the HRIR measured nearest its direction, so a binaural response does not needsh_order >= 1. - Changed
hybrid.late.renderto return the delay lines' outputs,(n, num_delays), after the directional absorption and slope weights; the ambisonic late field is that timesC.T. - Changed the format of a response to come from its channels,
ImpulseResponse.format('mono','binaural'for two channels without an ambisonic order,'ambisonic'), not ageneration["output"]flag; the Auraliser, the analysis plots and the Grasshopper Audio Player read it. - Filled in the package metadata, README and documentation for the first public release, and removed the tests that compared against external toolboxes, saved outputs or a running worker.
- Changed the component specs (
Specs/<CA_name>/metadata.json) to list their ports underports.
Fixed¶
- Fixed
raytracing.directional_histogramsfailing with "all input arrays must have the same shape" when the run is longer than pyroomacoustics' default 10 s and rays are still alive then:libroomsizes every direction's histogram for 10 s and doubles only the directions hit later. The hybrid's late envelope failed this way on a reverberant room whose Eyring buffer reached 12 s. - Fixed the Grasshopper Acoustic Scene component failing with
'NoneType' object has no attribute 'sources'when an object is empty or not an acoustic object (an upstream Acoustic Object that failed): such items are left out with a warning. - Fixed
RoomGeometrytaking a vertex up to 1 mm off an edge for a T-junction, which left rooms with sliver faces open: the edge test is 1e-6 of the model's extent again. - Fixed the single-number values of C50, C80, D50 and Ts averaging 500, 1000 and 2000 Hz: they average 500 and 1000 Hz, as ISO 3382-1:2009 Table A.1 has it for G, EDT, C80, D50 and Ts (C50 follows D50), like EDT, T20 and T30 already did.
- Fixed responses simulated in the worker (the hybrid in Rhino) always coming back as peak-normalized views: they keep the method's
normalize. Their raw level was already kept. - Fixed the Grasshopper Material Library component forgetting its material when the file is reopened: the choice is kept in the component's nickname, which Grasshopper saves.
- Fixed the Grasshopper Material Library component failing with
No module named 'compas_acoustics': its script pinned an empty Rhino script environment,compas_acoustics_rhino, instead of the one the installer fills. - Fixed the worker setup from the Grasshopper installer failing at its last check: the worker's Python 3.12 inherited the
PYTHONPATHof Rhino's 3.9 packages and could not import numpy. - Fixed a simulation failing on a Source whose
sound_fileis not a RIFF WAV (an AIFF or FLAC file; the library'sbassoon.wavis an AIFF): the file is read with soundfile. - Fixed the hybrid engine counting an image source twice when its path passes through an edge (+6 dB on that path);
hybrid.early.image_sourcesnow keeps one image per position, asShoeBoxdoes. - Fixed low-frequency bias in EDT, C50, C80, D50, DRR and Ts caused by the delay of causal octave filters.
- Fixed
speech_transmission_indexreturning NaN for impulse responses shorter than 1.6 s. - Fixed wheels that were missing the materials library, the HRTFs, the sound files and the Grasshopper components.
- Fixed
Auralisermodifying the caller's dicts, ignoring 1D responses and multichannel sounds, and wrapping energy around when resampling. - Fixed simulations above 16 kHz with air absorption, which failed on a 7 against 8 octave band mismatch.
- Fixed
ReceiverandSourcelosing name, orientation, directivity and settings in JSON andcopy(). - Fixed NaN responses for a source at a receiver's position (now a
ValueError), and unrelated errors for points inside a closed obstacle, scenes of obstacles only, or too few walls. - Fixed the hybrid failing on a fully absorbing surface, and counting a wall hit on a diffraction apex as a reflection.
- Fixed
display_in_viewerguards that treatedcompas.is_rhino()as a boolean, and the default HRTF path that pointed at a missing file. - Fixed the Grasshopper Auralise and Analyse IR components using a fixed sample rate.
- Fixed
AcousticObject.from_objreading OBJ files in the platform's encoding: a UTF-8 byte order mark dropped the first vertex, and non-ASCII group names were garbled on Windows; files are read as UTF-8. - Fixed obstacle faces lying on a boundary face being counted when the scene is not aligned with the axes: the test used the distance along each axis instead of the distance to the face's plane.
Removed¶
- Removed Plot 2D Settings and Plot 2D's Rhino geometry (texts, points, polylines, grid).
- Removed the worker's
compas.rpctransport (proxy()) and its temporary WAV folders. - Removed
Simulator; useScene.simulate(method)andRayField.from_scene. - Removed
simulation_platformfromSceneandImpulseResponse;generationnames the method. - Removed
Mono(n_jobs=)and the parallel ray tracer, which wrote privateRoomstate. - Removed
AcousticObjectsGroup,AcousticObject.surface_groups,Scene.iter_acoustic_objects,Scene.scene_surface_groupsand their display and print methods; seescene_materialsandprint_scene_materials. - Removed
compas_acoustics.stage_acousticsandtools.Tools; useAnalyser.stage_support(). - Removed
pra_converter.material_to_pra,mesh_to_pra_wall,acoustic_object_to_pra_wallsandroom_octave_bands; seeRoomGeometry.to_pra. - Removed
Auraliser.multiconvolution(),decode_stereo_simple(),resample_signal()and FuMa channel order. - Removed
compas_acoustics.orchestra_layoutsand the Orchestra Layout component. - Removed the two MIT KEMAR HRTF sets and
speech.wav. - Removed
HybridandHybrid.binaural(); useBinauralorAmbisonics. - Removed the ambisonic decoders of
Auraliser(mono from W, stereo cardioids and binaural:output_format,decode,decode_mono,decode_stereo,decode_binaural,hrtf_path,binaural_filters,ambisonics_upmix_downmix),binaural.magls_filters,worker.convolveand the installer'sworkeroptionbinaural. The hybrid renders binaural responses with an HRTF per path and per delay line; an ambisonic response or signal is saved as ACN/SN3D for a decoder elsewhere. Impulse responses of different formats in oneAuraliserraise aValueError.