use aether_midi::tuning::TuningTable;
use std::ffi::c_char;
#[repr(C)]
pub struct AetherTuningTable {
_private: [u8; 0],
}
#[repr(C)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum AetherResult {
Ok = 0,
ErrorNullPointer = 1,
ErrorInvalidNote = 2,
ErrorUnknown = 99,
}
unsafe fn tuning_from_handle(handle: *const AetherTuningTable) -> Option<&'static TuningTable> {
if handle.is_null() {
return None;
}
Some(&*(handle as *const TuningTable))
}
const CONCERT_A: f32 = 440.0;
#[no_mangle]
pub extern "C" fn aether_tuning_ethiopian_tizita() -> *mut AetherTuningTable {
let tuning = Box::new(TuningTable::ethiopian_tizita(CONCERT_A));
Box::into_raw(tuning) as *mut AetherTuningTable
}
#[no_mangle]
pub extern "C" fn aether_tuning_ethiopian_tizita_minor() -> *mut AetherTuningTable {
let tuning = Box::new(TuningTable::ethiopian_tizita_minor(CONCERT_A));
Box::into_raw(tuning) as *mut AetherTuningTable
}
#[no_mangle]
pub extern "C" fn aether_tuning_ethiopian_bati() -> *mut AetherTuningTable {
let tuning = Box::new(TuningTable::ethiopian_bati(CONCERT_A));
Box::into_raw(tuning) as *mut AetherTuningTable
}
#[no_mangle]
pub extern "C" fn aether_tuning_ethiopian_bati_major() -> *mut AetherTuningTable {
let tuning = Box::new(TuningTable::ethiopian_bati_major(CONCERT_A));
Box::into_raw(tuning) as *mut AetherTuningTable
}
#[no_mangle]
pub extern "C" fn aether_tuning_ethiopian_ambassel() -> *mut AetherTuningTable {
let tuning = Box::new(TuningTable::ethiopian_ambassel(CONCERT_A));
Box::into_raw(tuning) as *mut AetherTuningTable
}
#[no_mangle]
pub extern "C" fn aether_tuning_ethiopian_anchihoye() -> *mut AetherTuningTable {
let tuning = Box::new(TuningTable::ethiopian_anchihoye(CONCERT_A));
Box::into_raw(tuning) as *mut AetherTuningTable
}
#[no_mangle]
pub extern "C" fn aether_tuning_arabic_rast() -> *mut AetherTuningTable {
let tuning = Box::new(TuningTable::arabic_maqam_rast(CONCERT_A));
Box::into_raw(tuning) as *mut AetherTuningTable
}
#[no_mangle]
pub extern "C" fn aether_tuning_arabic_bayati() -> *mut AetherTuningTable {
let tuning = Box::new(TuningTable::arabic_maqam_bayati(CONCERT_A));
Box::into_raw(tuning) as *mut AetherTuningTable
}
#[no_mangle]
pub extern "C" fn aether_tuning_arabic_hijaz() -> *mut AetherTuningTable {
let tuning = Box::new(TuningTable::arabic_maqam_hijaz(CONCERT_A));
Box::into_raw(tuning) as *mut AetherTuningTable
}
#[no_mangle]
pub extern "C" fn aether_tuning_indian_yaman() -> *mut AetherTuningTable {
let tuning = Box::new(TuningTable::indian_raga_yaman(CONCERT_A));
Box::into_raw(tuning) as *mut AetherTuningTable
}
#[no_mangle]
pub extern "C" fn aether_tuning_gamelan_slendro() -> *mut AetherTuningTable {
let tuning = Box::new(TuningTable::gamelan_slendro(CONCERT_A));
Box::into_raw(tuning) as *mut AetherTuningTable
}
#[no_mangle]
pub extern "C" fn aether_tuning_gamelan_slendro_stretched() -> *mut AetherTuningTable {
let tuning = Box::new(TuningTable::gamelan_slendro_stretched(CONCERT_A));
Box::into_raw(tuning) as *mut AetherTuningTable
}
#[no_mangle]
pub extern "C" fn aether_tuning_gamelan_pelog() -> *mut AetherTuningTable {
let tuning = Box::new(TuningTable::gamelan_pelog(CONCERT_A));
Box::into_raw(tuning) as *mut AetherTuningTable
}
#[no_mangle]
pub extern "C" fn aether_tuning_just_intonation() -> *mut AetherTuningTable {
let tuning = Box::new(TuningTable::just_intonation(CONCERT_A));
Box::into_raw(tuning) as *mut AetherTuningTable
}
#[no_mangle]
pub extern "C" fn aether_tuning_just_intonation_7_limit() -> *mut AetherTuningTable {
let tuning = Box::new(TuningTable::just_intonation_7_limit(CONCERT_A));
Box::into_raw(tuning) as *mut AetherTuningTable
}
#[no_mangle]
pub extern "C" fn aether_tuning_equal_temperament() -> *mut AetherTuningTable {
let tuning = Box::new(TuningTable::equal_temperament(CONCERT_A));
Box::into_raw(tuning) as *mut AetherTuningTable
}
#[no_mangle]
pub unsafe extern "C" fn aether_tuning_free(tuning: *mut AetherTuningTable) {
if !tuning.is_null() {
let _ = Box::from_raw(tuning as *mut TuningTable);
}
}
#[no_mangle]
pub unsafe extern "C" fn aether_tuning_get_frequency(
tuning: *const AetherTuningTable,
midi_note: u8,
out_frequency: *mut f32,
) -> AetherResult {
if out_frequency.is_null() {
return AetherResult::ErrorNullPointer;
}
let tuning = match tuning_from_handle(tuning) {
Some(t) => t,
None => return AetherResult::ErrorNullPointer,
};
*out_frequency = tuning.frequency(midi_note);
AetherResult::Ok
}
#[no_mangle]
pub unsafe extern "C" fn aether_tuning_get_all_frequencies(
tuning: *const AetherTuningTable,
out_frequencies: *mut f32,
) -> AetherResult {
if out_frequencies.is_null() {
return AetherResult::ErrorNullPointer;
}
let tuning = match tuning_from_handle(tuning) {
Some(t) => t,
None => return AetherResult::ErrorNullPointer,
};
let out_slice = std::slice::from_raw_parts_mut(out_frequencies, 128);
for (i, freq) in out_slice.iter_mut().enumerate() {
*freq = tuning.frequency(i as u8);
}
AetherResult::Ok
}
#[no_mangle]
pub extern "C" fn aether_version() -> *const c_char {
concat!(env!("CARGO_PKG_VERSION"), "\0").as_ptr() as *const c_char
}
#[no_mangle]
pub extern "C" fn aether_tuning_count() -> u32 {
17
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_tuning_lifecycle() {
unsafe {
let tuning = aether_tuning_ethiopian_tizita();
assert!(!tuning.is_null());
let mut freq = 0.0f32;
let result = aether_tuning_get_frequency(tuning, 60, &mut freq);
assert_eq!(result, AetherResult::Ok);
assert!(freq > 200.0 && freq < 300.0);
aether_tuning_free(tuning);
}
}
#[test]
fn test_all_tuning_systems() {
unsafe {
let tunings = [
aether_tuning_ethiopian_tizita(),
aether_tuning_ethiopian_bati(),
aether_tuning_ethiopian_ambassel(),
aether_tuning_arabic_rast(),
aether_tuning_arabic_bayati(),
aether_tuning_arabic_hijaz(),
aether_tuning_indian_yaman(),
aether_tuning_gamelan_slendro(),
aether_tuning_gamelan_slendro_stretched(),
aether_tuning_gamelan_pelog(),
aether_tuning_just_intonation(),
aether_tuning_just_intonation_7_limit(),
aether_tuning_equal_temperament(),
];
for tuning in &tunings {
assert!(!tuning.is_null());
let mut freq = 0.0f32;
let result = aether_tuning_get_frequency(*tuning, 60, &mut freq);
assert_eq!(result, AetherResult::Ok);
assert!(freq > 0.0);
}
for tuning in tunings {
aether_tuning_free(tuning);
}
}
}
#[test]
fn test_get_all_frequencies() {
unsafe {
let tuning = aether_tuning_arabic_hijaz();
let mut frequencies = [0.0f32; 128];
let result = aether_tuning_get_all_frequencies(tuning, frequencies.as_mut_ptr());
assert_eq!(result, AetherResult::Ok);
for (i, freq) in frequencies.iter().enumerate() {
assert!(*freq > 0.0, "Note {} has invalid frequency", i);
}
aether_tuning_free(tuning);
}
}
#[test]
fn test_version() {
let version = aether_version();
assert!(!version.is_null());
}
#[test]
fn test_tuning_count() {
assert_eq!(aether_tuning_count(), 17);
}
}