essentia_core/algorithm/
algorithm.rs

1use cxx::UniquePtr;
2use essentia_sys::ffi;
3use std::marker::PhantomData;
4
5use crate::{
6    algorithm::{
7        ComputeError, ConfigurationError, InputError, Introspection, OutputError, ParameterError,
8        ResetError,
9    },
10    data::types::HasDataType,
11    data::{DataContainer, InputOutputData, ParameterData, TryIntoDataContainer},
12    essentia::Essentia,
13    parameter_map::ParameterMap,
14};
15
16pub struct Initialized {
17    parameter_map: ParameterMap,
18}
19
20pub struct Configured;
21
22pub struct Algorithm<'a, State = Initialized> {
23    algorithm_bridge: UniquePtr<ffi::AlgorithmBridge>,
24    state: State,
25    introspection: Introspection,
26    _marker: PhantomData<&'a Essentia>,
27}
28
29impl<'a, State> Algorithm<'a, State> {
30    pub fn introspection(&self) -> &Introspection {
31        &self.introspection
32    }
33}
34
35impl<'a> Algorithm<'a, Initialized> {
36    pub(crate) fn new(algorithm_bridge: UniquePtr<ffi::AlgorithmBridge>) -> Self {
37        let introspection = Introspection::from_algorithm_bridge(&algorithm_bridge);
38
39        Self {
40            algorithm_bridge,
41            state: Initialized {
42                parameter_map: ParameterMap::new(),
43            },
44            introspection,
45            _marker: PhantomData,
46        }
47    }
48
49    pub fn parameter<T>(
50        mut self,
51        key: &str,
52        value: impl TryIntoDataContainer<T>,
53    ) -> Result<Self, ParameterError>
54    where
55        T: ParameterData + HasDataType,
56    {
57        self.set_parameter(key, value)?;
58        Ok(self)
59    }
60
61    pub fn set_parameter<T>(
62        &mut self,
63        key: &str,
64        value: impl TryIntoDataContainer<T>,
65    ) -> Result<(), ParameterError>
66    where
67        T: ParameterData + HasDataType,
68    {
69        let param_info = self.introspection.get_parameter(key).ok_or_else(|| {
70            ParameterError::ParameterNotFound {
71                parameter: key.to_string(),
72            }
73        })?;
74
75        let expected_type = T::data_type();
76        let param_data_type = param_info.parameter_type();
77
78        if param_data_type != expected_type {
79            return Err(ParameterError::TypeMismatch {
80                parameter: key.to_string(),
81                expected: expected_type,
82                actual: param_data_type,
83            });
84        }
85
86        let variant_data =
87            value
88                .try_into_data_container()
89                .map_err(|error| ParameterError::DataConversion {
90                    parameter: key.to_string(),
91                    source: error,
92                })?;
93
94        self.state.parameter_map.set_parameter(key, variant_data);
95
96        Ok(())
97    }
98
99    pub fn configure(mut self) -> Result<Algorithm<'a, Configured>, ConfigurationError> {
100        self.algorithm_bridge
101            .pin_mut()
102            .configure(self.state.parameter_map.parameter_map_bridge)?;
103
104        Ok(Algorithm {
105            algorithm_bridge: self.algorithm_bridge,
106            state: Configured,
107            introspection: self.introspection,
108            _marker: PhantomData,
109        })
110    }
111}
112
113impl<'a> Algorithm<'a, Configured> {
114    pub fn input<T>(
115        mut self,
116        key: &str,
117        value: impl TryIntoDataContainer<T>,
118    ) -> Result<Self, InputError>
119    where
120        T: InputOutputData + HasDataType,
121    {
122        self.set_input(key, value)?;
123        Ok(self)
124    }
125
126    pub fn set_input<T>(
127        &mut self,
128        key: &str,
129        value: impl TryIntoDataContainer<T>,
130    ) -> Result<(), InputError>
131    where
132        T: InputOutputData + HasDataType,
133    {
134        let input_info =
135            self.introspection
136                .get_input(key)
137                .ok_or_else(|| InputError::InputNotFound {
138                    input: key.to_string(),
139                })?;
140
141        let expected_type = T::data_type();
142        let input_data_type = input_info.input_output_type();
143
144        if input_data_type != expected_type {
145            return Err(InputError::TypeMismatch {
146                input: key.to_string(),
147                expected: expected_type,
148                actual: input_data_type,
149            });
150        }
151
152        let variant_data =
153            value
154                .try_into_data_container()
155                .map_err(|error| InputError::DataConversion {
156                    input: key.to_string(),
157                    source: error,
158                })?;
159
160        let owned_ptr = variant_data.into_owned_ptr();
161
162        self.algorithm_bridge
163            .pin_mut()
164            .set_input(key, owned_ptr)
165            .map_err(|exception| InputError::Internal {
166                input: key.to_string(),
167                source: exception,
168            })?;
169
170        Ok(())
171    }
172
173    pub fn compute(&mut self) -> Result<ComputeResult<'a, '_>, ComputeError> {
174        for output in self.introspection.outputs() {
175            let data_type = output.input_output_type();
176
177            self.algorithm_bridge
178                .pin_mut()
179                .setup_output(output.name(), data_type.into())
180                .map_err(|exception| ComputeError::OutputSetup {
181                    output: output.name().to_string(),
182                    source: exception,
183                })?;
184        }
185
186        self.algorithm_bridge
187            .pin_mut()
188            .compute()
189            .map_err(ComputeError::Compute)?;
190
191        Ok(ComputeResult { algorithm: self })
192    }
193
194    pub fn reset(&mut self) -> Result<(), ResetError> {
195        self.algorithm_bridge
196            .pin_mut()
197            .reset()
198            .map_err(ResetError::Internal)
199    }
200}
201
202pub struct ComputeResult<'algorithm, 'result> {
203    algorithm: &'result Algorithm<'algorithm, Configured>,
204}
205
206impl<'algorithm, 'result> ComputeResult<'algorithm, 'result> {
207    pub fn output<T>(&self, key: &str) -> Result<DataContainer<'result, T>, OutputError>
208    where
209        T: InputOutputData + HasDataType,
210    {
211        let output_info = self
212            .algorithm
213            .introspection
214            .get_output(key)
215            .ok_or_else(|| OutputError::OutputNotFound {
216                output: key.to_string(),
217            })?;
218
219        let expected_type = T::data_type();
220        let output_data_type = output_info.input_output_type();
221
222        if output_data_type != expected_type {
223            return Err(OutputError::TypeMismatch {
224                output: key.to_string(),
225                expected: expected_type,
226                actual: output_data_type,
227            });
228        }
229
230        let variant_data = self
231            .algorithm
232            .algorithm_bridge
233            .get_output(key)
234            .map(|ffi_variant_data| DataContainer::new_borrowed(ffi_variant_data))
235            .map_err(|exception| OutputError::Internal {
236                output: key.to_string(),
237                source: exception,
238            })?;
239
240        Ok(variant_data)
241    }
242}