Struct tune::tuner::ChannelTuner [−][src]
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]
tuning: impl KeyboardMapping<K>,
keys: impl IntoIterator<Item = K>
) -> (Self, Vec<FullKeyboardDetuning>)
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]
tuning: impl KeyboardMapping<K>,
keys: impl IntoIterator<Item = K>
) -> (Self, Vec<OctaveBasedDetuning>)
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]
tuning: impl KeyboardMapping<K>,
keys: impl IntoIterator<Item = K>
) -> (Self, Vec<Ratio>)
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,
K: RefUnwindSafe,
impl<K> Send for ChannelTuner<K> where
K: Send,
K: Send,
impl<K> Sync for ChannelTuner<K> where
K: Sync,
K: Sync,
impl<K> Unpin for ChannelTuner<K> where
K: Unpin,
K: Unpin,
impl<K> UnwindSafe for ChannelTuner<K> where
K: UnwindSafe,
K: UnwindSafe,
Blanket Implementations
impl<T> Any for T where
T: 'static + ?Sized,
[src]
T: 'static + ?Sized,
impl<T> Borrow<T> for T where
T: ?Sized,
[src]
T: ?Sized,
impl<T> BorrowMut<T> for T where
T: ?Sized,
[src]
T: ?Sized,
pub fn borrow_mut(&mut self) -> &mut T
[src]
impl<T> From<T> for T
[src]
impl<T, U> Into<U> for T where
U: From<T>,
[src]
U: From<T>,
impl<T, U> TryFrom<U> for T where
U: Into<T>,
[src]
U: Into<T>,
type Error = Infallible
The type returned in the event of a conversion error.
pub fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>
[src]
impl<T, U> TryInto<U> for T where
U: TryFrom<T>,
[src]
U: TryFrom<T>,