audionimbus/
probe.rs

1use crate::context::Context;
2use crate::energy_field::EnergyField;
3use crate::error::{to_option_error, SteamAudioError};
4use crate::geometry::{Matrix, Scene, Sphere};
5use crate::serialized_object::SerializedObject;
6use crate::simulation::BakedDataIdentifier;
7
8/// An array of sound probes.
9///
10/// Each probe has a position and a radius of influence.
11#[derive(Debug)]
12pub struct ProbeArray(audionimbus_sys::IPLProbeArray);
13
14impl ProbeArray {
15    pub fn try_new(context: &Context) -> Result<Self, SteamAudioError> {
16        let mut probe_array = Self(std::ptr::null_mut());
17
18        let status = unsafe {
19            audionimbus_sys::iplProbeArrayCreate(context.raw_ptr(), probe_array.raw_ptr_mut())
20        };
21
22        if let Some(error) = to_option_error(status) {
23            return Err(error);
24        }
25
26        Ok(probe_array)
27    }
28
29    /// Generates probes and adds them to the probe array.
30    pub fn generate_probes(&mut self, scene: &Scene, probe_params: &ProbeGenerationParams) {
31        unsafe {
32            audionimbus_sys::iplProbeArrayGenerateProbes(
33                self.raw_ptr(),
34                scene.raw_ptr(),
35                &mut audionimbus_sys::IPLProbeGenerationParams::from(*probe_params),
36            );
37        }
38    }
39
40    /// Returns the number of probes in the probe array.
41    pub fn num_probes(&self) -> usize {
42        unsafe { audionimbus_sys::iplProbeArrayGetNumProbes(self.raw_ptr()) as usize }
43    }
44
45    /// Returns the probe at a given index in the probe array.
46    pub fn probe(&self, index: usize) -> Sphere {
47        assert!(index < self.num_probes(), "probe index out of bounds");
48
49        let ipl_sphere =
50            unsafe { audionimbus_sys::iplProbeArrayGetProbe(self.raw_ptr(), index as i32) };
51
52        Sphere::from(ipl_sphere)
53    }
54
55    pub fn raw_ptr(&self) -> audionimbus_sys::IPLProbeArray {
56        self.0
57    }
58
59    pub fn raw_ptr_mut(&mut self) -> &mut audionimbus_sys::IPLProbeArray {
60        &mut self.0
61    }
62}
63
64impl Clone for ProbeArray {
65    fn clone(&self) -> Self {
66        unsafe {
67            audionimbus_sys::iplProbeArrayRetain(self.0);
68        }
69        Self(self.0)
70    }
71}
72
73impl Drop for ProbeArray {
74    fn drop(&mut self) {
75        unsafe { audionimbus_sys::iplProbeArrayRelease(&mut self.0) }
76    }
77}
78
79unsafe impl Send for ProbeArray {}
80unsafe impl Sync for ProbeArray {}
81
82/// Settings used to generate probes.
83#[derive(Copy, Clone, Debug)]
84pub enum ProbeGenerationParams {
85    /// Generates a single probe at the center of the specified box.
86    Centroid {
87        /// A transformation matrix that transforms an axis-aligned unit cube, with minimum and maximum vertices at (0.0, 0.0, 0.0) and (1.0, 1.0, 1.0), into a parallelopiped volume.
88        /// Probes will be generated within this volume.
89        transform: Matrix<f32, 4, 4>,
90    },
91
92    /// Generates probes that are uniformly-spaced, at a fixed height above solid geometry.
93    /// A probe will never be generated above another probe unless there is a solid object between them.
94    /// The goal is to model floors or terrain, and generate probes that are a fixed height above the floor or terrain, and uniformly-spaced along the horizontal plane.
95    /// This algorithm is not suitable for scenarios where the listener may fly into a region with no probes; if this happens, the listener will not be influenced by any of the baked data.
96    UniformFloor {
97        /// Spacing (in meters) between two neighboring probes.
98        spacing: f32,
99
100        /// Height (in meters) above the floor at which probes will be generated.
101        height: f32,
102
103        /// A transformation matrix that transforms an axis-aligned unit cube, with minimum and maximum vertices at (0.0, 0.0, 0.0) and (1.0, 1.0, 1.0), into a parallelopiped volume.
104        /// Probes will be generated within this volume.
105        transform: Matrix<f32, 4, 4>,
106    },
107}
108
109impl From<ProbeGenerationParams> for audionimbus_sys::IPLProbeGenerationParams {
110    fn from(probe_generation_params: ProbeGenerationParams) -> Self {
111        let (type_, spacing, height, transform) = match probe_generation_params {
112            ProbeGenerationParams::Centroid { transform } => (
113                audionimbus_sys::IPLProbeGenerationType::IPL_PROBEGENERATIONTYPE_CENTROID,
114                f32::default(),
115                f32::default(),
116                transform,
117            ),
118            ProbeGenerationParams::UniformFloor {
119                spacing,
120                height,
121                transform,
122            } => (
123                audionimbus_sys::IPLProbeGenerationType::IPL_PROBEGENERATIONTYPE_UNIFORMFLOOR,
124                spacing,
125                height,
126                transform,
127            ),
128        };
129
130        Self {
131            type_,
132            spacing,
133            height,
134            transform: transform.into(),
135        }
136    }
137}
138
139/// A batch of sound probes, along with associated data.
140///
141/// The associated data may include reverb, reflections from a static source position, pathing, and more.
142/// This data is loaded and unloaded as a unit, either from disk or over the network.
143#[derive(Debug)]
144pub struct ProbeBatch(audionimbus_sys::IPLProbeBatch);
145
146impl ProbeBatch {
147    pub fn try_new(context: &Context) -> Result<Self, SteamAudioError> {
148        let mut probe_batch = Self(std::ptr::null_mut());
149
150        let status = unsafe {
151            audionimbus_sys::iplProbeBatchCreate(context.raw_ptr(), probe_batch.raw_ptr_mut())
152        };
153
154        if let Some(error) = to_option_error(status) {
155            return Err(error);
156        }
157
158        Ok(probe_batch)
159    }
160
161    /// Returns the number of probes in the probe batch.
162    pub fn num_probes(&self) -> usize {
163        unsafe { audionimbus_sys::iplProbeBatchGetNumProbes(self.raw_ptr()) as usize }
164    }
165
166    /// Returns the size (in bytes) of a specific baked data layer in the probe batch.
167    pub fn data_size(&self, identifier: BakedDataIdentifier) -> usize {
168        let mut ffi_identifier: audionimbus_sys::IPLBakedDataIdentifier = identifier.into();
169
170        unsafe {
171            audionimbus_sys::iplProbeBatchGetDataSize(self.raw_ptr(), &mut ffi_identifier as *mut _)
172                as usize
173        }
174    }
175
176    pub fn remove_data(&mut self, identifier: BakedDataIdentifier) {
177        let mut ffi_identifier: audionimbus_sys::IPLBakedDataIdentifier = identifier.into();
178
179        unsafe {
180            audionimbus_sys::iplProbeBatchRemoveData(self.raw_ptr(), &mut ffi_identifier as *mut _)
181        }
182    }
183
184    /// Adds a probe to a batch.
185    /// The new probe will be added as the last probe in the batch.
186    pub fn add_probe(&mut self, probe: &Sphere) {
187        unsafe {
188            audionimbus_sys::iplProbeBatchAddProbe(
189                self.raw_ptr(),
190                audionimbus_sys::IPLSphere::from(*probe),
191            );
192        }
193    }
194
195    /// Removes a probe from the batch.
196    pub fn remove_probe(&mut self, probe_index: usize) {
197        assert!(probe_index < self.num_probes(), "probe index out of bounds");
198
199        unsafe {
200            audionimbus_sys::iplProbeBatchRemoveProbe(self.raw_ptr(), probe_index as i32);
201        }
202    }
203
204    /// Adds every probe in an array to a batch.
205    /// The new probes will be added, in order, at the end of the batch.
206    pub fn add_probe_array(&mut self, probe_array: &ProbeArray) {
207        unsafe {
208            audionimbus_sys::iplProbeBatchAddProbeArray(self.raw_ptr(), probe_array.raw_ptr());
209        }
210    }
211
212    /// Retrieves a single array of parametric reverb times in a specific baked data layer of a specific probe in the probe batch.
213    pub fn reverb(&self, identifier: BakedDataIdentifier, probe_index: usize) -> [f32; 3] {
214        assert!(probe_index < self.num_probes(), "probe index out of bounds");
215
216        let mut ffi_identifier: audionimbus_sys::IPLBakedDataIdentifier = identifier.into();
217        let mut reverb_times: [f32; 3] = [0.0; 3];
218
219        unsafe {
220            audionimbus_sys::iplProbeBatchGetReverb(
221                self.raw_ptr(),
222                &mut ffi_identifier as *mut _,
223                probe_index as i32,
224                reverb_times.as_mut_ptr(),
225            );
226        }
227
228        reverb_times
229    }
230
231    /// Retrieves a single energy field in a specific baked data layer of a specific probe in the probe batch.
232    pub fn energy_field(&self, identifier: BakedDataIdentifier, probe_index: usize) -> EnergyField {
233        assert!(probe_index < self.num_probes(), "probe index out of bounds");
234
235        let mut ffi_identifier: audionimbus_sys::IPLBakedDataIdentifier = identifier.into();
236        let energy_field = EnergyField(std::ptr::null_mut());
237
238        unsafe {
239            audionimbus_sys::iplProbeBatchGetEnergyField(
240                self.raw_ptr(),
241                &mut ffi_identifier as *mut _,
242                probe_index as i32,
243                energy_field.raw_ptr(),
244            );
245        }
246
247        energy_field
248    }
249
250    /// Commits all changes made to a probe batch since this function was last called (or since the probe batch was first created, if this function was never called).
251    /// This function must be called after adding, removing, or updating any probes in the batch, for the changes to take effect.
252    pub fn commit(&self) {
253        unsafe { audionimbus_sys::iplProbeBatchCommit(self.raw_ptr()) }
254    }
255
256    /// Saves a probe batch to a serialized object.
257    /// Typically, the serialized object will then be saved to disk.
258    pub fn save(&self, serialized_object: &mut SerializedObject) {
259        unsafe {
260            audionimbus_sys::iplProbeBatchSave(self.raw_ptr(), serialized_object.raw_ptr());
261        }
262    }
263
264    /// Loads a probe batch from a serialized object.
265    /// Typically, the serialized object will be created from a byte array loaded from disk or over the network.
266    pub fn load(
267        context: &Context,
268        serialized_object: &mut SerializedObject,
269    ) -> Result<Self, SteamAudioError> {
270        let mut probe_batch = Self(std::ptr::null_mut());
271
272        let status = unsafe {
273            audionimbus_sys::iplProbeBatchLoad(
274                context.raw_ptr(),
275                serialized_object.raw_ptr(),
276                probe_batch.raw_ptr_mut(),
277            )
278        };
279
280        if let Some(error) = to_option_error(status) {
281            return Err(error);
282        }
283
284        Ok(probe_batch)
285    }
286
287    pub fn raw_ptr(&self) -> audionimbus_sys::IPLProbeBatch {
288        self.0
289    }
290
291    pub fn raw_ptr_mut(&mut self) -> &mut audionimbus_sys::IPLProbeBatch {
292        &mut self.0
293    }
294}
295
296impl Clone for ProbeBatch {
297    fn clone(&self) -> Self {
298        unsafe {
299            audionimbus_sys::iplProbeBatchRetain(self.0);
300        }
301        Self(self.0)
302    }
303}
304
305impl Drop for ProbeBatch {
306    fn drop(&mut self) {
307        unsafe { audionimbus_sys::iplProbeBatchRelease(&mut self.0) }
308    }
309}
310
311unsafe impl Send for ProbeBatch {}
312unsafe impl Sync for ProbeBatch {}