libfmod 2.222.6

A library wrapper for integrating FMOD Engine in Rust applications.
Documentation
use libfmod::ffi::{
    FMOD_DSP_PARAMETER_DESC_FLOAT, FMOD_DSP_PARAMETER_DESC_UNION, FMOD_DSP_STATE, FMOD_INIT_NORMAL,
    FMOD_LOOP_NORMAL, FMOD_OK, FMOD_RESULT,
};
use libfmod::{DspDescription, DspParameterDesc, DspParameterType, Error, System};
use std::os::raw::{c_char, c_float, c_int};
use std::ptr::null_mut;
use std::thread;
use std::time::Duration;

#[test]
fn test_system_advanced_settings_before_init() -> Result<(), Error> {
    let system = System::create()?;
    let settings = system.get_advanced_settings()?;
    println!("Settings: {:?}", settings);
    system.release()
}

#[test]
fn test_dsp_custom() -> Result<(), Error> {
    let system = System::create()?;
    system.init(32, FMOD_INIT_NORMAL, None)?;

    let sound = system.create_sound("./tests/data/Assets/1.ogg", FMOD_LOOP_NORMAL, None)?;
    system.play_sound(sound, None, false)?;

    let volume_desc = DspParameterDesc {
        type_: DspParameterType::Float,
        name: name16("volume"),
        label: name16("%"),
        description: "linear volume in percent".to_string(),
        union: FMOD_DSP_PARAMETER_DESC_UNION {
            floatdesc: FMOD_DSP_PARAMETER_DESC_FLOAT {
                min: 0.0,
                max: 1.0,
                defaultval: 0.42,
                mapping: Default::default(),
            },
        },
    };

    let other_desc = DspParameterDesc {
        type_: DspParameterType::Float,
        name: name16("other"),
        label: name16("%"),
        description: "linear value in percent".to_string(),
        union: FMOD_DSP_PARAMETER_DESC_UNION {
            floatdesc: FMOD_DSP_PARAMETER_DESC_FLOAT {
                min: 0.0,
                max: 1.0,
                defaultval: 0.42,
                mapping: Default::default(),
            },
        },
    };

    struct MyDspData {
        volume: f32,
        other: f32,
    }

    unsafe extern "C" fn create_callback(dsp_state: *mut FMOD_DSP_STATE) -> FMOD_RESULT {
        let data = Box::new(MyDspData {
            volume: 1.0,
            other: 0.0,
        });
        (*dsp_state).plugindata = Box::into_raw(data) as *mut _;
        FMOD_OK
    }

    unsafe extern "C" fn set_parameter_float_callback(
        dsp_state: *mut FMOD_DSP_STATE,
        index: c_int,
        value: c_float,
    ) -> FMOD_RESULT {
        let data = &mut *((*dsp_state).plugindata as *mut MyDspData);
        match index {
            0 => data.volume = value,
            1 => data.other = value,
            _ => unreachable!(),
        }
        FMOD_OK
    }

    unsafe extern "C" fn get_parameter_float_callback(
        dsp_state: *mut FMOD_DSP_STATE,
        index: c_int,
        value: *mut c_float,
        _valuestr: *mut c_char,
    ) -> FMOD_RESULT {
        let data = (*dsp_state).plugindata as *mut MyDspData;
        let data = match index {
            0 => (*data).volume,
            1 => (*data).other,
            _ => unreachable!(),
        };
        value.write(data);
        FMOD_OK
    }

    let dspdesc = DspDescription {
        pluginsdkversion: 0,
        name: name32("My first DSP unit"),
        version: 0x00010000,
        numinputbuffers: 1,
        numoutputbuffers: 1,
        create: Some(create_callback),
        release: None,
        reset: None,
        read: None,
        process: None,
        setposition: None,
        paramdesc: vec![volume_desc, other_desc],
        setparameterfloat: Some(set_parameter_float_callback),
        setparameterint: None,
        setparameterbool: None,
        setparameterdata: None,
        getparameterfloat: Some(get_parameter_float_callback),
        getparameterint: None,
        getparameterbool: None,
        getparameterdata: None,
        shouldiprocess: None,
        userdata: null_mut(),
        sys_register: None,
        sys_deregister: None,
        sys_mix: None,
    };

    let mydsp = system.create_dsp(dspdesc)?;
    let mastergroup = system.get_master_channel_group()?;
    mastergroup.add_dsp(0, mydsp)?;

    for step in 0..5 {
        match step {
            1 => {
                mydsp.set_bypass(true)?;
            }
            2 => {
                mydsp.set_bypass(false)?;
            }
            3 => {
                mydsp.set_parameter_float(0, 0.25)?;
            }
            4 => {
                mydsp.set_parameter_float(1, 0.75)?;
            }
            _ => {}
        }
        thread::sleep(Duration::from_millis(100))
    }

    let info = mydsp.get_parameter_info(0)?;
    let volume_value_default = unsafe { info.union.floatdesc.defaultval };
    let (volume_value, _) = mydsp.get_parameter_float(0, 0)?;
    assert_eq!(volume_value_default, 0.42, "volume default value");
    assert_eq!(volume_value, 0.25, "volume value");
    assert_eq!(info.description, "linear volume in percent", "description");

    let info = mydsp.get_parameter_info(1)?;
    let (other_value, _) = mydsp.get_parameter_float(1, 0)?;
    assert_eq!(other_value, 0.75, "other value");
    assert_eq!(info.description, "linear value in percent", "description");

    let (name, version, _, _, _) = mydsp.get_info()?;
    assert_eq!(name, "My first DSP unit", "dsp info name");
    assert_eq!(version, 0x00010000, "dsp info version");

    system.release()
}

#[test]
pub fn test_channel_group_get_name() -> Result<(), Error> {
    let system = System::create()?;
    system.init(32, FMOD_INIT_NORMAL, None)?;
    let group = system.get_master_channel_group()?;
    let name_5 = group.get_name(5)?;
    let name_100 = group.get_name(100)?;
    assert_eq!(name_5, "FMOD", "name 5");
    assert_eq!(name_100, "FMOD master", "name 100");
    system.release()
}

#[test]
pub fn test_get_driver_info() -> Result<(), Error> {
    let system = System::create()?;
    system.init(32, FMOD_INIT_NORMAL, None)?;
    let info = system.get_driver_info(0, 100)?;
    println!("info: {:?}", info);
    system.release()
}

fn name16(name: &str) -> [i8; 16] {
    let mut output = [0; 16];
    for (i, ch) in name.as_bytes().iter().enumerate() {
        output[i] = *ch as i8;
    }
    output
}

fn name32(name: &str) -> [i8; 32] {
    let mut output = [0; 32];
    for (i, ch) in name.as_bytes().iter().enumerate() {
        output[i] = *ch as i8;
    }
    output
}