1use essentia_sys::ffi;
2use ndarray::{Array2, Array4};
3use std::collections::HashMap;
4
5use crate::{ConversionError, DataContainer, phantom};
6
7pub trait IntoDataContainer<T> {
8 fn into_data_container(self) -> DataContainer<'static, T>;
9}
10
11pub trait TryIntoDataContainer<T> {
12 fn try_into_data_container(self) -> Result<DataContainer<'static, T>, ConversionError>;
13}
14
15impl<T, V> TryIntoDataContainer<T> for V
16where
17 V: IntoDataContainer<T>,
18{
19 fn try_into_data_container(self) -> Result<DataContainer<'static, T>, ConversionError> {
20 Ok(self.into_data_container())
21 }
22}
23
24impl<'a, T> IntoDataContainer<T> for DataContainer<'a, T> {
25 fn into_data_container(self) -> DataContainer<'static, T> {
26 let owned_ptr = self.into_owned_ptr();
27 DataContainer::new_owned(owned_ptr)
28 }
29}
30
31impl IntoDataContainer<phantom::Bool> for bool {
32 fn into_data_container(self) -> DataContainer<'static, phantom::Bool> {
33 DataContainer::new_owned(ffi::create_data_container_from_bool(self))
34 }
35}
36
37impl IntoDataContainer<phantom::String> for &str {
38 fn into_data_container(self) -> DataContainer<'static, phantom::String> {
39 DataContainer::new_owned(ffi::create_data_container_from_string(self))
40 }
41}
42
43impl IntoDataContainer<phantom::Int> for i32 {
44 fn into_data_container(self) -> DataContainer<'static, phantom::Int> {
45 DataContainer::new_owned(ffi::create_data_container_from_int(self))
46 }
47}
48
49impl IntoDataContainer<phantom::Float> for f32 {
50 fn into_data_container(self) -> DataContainer<'static, phantom::Float> {
51 DataContainer::new_owned(ffi::create_data_container_from_float(self))
52 }
53}
54
55impl IntoDataContainer<phantom::UnsignedInt> for u32 {
56 fn into_data_container(self) -> DataContainer<'static, phantom::UnsignedInt> {
57 DataContainer::new_owned(ffi::create_data_container_from_unsigned_int(self))
58 }
59}
60
61impl IntoDataContainer<phantom::Long> for i64 {
62 fn into_data_container(self) -> DataContainer<'static, phantom::Long> {
63 DataContainer::new_owned(ffi::create_data_container_from_long(self))
64 }
65}
66
67impl IntoDataContainer<phantom::StereoSample> for ffi::StereoSample {
68 fn into_data_container(self) -> DataContainer<'static, phantom::StereoSample> {
69 DataContainer::new_owned(ffi::create_data_container_from_stereo_sample(self))
70 }
71}
72
73impl IntoDataContainer<phantom::Complex> for num::Complex<f32> {
74 fn into_data_container(self) -> DataContainer<'static, phantom::Complex> {
75 DataContainer::new_owned(ffi::create_data_container_from_complex(ffi::Complex {
76 real: self.re,
77 imag: self.im,
78 }))
79 }
80}
81
82impl IntoDataContainer<phantom::TensorFloat> for &Array4<f32> {
83 fn into_data_container(self) -> DataContainer<'static, phantom::TensorFloat> {
84 let slice = self.as_slice().expect("Array must be contiguous");
85 let shape = [
86 self.shape()[0],
87 self.shape()[1],
88 self.shape()[2],
89 self.shape()[3],
90 ];
91
92 DataContainer::new_owned(ffi::create_data_container_from_tensor_float(
93 ffi::TensorFloat {
94 slice,
95 shape: &shape,
96 },
97 ))
98 }
99}
100
101impl IntoDataContainer<phantom::VectorBool> for &[bool] {
102 fn into_data_container(self) -> DataContainer<'static, phantom::VectorBool> {
103 DataContainer::new_owned(ffi::create_data_container_from_vector_bool(self))
104 }
105}
106
107impl IntoDataContainer<phantom::VectorInt> for &[i32] {
108 fn into_data_container(self) -> DataContainer<'static, phantom::VectorInt> {
109 DataContainer::new_owned(ffi::create_data_container_from_vector_int(self))
110 }
111}
112
113impl IntoDataContainer<phantom::VectorString> for &[&str] {
114 fn into_data_container(self) -> DataContainer<'static, phantom::VectorString> {
115 DataContainer::new_owned(ffi::create_data_container_from_vector_string(self))
116 }
117}
118
119impl IntoDataContainer<phantom::VectorFloat> for &[f32] {
120 fn into_data_container(self) -> DataContainer<'static, phantom::VectorFloat> {
121 DataContainer::new_owned(ffi::create_data_container_from_vector_float(self))
122 }
123}
124
125impl IntoDataContainer<phantom::VectorStereoSample> for &[ffi::StereoSample] {
126 fn into_data_container(self) -> DataContainer<'static, phantom::VectorStereoSample> {
127 DataContainer::new_owned(ffi::create_data_container_from_vector_stereo_sample(self))
128 }
129}
130
131impl IntoDataContainer<phantom::VectorComplex> for &[num::Complex<f32>] {
132 fn into_data_container(self) -> DataContainer<'static, phantom::VectorComplex> {
133 let ffi_vec: Vec<ffi::Complex> = self
134 .iter()
135 .map(|c| ffi::Complex {
136 real: c.re,
137 imag: c.im,
138 })
139 .collect();
140 DataContainer::new_owned(ffi::create_data_container_from_vector_complex(&ffi_vec))
141 }
142}
143
144impl IntoDataContainer<phantom::VectorVectorFloat> for &[Vec<f32>] {
145 fn into_data_container(self) -> DataContainer<'static, phantom::VectorVectorFloat> {
146 DataContainer::new_owned(ffi::create_data_container_from_vector_vector_float(
147 self.iter()
148 .map(|item| ffi::SliceFloat {
149 slice: item.as_slice(),
150 })
151 .collect(),
152 ))
153 }
154}
155
156impl IntoDataContainer<phantom::MatrixFloat> for &Array2<f32> {
157 fn into_data_container(self) -> DataContainer<'static, phantom::MatrixFloat> {
158 let slice = self.as_slice().expect("Array must be contiguous");
159 let (dim1, dim2) = self.dim();
160
161 DataContainer::new_owned(ffi::create_data_container_from_matrix_float(
162 ffi::MatrixFloat { slice, dim1, dim2 },
163 ))
164 }
165}
166
167impl IntoDataContainer<phantom::VectorVectorString> for &[&[&str]] {
168 fn into_data_container(self) -> DataContainer<'static, phantom::VectorVectorString> {
169 DataContainer::new_owned(ffi::create_data_container_from_vector_vector_string(
170 self.iter()
171 .map(|item| ffi::VecString {
172 vec: item.iter().map(|s| s.to_string()).collect(),
173 })
174 .collect(),
175 ))
176 }
177}
178
179impl IntoDataContainer<phantom::VectorVectorStereoSample> for &[&[ffi::StereoSample]] {
180 fn into_data_container(self) -> DataContainer<'static, phantom::VectorVectorStereoSample> {
181 DataContainer::new_owned(ffi::create_data_container_from_vector_vector_stereo_sample(
182 self.iter()
183 .map(|item| ffi::SliceStereoSample { slice: item })
184 .collect(),
185 ))
186 }
187}
188
189impl IntoDataContainer<phantom::VectorVectorComplex> for &[Vec<num::Complex<f32>>] {
190 fn into_data_container(self) -> DataContainer<'static, phantom::VectorVectorComplex> {
191 DataContainer::new_owned(ffi::create_data_container_from_vector_vector_complex(
192 self.iter()
193 .map(|item| ffi::VecComplex {
194 vec: item
195 .iter()
196 .map(|c| ffi::Complex {
197 real: c.re,
198 imag: c.im,
199 })
200 .collect(),
201 })
202 .collect(),
203 ))
204 }
205}
206
207impl IntoDataContainer<phantom::VectorMatrixFloat> for &[Array2<f32>] {
208 fn into_data_container(self) -> DataContainer<'static, phantom::VectorMatrixFloat> {
209 DataContainer::new_owned(ffi::create_data_container_from_vector_matrix_float(
210 self.iter()
211 .map(|array| {
212 let slice = array.as_slice().expect("Array must be contiguous");
213 let (dim1, dim2) = array.dim();
214 ffi::MatrixFloat { slice, dim1, dim2 }
215 })
216 .collect(),
217 ))
218 }
219}
220
221impl IntoDataContainer<phantom::MapVectorFloat> for &HashMap<String, Vec<f32>> {
222 fn into_data_container(self) -> DataContainer<'static, phantom::MapVectorFloat> {
223 DataContainer::new_owned(ffi::create_data_container_from_map_vector_float(
224 self.iter()
225 .map(|(key, vec)| ffi::MapEntryVectorFloat {
226 key: key.clone(),
227 value: vec.as_slice(),
228 })
229 .collect(),
230 ))
231 }
232}
233
234impl IntoDataContainer<phantom::MapVectorString> for &HashMap<String, Vec<String>> {
235 fn into_data_container(self) -> DataContainer<'static, phantom::MapVectorString> {
236 DataContainer::new_owned(ffi::create_data_container_from_map_vector_string(
237 self.iter()
238 .map(|(key, vec)| ffi::MapEntryVectorString {
239 key: key.clone(),
240 value: vec.clone(),
241 })
242 .collect(),
243 ))
244 }
245}
246
247impl IntoDataContainer<phantom::MapVectorInt> for &HashMap<String, Vec<i32>> {
248 fn into_data_container(self) -> DataContainer<'static, phantom::MapVectorInt> {
249 DataContainer::new_owned(ffi::create_data_container_from_map_vector_int(
250 self.iter()
251 .map(|(key, vec)| ffi::MapEntryVectorInt {
252 key: key.clone(),
253 value: vec.as_slice(),
254 })
255 .collect(),
256 ))
257 }
258}
259
260impl IntoDataContainer<phantom::MapVectorComplex> for &HashMap<String, Vec<num::Complex<f32>>> {
261 fn into_data_container(self) -> DataContainer<'static, phantom::MapVectorComplex> {
262 let converted_data: Vec<(String, Vec<ffi::Complex>)> = self
263 .iter()
264 .map(|(key, vec)| {
265 (
266 key.clone(),
267 vec.iter()
268 .map(|c| ffi::Complex {
269 real: c.re,
270 imag: c.im,
271 })
272 .collect(),
273 )
274 })
275 .collect();
276
277 let entries: Vec<ffi::MapEntryVectorComplex> = converted_data
278 .iter()
279 .map(|(key, ffi_vec)| ffi::MapEntryVectorComplex {
280 key: key.clone(),
281 value: ffi_vec.as_slice(),
282 })
283 .collect();
284
285 DataContainer::new_owned(ffi::create_data_container_from_map_vector_complex(entries))
286 }
287}
288
289impl IntoDataContainer<phantom::MapFloat> for &HashMap<String, f32> {
290 fn into_data_container(self) -> DataContainer<'static, phantom::MapFloat> {
291 DataContainer::new_owned(ffi::create_data_container_from_map_float(
292 self.iter()
293 .map(|(key, &val)| ffi::MapEntryFloat {
294 key: key.clone(),
295 value: val,
296 })
297 .collect(),
298 ))
299 }
300}
301
302impl TryIntoDataContainer<phantom::MatrixFloat> for &[Vec<f32>] {
303 fn try_into_data_container(
304 self,
305 ) -> Result<DataContainer<'static, phantom::MatrixFloat>, ConversionError> {
306 if self.is_empty() {
307 return Err(ConversionError::InvalidFormat {
308 message: "Cannot create matrix from empty vector".to_string(),
309 });
310 }
311
312 let expected_cols = self[0].len();
313 if expected_cols == 0 {
314 return Err(ConversionError::InvalidFormat {
315 message: "Cannot create matrix from empty rows".to_string(),
316 });
317 }
318
319 for (row_idx, row) in self.iter().enumerate() {
320 if row.len() != expected_cols {
321 return Err(ConversionError::InvalidFormat {
322 message: format!(
323 "Non-rectangular matrix: row {} has {} elements, expected {}",
324 row_idx,
325 row.len(),
326 expected_cols
327 ),
328 });
329 }
330 }
331
332 let mut flat_data = Vec::with_capacity(self.len() * expected_cols);
333 for row in self {
334 flat_data.extend(row);
335 }
336
337 let dim1 = flat_data.len() / expected_cols;
338 let dim2 = expected_cols;
339
340 Ok(DataContainer::new_owned(
341 ffi::create_data_container_from_matrix_float(ffi::MatrixFloat {
342 slice: &flat_data,
343 dim1,
344 dim2,
345 }),
346 ))
347 }
348}
349
350impl IntoDataContainer<phantom::Pool> for crate::pool::Pool {
351 fn into_data_container(self) -> DataContainer<'static, phantom::Pool> {
352 DataContainer::new_owned(ffi::create_data_container_from_pool(self.into_owned_ptr()))
353 }
354}