aetherdsp_juce_bridge/
lib.rs1use aether_midi::tuning::TuningTable;
25use std::ffi::c_char;
26
27#[repr(C)]
33pub struct AetherTuningTable {
34 _private: [u8; 0],
35}
36
37#[repr(C)]
39#[derive(Debug, Clone, Copy, PartialEq, Eq)]
40pub enum AetherResult {
41 Ok = 0,
42 ErrorNullPointer = 1,
43 ErrorInvalidNote = 2,
44 ErrorUnknown = 99,
45}
46
47unsafe fn tuning_from_handle(handle: *const AetherTuningTable) -> Option<&'static TuningTable> {
52 if handle.is_null() {
53 return None;
54 }
55 Some(&*(handle as *const TuningTable))
56}
57
58const CONCERT_A: f32 = 440.0;
60
61#[no_mangle]
67pub extern "C" fn aether_tuning_ethiopian_tizita() -> *mut AetherTuningTable {
68 let tuning = Box::new(TuningTable::ethiopian_tizita(CONCERT_A));
69 Box::into_raw(tuning) as *mut AetherTuningTable
70}
71
72#[no_mangle]
74pub extern "C" fn aether_tuning_ethiopian_tizita_minor() -> *mut AetherTuningTable {
75 let tuning = Box::new(TuningTable::ethiopian_tizita_minor(CONCERT_A));
76 Box::into_raw(tuning) as *mut AetherTuningTable
77}
78
79#[no_mangle]
81pub extern "C" fn aether_tuning_ethiopian_bati() -> *mut AetherTuningTable {
82 let tuning = Box::new(TuningTable::ethiopian_bati(CONCERT_A));
83 Box::into_raw(tuning) as *mut AetherTuningTable
84}
85
86#[no_mangle]
88pub extern "C" fn aether_tuning_ethiopian_bati_major() -> *mut AetherTuningTable {
89 let tuning = Box::new(TuningTable::ethiopian_bati_major(CONCERT_A));
90 Box::into_raw(tuning) as *mut AetherTuningTable
91}
92
93#[no_mangle]
95pub extern "C" fn aether_tuning_ethiopian_ambassel() -> *mut AetherTuningTable {
96 let tuning = Box::new(TuningTable::ethiopian_ambassel(CONCERT_A));
97 Box::into_raw(tuning) as *mut AetherTuningTable
98}
99
100#[no_mangle]
102pub extern "C" fn aether_tuning_ethiopian_anchihoye() -> *mut AetherTuningTable {
103 let tuning = Box::new(TuningTable::ethiopian_anchihoye(CONCERT_A));
104 Box::into_raw(tuning) as *mut AetherTuningTable
105}
106
107#[no_mangle]
109pub extern "C" fn aether_tuning_arabic_rast() -> *mut AetherTuningTable {
110 let tuning = Box::new(TuningTable::arabic_maqam_rast(CONCERT_A));
111 Box::into_raw(tuning) as *mut AetherTuningTable
112}
113
114#[no_mangle]
116pub extern "C" fn aether_tuning_arabic_bayati() -> *mut AetherTuningTable {
117 let tuning = Box::new(TuningTable::arabic_maqam_bayati(CONCERT_A));
118 Box::into_raw(tuning) as *mut AetherTuningTable
119}
120
121#[no_mangle]
123pub extern "C" fn aether_tuning_arabic_hijaz() -> *mut AetherTuningTable {
124 let tuning = Box::new(TuningTable::arabic_maqam_hijaz(CONCERT_A));
125 Box::into_raw(tuning) as *mut AetherTuningTable
126}
127
128#[no_mangle]
130pub extern "C" fn aether_tuning_indian_yaman() -> *mut AetherTuningTable {
131 let tuning = Box::new(TuningTable::indian_raga_yaman(CONCERT_A));
132 Box::into_raw(tuning) as *mut AetherTuningTable
133}
134
135#[no_mangle]
137pub extern "C" fn aether_tuning_gamelan_slendro() -> *mut AetherTuningTable {
138 let tuning = Box::new(TuningTable::gamelan_slendro(CONCERT_A));
139 Box::into_raw(tuning) as *mut AetherTuningTable
140}
141
142#[no_mangle]
144pub extern "C" fn aether_tuning_gamelan_slendro_stretched() -> *mut AetherTuningTable {
145 let tuning = Box::new(TuningTable::gamelan_slendro_stretched(CONCERT_A));
146 Box::into_raw(tuning) as *mut AetherTuningTable
147}
148
149#[no_mangle]
151pub extern "C" fn aether_tuning_gamelan_pelog() -> *mut AetherTuningTable {
152 let tuning = Box::new(TuningTable::gamelan_pelog(CONCERT_A));
153 Box::into_raw(tuning) as *mut AetherTuningTable
154}
155
156#[no_mangle]
158pub extern "C" fn aether_tuning_just_intonation() -> *mut AetherTuningTable {
159 let tuning = Box::new(TuningTable::just_intonation(CONCERT_A));
160 Box::into_raw(tuning) as *mut AetherTuningTable
161}
162
163#[no_mangle]
165pub extern "C" fn aether_tuning_just_intonation_7_limit() -> *mut AetherTuningTable {
166 let tuning = Box::new(TuningTable::just_intonation_7_limit(CONCERT_A));
167 Box::into_raw(tuning) as *mut AetherTuningTable
168}
169
170#[no_mangle]
172pub extern "C" fn aether_tuning_equal_temperament() -> *mut AetherTuningTable {
173 let tuning = Box::new(TuningTable::equal_temperament(CONCERT_A));
174 Box::into_raw(tuning) as *mut AetherTuningTable
175}
176
177#[no_mangle]
183pub unsafe extern "C" fn aether_tuning_free(tuning: *mut AetherTuningTable) {
184 if !tuning.is_null() {
185 let _ = Box::from_raw(tuning as *mut TuningTable);
186 }
187}
188
189#[no_mangle]
202pub unsafe extern "C" fn aether_tuning_get_frequency(
203 tuning: *const AetherTuningTable,
204 midi_note: u8,
205 out_frequency: *mut f32,
206) -> AetherResult {
207 if out_frequency.is_null() {
208 return AetherResult::ErrorNullPointer;
209 }
210
211 let tuning = match tuning_from_handle(tuning) {
212 Some(t) => t,
213 None => return AetherResult::ErrorNullPointer,
214 };
215
216 *out_frequency = tuning.frequency(midi_note);
217 AetherResult::Ok
218}
219
220#[no_mangle]
232pub unsafe extern "C" fn aether_tuning_get_all_frequencies(
233 tuning: *const AetherTuningTable,
234 out_frequencies: *mut f32,
235) -> AetherResult {
236 if out_frequencies.is_null() {
237 return AetherResult::ErrorNullPointer;
238 }
239
240 let tuning = match tuning_from_handle(tuning) {
241 Some(t) => t,
242 None => return AetherResult::ErrorNullPointer,
243 };
244
245 let out_slice = std::slice::from_raw_parts_mut(out_frequencies, 128);
246 for (i, freq) in out_slice.iter_mut().enumerate() {
247 *freq = tuning.frequency(i as u8);
248 }
249
250 AetherResult::Ok
251}
252
253#[no_mangle]
263pub extern "C" fn aether_version() -> *const c_char {
264 concat!(env!("CARGO_PKG_VERSION"), "\0").as_ptr() as *const c_char
265}
266
267#[no_mangle]
272pub extern "C" fn aether_tuning_count() -> u32 {
273 17
274}
275
276#[cfg(test)]
281mod tests {
282 use super::*;
283
284 #[test]
285 fn test_tuning_lifecycle() {
286 unsafe {
287 let tuning = aether_tuning_ethiopian_tizita();
288 assert!(!tuning.is_null());
289
290 let mut freq = 0.0f32;
291 let result = aether_tuning_get_frequency(tuning, 60, &mut freq);
292 assert_eq!(result, AetherResult::Ok);
293 assert!(freq > 200.0 && freq < 300.0); aether_tuning_free(tuning);
296 }
297 }
298
299 #[test]
300 fn test_all_tuning_systems() {
301 unsafe {
302 let tunings = [
303 aether_tuning_ethiopian_tizita(),
304 aether_tuning_ethiopian_bati(),
305 aether_tuning_ethiopian_ambassel(),
306 aether_tuning_arabic_rast(),
307 aether_tuning_arabic_bayati(),
308 aether_tuning_arabic_hijaz(),
309 aether_tuning_indian_yaman(),
310 aether_tuning_gamelan_slendro(),
311 aether_tuning_gamelan_slendro_stretched(),
312 aether_tuning_gamelan_pelog(),
313 aether_tuning_just_intonation(),
314 aether_tuning_just_intonation_7_limit(),
315 aether_tuning_equal_temperament(),
316 ];
317
318 for tuning in &tunings {
319 assert!(!tuning.is_null());
320
321 let mut freq = 0.0f32;
322 let result = aether_tuning_get_frequency(*tuning, 60, &mut freq);
323 assert_eq!(result, AetherResult::Ok);
324 assert!(freq > 0.0);
325 }
326
327 for tuning in tunings {
328 aether_tuning_free(tuning);
329 }
330 }
331 }
332
333 #[test]
334 fn test_get_all_frequencies() {
335 unsafe {
336 let tuning = aether_tuning_arabic_hijaz();
337 let mut frequencies = [0.0f32; 128];
338
339 let result = aether_tuning_get_all_frequencies(tuning, frequencies.as_mut_ptr());
340 assert_eq!(result, AetherResult::Ok);
341
342 for (i, freq) in frequencies.iter().enumerate() {
344 assert!(*freq > 0.0, "Note {} has invalid frequency", i);
345 }
346
347 aether_tuning_free(tuning);
348 }
349 }
350
351 #[test]
352 fn test_version() {
353 let version = aether_version();
354 assert!(!version.is_null());
355 }
356
357 #[test]
358 fn test_tuning_count() {
359 assert_eq!(aether_tuning_count(), 17);
360 }
361}