1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
use std::{
    mem::MaybeUninit,
    marker::PhantomData,
    ffi::{CStr, CString},
    ptr::null_mut,
};
use crate::{ffi, Synth, Result, Status, Chan, Bank, Prog};

/**
 * Tuning
 */
impl Synth {
    /**
    Create a new key-based tuning with given name, number, and
    pitches. The array 'pitches' should have length 128 and contains
    the pitch in cents of every key in cents. However, if 'pitches' is
    NULL, a new tuning is created with the well-tempered scale.
     */
    pub fn create_key_tuning<S: AsRef<str>>(&self, tuning_bank: Bank, tuning_prog: Prog, name: S, pitch: &[f64; 128]) -> Status {
        let name = CString::new(name.as_ref()).unwrap();
        self.zero_ok(unsafe { ffi::fluid_synth_create_key_tuning(
            self.handle, tuning_bank as _, tuning_prog as _, name.as_ptr(), pitch.as_ptr() as _) })
    }

    /**
    Create a new octave-based tuning with given name, number, and
    pitches.  The array 'pitches' should have length 12 and contains
    derivation in cents from the well-tempered scale. For example, if
    pitches[0] equals -33, then the C-keys will be tuned 33 cents
    below the well-tempered C.
     */
    pub fn create_octave_tuning<S: AsRef<str>>(&self, tuning_bank: Bank, tuning_prog: Prog, name: S, pitch: &[f64; 12]) -> Status {
        let name = CString::new(name.as_ref()).unwrap();
        self.zero_ok(unsafe { ffi::fluid_synth_create_octave_tuning(
            self.handle, tuning_bank as _, tuning_prog as _, name.as_ptr(), pitch.as_ptr()) })
    }

    pub fn activate_octave_tuning<S: AsRef<str>>(&self, bank: Bank, prog: Prog, name: S, pitch: &[f64; 12], apply: bool) -> Status {
        let name = CString::new(name.as_ref()).unwrap();
        self.zero_ok(unsafe { ffi::fluid_synth_activate_octave_tuning(
            self.handle, bank as _, prog as _, name.as_ptr(), pitch.as_ptr(), apply as _) })
    }

    /**
    Request a note tuning changes. Both they 'keys' and 'pitches'
    arrays should be of length 'num_pitches'. If 'apply' is non-zero,
    the changes should be applied in real-time, i.e. sounding notes
    will have their pitch updated. 'APPLY' IS CURRENTLY IGNORED. The
    changes will be available for newly triggered notes only.
     */
    pub fn tune_notes<K, P>(&self, tuning_bank: Bank, tuning_prog: Prog, keys: K, pitch: P, apply: bool) -> Status
    where
        K: AsRef<[u32]>,
        P: AsRef<[f64]>,
    {
        let keys = keys.as_ref();
        let pitch = pitch.as_ref();
        let len = keys.len().min(pitch.len());
        self.zero_ok(unsafe { ffi::fluid_synth_tune_notes(
            self.handle, tuning_bank as _, tuning_prog as _, len as _, keys.as_ptr() as _, pitch.as_ptr() as _, apply as _) })
    }

    /**
    Select a tuning for a channel.
     */
    pub fn select_tuning(&self, chan: Chan, tuning_bank: Bank, tuning_prog: Prog) -> Status {
        self.zero_ok(unsafe { ffi::fluid_synth_select_tuning(self.handle, chan as _, tuning_bank as _, tuning_prog as _) })
    }

    pub fn activate_tuning(&self, chan: Chan, bank: Bank, prog: Prog, apply: bool) -> Status {
        self.zero_ok(unsafe { ffi::fluid_synth_activate_tuning(self.handle, chan as _, bank as _, prog as _, apply as _) })
    }

    /**
    Set the tuning to the default well-tempered tuning on a channel.
     */
    pub fn reset_tuning(&self, chan: Chan) -> Status {
        self.zero_ok(unsafe { ffi::fluid_synth_reset_tuning(self.handle, chan as _) })
    }

    /**
    Get the iterator throught the list of available tunings.
     */
    pub fn tuning_iter(&self) -> TuningIter<'_> {
        TuningIter::from_ptr(self.handle)
    }

    /**
    Dump the data of a tuning.

    This function returns both the name and pitch values of a tuning.
     */
    pub fn tuning_dump(&self, bank: Bank, prog: Prog) -> Result<(String, [f64; 128])> {
        const NAME_LEN: usize = 128;

        let mut name = MaybeUninit::<[u8; NAME_LEN]>::uninit();
        let mut pitch = MaybeUninit::<[f64; 128]>::uninit();

        self.zero_ok(unsafe { ffi::fluid_synth_tuning_dump(
            self.handle, bank as _, prog as _, name.as_mut_ptr() as _, NAME_LEN as _, pitch.as_mut_ptr() as _) })?;
        Ok((
            (unsafe { CStr::from_ptr(name.as_ptr() as _) }).to_str().unwrap().into(),
            unsafe { pitch.assume_init() },
        ))
    }

    /**
    Dump the data of a tuning.

    This function returns the only name of a tuning.
     */
    pub fn tuning_dump_name(&self, bank: Bank, prog: Prog) -> Result<String> {
        const NAME_LEN: usize = 128;

        let mut name = MaybeUninit::<[u8; NAME_LEN]>::uninit();

        self.zero_ok(unsafe { ffi::fluid_synth_tuning_dump(
            self.handle, bank as _, prog as _, name.as_mut_ptr() as _, NAME_LEN as _, null_mut()) })?;
        Ok((unsafe { CStr::from_ptr(name.as_ptr() as _) }).to_str().unwrap().into())
    }

    /**
    Dump the data of a tuning.

    This function returns the only pitch values of a tuning.
     */
    pub fn tuning_dump_pitch(&self, bank: Bank, prog: Prog) -> Result<[f64; 128]> {
        let mut pitch = MaybeUninit::<[f64; 128]>::uninit();

        self.zero_ok(unsafe { ffi::fluid_synth_tuning_dump(
            self.handle, bank as _, prog as _, null_mut(), 0, pitch.as_mut_ptr() as _) })?;
        Ok(unsafe { pitch.assume_init() })
    }
}

/**
The iterator over tunings
 */
pub struct TuningIter<'a> {
    handle: *mut ffi::fluid_synth_t,
    phantom: PhantomData<&'a ()>,
    init: bool,
    next: bool,
}

impl<'a> TuningIter<'a> {
    fn from_ptr(handle: *mut ffi::fluid_synth_t) -> Self {
        Self { handle, phantom: PhantomData, init: true, next: true }
    }
}

impl<'a> Iterator for TuningIter<'a> {
    type Item = (Bank, Prog);

    fn next(&mut self) -> Option<Self::Item> {
        if self.init {
            self.init = false;
            unsafe { ffi::fluid_synth_tuning_iteration_start(self.handle); }
        }
        if self.next {
            let mut bank = MaybeUninit::uninit();
            let mut prog = MaybeUninit::uninit();
            self.next = 0 != unsafe { ffi::fluid_synth_tuning_iteration_next(
                self.handle, bank.as_mut_ptr(), prog.as_mut_ptr()) };

            Some((
                unsafe { bank.assume_init() as _ },
                unsafe { prog.assume_init() as _ },
            ))
        } else {
            None
        }
    }
}