Struct tune::tuner::ChannelTuner[][src]

pub struct ChannelTuner<K> { /* fields omitted */ }

Maps keys accross multiple channels to overcome several tuning limitations.

Implementations

impl<K: Copy + Eq + Hash> ChannelTuner<K>[src]

pub fn empty() -> Self[src]

pub fn apply_full_keyboard_tuning(
    tuning: impl KeyboardMapping<K>,
    keys: impl IntoIterator<Item = K>
) -> (Self, Vec<FullKeyboardDetuning>)
[src]

Distributes the provided KeyboardMapping accross multiple channels s.t. each note is only detuned once per channel and by 50c at most.

This works around a restriction of some synthesizers (e.g. FluidSynth) where the pitch per note can be customized but the sound sample per note cannot.

Apply this strategy if your synthesizer has full keyboard tuning support but your samples sound as if they were played back in slow motion or time lapse at certain pitches.

Examples

In the following example, tuner holds a ChannelTuner instance which encapsulates the mapping required to find the appropriate channel and note for a given scale degree. The variable channel_tunings stores a Vec of tunings that need to be applied on the channels of your synthesizer.

let scl = Scl::builder()
    .push_ratio(Ratio::octave().divided_into_equal_steps(36))
    .build()
    .unwrap();

let edo_36_tuning = (scl, KbmRoot::from(Note::from_midi_number(62)).to_kbm());

let (tuner, channel_tunings) = ChannelTuner::apply_full_keyboard_tuning(
    edo_36_tuning,
    (0..128).map(PianoKey::from_midi_number),
);

// Since 3 36-EDO notes fit into one semitone, 3 channels are required.
assert_eq!(tuner.num_channels(), 3);
assert_eq!(tuner.num_channels(), channel_tunings.len());

assert_eq!(
    tuner.get_channel_and_note_for_key(PianoKey::from_midi_number(60)),
    Some((2, Note::from_midi_number(61)))
);
assert_eq!(
    tuner.get_channel_and_note_for_key(PianoKey::from_midi_number(61)),
    Some((0, Note::from_midi_number(62)))
);
assert_eq!(
    tuner.get_channel_and_note_for_key(PianoKey::from_midi_number(62)),
    Some((1, Note::from_midi_number(62)))
);
assert_eq!(
    tuner.get_channel_and_note_for_key(PianoKey::from_midi_number(63)),
    Some((2, Note::from_midi_number(62)))
);
assert_eq!(
    tuner.get_channel_and_note_for_key(PianoKey::from_midi_number(64)),
    Some((0, Note::from_midi_number(63)))
);

pub fn apply_octave_based_tuning(
    tuning: impl KeyboardMapping<K>,
    keys: impl IntoIterator<Item = K>
) -> (Self, Vec<OctaveBasedDetuning>)
[src]

Distributes the provided KeyboardMapping accross multiple channels s.t. each note letter is only detuned once per channel and by 50c at most.

This method works in the same way as ChannelTuner::apply_full_keyboard_tuning does but instead of retuning each note individually, the retuning pattern repeats at the octave.

When applied to octave-repeating scales the octave-based tuning strategy and the full keyboard tuning strategy work equally well. For non-octave-repeating scales, however, the situation is different: Since only few (if any) notes can share the same detuning in different octaves the octave-based tuning strategy will require a large number of channels to account for all items of a tuning.

Apply this strategy if your synthesizer supports octave-based tunings but does not support full keyboard tunings.

pub fn apply_channel_based_tuning(
    tuning: impl KeyboardMapping<K>,
    keys: impl IntoIterator<Item = K>
) -> (Self, Vec<Ratio>)
[src]

Distributes the provided KeyboardMapping accross multiple channels where each channel is detuned as a whole and by 50c at most.

This tuning method is the least powerful one and should only be used if your synthesizer has neither full keyboard nor octave-based tuning support. It works quite well for n-edo tunings where gcd(n, 12) is large. This because each channel can handle gcd(n, 12) notes resulting in a total number of required channels of n / gcd(n, 12).

Examples

let kbm = KbmRoot::from(Note::from_midi_number(62)).to_kbm();

let scl_of_16_edo = Scl::builder()
    .push_ratio(Ratio::octave().divided_into_equal_steps(16))
    .build()
    .unwrap();

let (_, tunings) = ChannelTuner::apply_channel_based_tuning(
    (scl_of_16_edo, &kbm),
    (0..128).map(PianoKey::from_midi_number),
);

// The number of channels for 16-edo is 4 = 16/gcd(16, 12)
assert_eq!(tunings.len(), 4);
assert_approx_eq!(tunings[0].as_cents(), -25.0);
assert_approx_eq!(tunings[1].as_cents(), 0.0);
assert_approx_eq!(tunings[2].as_cents(), 25.0);
assert_approx_eq!(tunings[3].as_cents(), 50.0);

let scl_of_13_edt = Scl::builder()
    .push_ratio(Ratio::from_float(3.0).divided_into_equal_steps(13))
    .build()
    .unwrap();

let (_, tunings) = ChannelTuner::apply_channel_based_tuning(
    (scl_of_13_edt, &kbm),
    (0..128).map(PianoKey::from_midi_number),
);

// Since 13edt has an irrational step size (measured in semitones) every detuning is unique.
assert_eq!(tunings.len(), 128);

pub fn get_channel_and_note_for_key(&self, key: K) -> Option<(usize, Note)>[src]

Returns the channel and Note to be played when hitting a key.

See ChannelTuner::apply_full_keyboard_tuning for an explanation of how to use this method.

pub fn num_channels(&self) -> usize[src]

Returns the number of channels that this ChannelTuner will make use of.

Auto Trait Implementations

impl<K> RefUnwindSafe for ChannelTuner<K> where
    K: RefUnwindSafe

impl<K> Send for ChannelTuner<K> where
    K: Send

impl<K> Sync for ChannelTuner<K> where
    K: Sync

impl<K> Unpin for ChannelTuner<K> where
    K: Unpin

impl<K> UnwindSafe for ChannelTuner<K> where
    K: UnwindSafe

Blanket Implementations

impl<T> Any for T where
    T: 'static + ?Sized
[src]

impl<T> Borrow<T> for T where
    T: ?Sized
[src]

impl<T> BorrowMut<T> for T where
    T: ?Sized
[src]

impl<T> From<T> for T[src]

impl<T, U> Into<U> for T where
    U: From<T>, 
[src]

impl<T, U> TryFrom<U> for T where
    U: Into<T>, 
[src]

type Error = Infallible

The type returned in the event of a conversion error.

impl<T, U> TryInto<U> for T where
    U: TryFrom<T>, 
[src]

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.