fmod/studio/system/
parameter.rs

1// Copyright (c) 2024 Melody Madeline Lyons
2//
3// This Source Code Form is subject to the terms of the Mozilla Public
4// License, v. 2.0. If a copy of the MPL was not distributed with this
5// file, You can obtain one at https://mozilla.org/MPL/2.0/.
6
7use fmod_sys::*;
8use lanyard::{Utf8CStr, Utf8CString};
9use std::{
10    ffi::{c_float, c_int},
11    mem::MaybeUninit,
12};
13
14use crate::studio::{ParameterDescription, ParameterID, System, get_string_out_size};
15use crate::{FmodResultExt, Result};
16
17impl System {
18    /// Retrieves a global parameter value by unique identifier.
19    ///
20    /// The second tuple field is the final value of the parameter after applying adjustments due to automation, modulation, seek speed, and parameter velocity to value.
21    /// This is calculated asynchronously when the Studio system updates.
22    pub fn get_parameter_by_id(&self, id: ParameterID) -> Result<(c_float, c_float)> {
23        let mut value = 0.0;
24        let mut final_value = 0.0;
25
26        unsafe {
27            FMOD_Studio_System_GetParameterByID(
28                self.inner.as_ptr(),
29                id.into(),
30                &raw mut value,
31                &raw mut final_value,
32            )
33            .to_result()?;
34        }
35
36        Ok((value, final_value))
37    }
38
39    /// Sets a global parameter value by unique identifier.
40    pub fn set_parameter_by_id(
41        &self,
42        id: ParameterID,
43        value: c_float,
44        ignore_seek_speed: bool,
45    ) -> Result<()> {
46        unsafe {
47            FMOD_Studio_System_SetParameterByID(
48                self.inner.as_ptr(),
49                id.into(),
50                value,
51                ignore_seek_speed.into(),
52            )
53            .to_result()
54        }
55    }
56
57    /// Sets a global parameter value by unique identifier, looking up the value label.
58    ///
59    /// If the specified label is not found, [`FMOD_RESULT::FMOD_ERR_EVENT_NOTFOUND`] is returned.
60    /// This lookup is case sensitive.
61    pub fn set_parameter_by_id_with_label(
62        &self,
63        id: ParameterID,
64        label: &Utf8CStr,
65        ignore_seek_speed: bool,
66    ) -> Result<()> {
67        unsafe {
68            FMOD_Studio_System_SetParameterByIDWithLabel(
69                self.inner.as_ptr(),
70                id.into(),
71                label.as_ptr(),
72                ignore_seek_speed.into(),
73            )
74            .to_result()
75        }
76    }
77
78    /// Sets multiple global parameter values by unique identifier.
79    ///
80    /// If any ID is set to all zeroes then the corresponding value will be ignored.
81    ///
82    /// # Panics
83    ///
84    /// This function will panic if `ids.len()` != `values.len()`.
85    pub fn set_parameters_by_ids(
86        &self,
87        ids: &[ParameterID], // TODO fmod says that the size of this must range from 1-32. do we need to enforce this?
88        values: &mut [c_float], // TODO is this &mut correct? does fmod perform any writes?
89        ignore_seek_speed: bool,
90    ) -> Result<()> {
91        // TODO don't panic, return result
92        assert_eq!(ids.len(), values.len());
93
94        unsafe {
95            FMOD_Studio_System_SetParametersByIDs(
96                self.inner.as_ptr(),
97                ids.as_ptr().cast(),
98                values.as_mut_ptr(),
99                ids.len() as c_int,
100                ignore_seek_speed.into(),
101            )
102            .to_result()
103        }
104    }
105
106    /// Retrieves a global parameter value by name.
107    ///
108    /// The second tuple field is the final value of the parameter after applying adjustments due to automation, modulation, seek speed, and parameter velocity to value.
109    /// This is calculated asynchronously when the Studio system updates.
110    pub fn get_parameter_by_name(&self, name: &Utf8CStr) -> Result<(c_float, c_float)> {
111        let mut value = 0.0;
112        let mut final_value = 0.0;
113
114        unsafe {
115            FMOD_Studio_System_GetParameterByName(
116                self.inner.as_ptr(),
117                name.as_ptr(),
118                &raw mut value,
119                &raw mut final_value,
120            )
121            .to_result()?;
122        }
123
124        Ok((value, final_value))
125    }
126
127    /// Sets a global parameter value by name.
128    pub fn set_parameter_by_name(
129        &self,
130        name: &Utf8CStr,
131        value: c_float,
132        ignore_seek_speed: bool,
133    ) -> Result<()> {
134        unsafe {
135            FMOD_Studio_System_SetParameterByName(
136                self.inner.as_ptr(),
137                name.as_ptr(),
138                value,
139                ignore_seek_speed.into(),
140            )
141            .to_result()
142        }
143    }
144
145    /// Sets a global parameter value by name, looking up the value label.
146    ///
147    /// If the specified label is not found, [`FMOD_RESULT::FMOD_ERR_EVENT_NOTFOUND`] is returned. This lookup is case sensitive.
148    pub fn set_parameter_by_name_with_label(
149        &self,
150        name: &Utf8CStr,
151        label: &Utf8CStr,
152        ignore_seek_speed: bool,
153    ) -> Result<()> {
154        unsafe {
155            FMOD_Studio_System_SetParameterByNameWithLabel(
156                self.inner.as_ptr(),
157                name.as_ptr(),
158                label.as_ptr(),
159                ignore_seek_speed.into(),
160            )
161            .to_result()
162        }
163    }
164
165    /// Retrieves a global parameter by name or path.
166    ///
167    /// `name` can be the short name (such as `Wind`) or the full path (such as `parameter:/Ambience/Wind`).
168    /// Path lookups will only succeed if the strings bank has been loaded.
169    pub fn get_parameter_description_by_name(
170        &self,
171        name: &Utf8CStr,
172    ) -> Result<ParameterDescription> {
173        let mut description = MaybeUninit::zeroed();
174        unsafe {
175            FMOD_Studio_System_GetParameterDescriptionByName(
176                self.inner.as_ptr(),
177                name.as_ptr(),
178                description.as_mut_ptr(),
179            )
180            .to_result()?;
181
182            let description = ParameterDescription::from_ffi(description.assume_init());
183            Ok(description)
184        }
185    }
186
187    /// Retrieves a global parameter by ID.
188    pub fn get_parameter_description_by_id(&self, id: ParameterID) -> Result<ParameterDescription> {
189        let mut description = MaybeUninit::zeroed();
190        unsafe {
191            FMOD_Studio_System_GetParameterDescriptionByID(
192                self.inner.as_ptr(),
193                id.into(),
194                description.as_mut_ptr(),
195            )
196            .to_result()?;
197
198            let description = ParameterDescription::from_ffi(description.assume_init());
199            Ok(description)
200        }
201    }
202
203    /// Retrieves the number of global parameters.
204    pub fn parameter_description_count(&self) -> Result<c_int> {
205        let mut count = 0;
206        unsafe {
207            FMOD_Studio_System_GetParameterDescriptionCount(self.inner.as_ptr(), &raw mut count)
208                .to_result()?;
209        }
210        Ok(count)
211    }
212
213    /// Retrieves a list of global parameters.
214    pub fn get_parameter_description_list(&self) -> Result<Vec<ParameterDescription>> {
215        let expected_count = self.parameter_description_count()?;
216        let mut count = 0;
217        let mut list = vec![MaybeUninit::zeroed(); expected_count as usize];
218
219        unsafe {
220            FMOD_Studio_System_GetParameterDescriptionList(
221                self.inner.as_ptr(),
222                // bank is repr transparent and has the same layout as *mut FMOD_STUDIO_BANK, so this cast is ok
223                list.as_mut_ptr()
224                    .cast::<FMOD_STUDIO_PARAMETER_DESCRIPTION>(),
225                list.capacity() as c_int,
226                &raw mut count,
227            )
228            .to_result()?;
229
230            debug_assert_eq!(count, expected_count);
231
232            let list = list
233                .into_iter()
234                .map(|uninit| {
235                    let description = uninit.assume_init();
236                    ParameterDescription::from_ffi(description)
237                })
238                .collect();
239
240            Ok(list)
241        }
242    }
243
244    /// Retrieves a global parameter label by name or path.
245    ///
246    /// `name` can be the short name (such as `Wind`) or the full path (such as `parameter:/Ambience/Wind`).
247    /// Path lookups will only succeed if the strings bank has been loaded.
248    pub fn get_parameter_label_by_name(
249        &self,
250        name: &Utf8CStr,
251        label_index: c_int,
252    ) -> Result<Utf8CString> {
253        get_string_out_size(|path, size, ret| unsafe {
254            FMOD_Studio_System_GetParameterLabelByName(
255                self.inner.as_ptr(),
256                name.as_ptr(),
257                label_index,
258                path,
259                size,
260                ret,
261            )
262        })
263    }
264
265    /// Retrieves a global parameter label by ID.
266    pub fn get_parameter_label_by_id(
267        &self,
268        id: ParameterID,
269        label_index: c_int,
270    ) -> Result<Utf8CString> {
271        get_string_out_size(|path, size, ret| unsafe {
272            FMOD_Studio_System_GetParameterLabelByID(
273                self.inner.as_ptr(),
274                id.into(),
275                label_index,
276                path,
277                size,
278                ret,
279            )
280        })
281    }
282}