From beae34f7be77b4c1ce58a57bd72d12f4918c8ef7 Mon Sep 17 00:00:00 2001 From: Petr Horacek Date: Sun, 16 Jun 2024 22:41:08 +0200 Subject: [PATCH] Add firmware without white key quantization Due to white key quantization, the module is quite sensitive to inaccurate CV input of TONE. This is especially notable when the same CV is simultaneously used to control the pitch of another module as well. This patch adds a feature flag `even_quantization`. If this is enabled, white key quantization is switched off. Signed-off-by: Petr Horacek --- CHANGELOG.md | 1 + eurorack/Cargo.toml | 3 + hack/release.sh | 1 + hack/release.tmpl.md | 4 + lib/Cargo.toml | 1 + lib/src/note.rs | 5 + lib/src/quantizer/diatonic.rs | 511 ++++++++++++++++++++++++++++------ 7 files changed, 444 insertions(+), 82 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b050ed6..5d46af9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,7 @@ backwards compatibility. ## Unreleased * Pure Data externals were moved to a [dedicated project](https://github.com/zlosynth/automaton). +* Offer alternative firmware with white key quantization disabled. ## 2.2.1 diff --git a/eurorack/Cargo.toml b/eurorack/Cargo.toml index 82115ed..78dfe00 100644 --- a/eurorack/Cargo.toml +++ b/eurorack/Cargo.toml @@ -35,3 +35,6 @@ opt-level = "z" # This contains code used only during the boot, no need for spee [profile.release.package.crc] opt-level = "z" # CRC is only used while restoring a config on boot and does not need to be fast + +[features] +even_quantization = ["achordion-lib/even_quantization"] diff --git a/hack/release.sh b/hack/release.sh index 1dd8dcd..e67a6d5 100755 --- a/hack/release.sh +++ b/hack/release.sh @@ -17,6 +17,7 @@ rm -rf release mkdir release pushd eurorack && cargo +1.63.0 objcopy --release -- -O binary ../release/achordion-firmware-${version}.bin && popd +pushd eurorack && cargo +1.63.0 objcopy --release --features even_quantization -- -O binary ../release/achordion-firmware-${version}-even-quantization.bin && popd make manual cp manual/user/manual_digital.pdf release/achordion-user-manual.pdf diff --git a/hack/release.tmpl.md b/hack/release.tmpl.md index 4edcf86..44b801d 100644 --- a/hack/release.tmpl.md +++ b/hack/release.tmpl.md @@ -5,3 +5,7 @@ ${CHANGES} ## Firmware Achordion's firmware can be upgraded to obtain new features and bug-fixes. To do so, download the binary from attachments bellow, navigate to [Electro-Smith's web programmer](https://electro-smith.github.io/Programmer/) and follow the guide described under the "Display Help" button. + +## Known issues + +Due to white key quantization, the module is quite sensitive to inaccurate CV input of TONE. This is especially notable when the same CV is simultaneously used to control the pitch of another module as well. If you suffer from these issues, try the alternative firmware `achordion-firmware-x.y.z-even-quantization.bin`. diff --git a/lib/Cargo.toml b/lib/Cargo.toml index 5820f98..76a3837 100644 --- a/lib/Cargo.toml +++ b/lib/Cargo.toml @@ -13,6 +13,7 @@ publish = false default = ["stable_amplitude"] balanced_amplitude = [] stable_amplitude = [] +even_quantization = [] [dependencies] micromath = "1.1" diff --git a/lib/src/note.rs b/lib/src/note.rs index 5aa1e6c..8ce65d8 100644 --- a/lib/src/note.rs +++ b/lib/src/note.rs @@ -326,6 +326,11 @@ impl Note { Self::try_from_i16(note as i16).unwrap() } + #[inline(always)] + pub fn try_from_u8(note: u8) -> Option { + Self::try_from_i16(note as i16) + } + #[inline(always)] pub fn try_from_i16(note: i16) -> Option { if note >= 0 && note <= Note::HIGHEST_NOTE as i16 { diff --git a/lib/src/quantizer/diatonic.rs b/lib/src/quantizer/diatonic.rs index 8606968..ca91c50 100644 --- a/lib/src/quantizer/diatonic.rs +++ b/lib/src/quantizer/diatonic.rs @@ -3,11 +3,21 @@ use micromath::F32Ext; use crate::note::Note; use crate::scales; -use crate::scales::diatonic::Mode; +use crate::scales::diatonic::{Mode, SEMITONES}; pub type Degree = u8; -pub fn quantize_voct(mode: Mode, root: Note, mut voct: f32) -> (Note, Degree) { +#[cfg(feature = "even_quantization")] +pub fn quantize_voct(mode: Mode, root: Note, voct: f32) -> (Note, Degree) { + quantize_voct_center(mode, root, voct) +} + +#[cfg(not(feature = "even_quantization"))] +pub fn quantize_voct(mode: Mode, root: Note, voct: f32) -> (Note, Degree) { + quantize_voct_white_keys(mode, root, voct) +} + +fn quantize_voct_white_keys(mode: Mode, root: Note, mut voct: f32) -> (Note, Degree) { if voct > to_voct(Note::G9) { // One below the highest to allow it to quantize up voct = to_voct(Note::Gb9); @@ -28,6 +38,207 @@ pub fn quantize_voct(mode: Mode, root: Note, mut voct: f32) -> (Note, Degree) { (note, white_diff.rem_euclid(7) as u8 + 1) } +#[allow(dead_code)] +fn quantize_voct_center(mode: Mode, root: Note, voct: f32) -> (Note, Degree) { + // XXX: This is making the method simpler by sacrificing a part of the + // lowest octave. + let lowest_tonic = lowest_note(root); + if voct < lowest_tonic.to_voct() { + return (lowest_tonic, 0); + } + + let closest_tonic = find_closest_tonic(lowest_tonic, voct); + if closest_tonic.is_none() { + return (lowest_tonic, 0); + } + let closest_tonic = closest_tonic.unwrap(); + + let surrounding_notes = find_surrounding_notes_ascending(mode, voct, closest_tonic); + if surrounding_notes.is_none() { + return (lowest_tonic, 0); + } + let ((below_note, below_degree), (above_note, above_degree)) = surrounding_notes.unwrap(); + + let center_voct = (below_note.to_voct() + above_note.to_voct()) / 2.0; + if voct < center_voct { + (below_note, below_degree) + } else { + (above_note, above_degree) + } +} + +fn lowest_note(note: Note) -> Note { + match note { + Note::A0 + | Note::A1 + | Note::A2 + | Note::A3 + | Note::A4 + | Note::A5 + | Note::A6 + | Note::A7 + | Note::A8 + | Note::AMinus1 => Note::AMinus1, + Note::Ab0 + | Note::Ab1 + | Note::Ab2 + | Note::Ab3 + | Note::Ab4 + | Note::Ab5 + | Note::Ab6 + | Note::Ab7 + | Note::Ab8 + | Note::AbMinus1 => Note::AbMinus1, + Note::B0 + | Note::B1 + | Note::B2 + | Note::B3 + | Note::B4 + | Note::B5 + | Note::B6 + | Note::B7 + | Note::B8 + | Note::BMinus1 => Note::BMinus1, + Note::Bb0 + | Note::Bb1 + | Note::Bb2 + | Note::Bb3 + | Note::Bb4 + | Note::Bb5 + | Note::Bb6 + | Note::Bb7 + | Note::Bb8 + | Note::BbMinus1 => Note::BbMinus1, + Note::C0 + | Note::C1 + | Note::C2 + | Note::C3 + | Note::C4 + | Note::C5 + | Note::C6 + | Note::C7 + | Note::C8 + | Note::C9 + | Note::CMinus1 => Note::CMinus1, + Note::D0 + | Note::D1 + | Note::D2 + | Note::D3 + | Note::D4 + | Note::D5 + | Note::D6 + | Note::D7 + | Note::D8 + | Note::D9 + | Note::DMinus1 => Note::DMinus1, + Note::Db0 + | Note::Db1 + | Note::Db2 + | Note::Db3 + | Note::Db4 + | Note::Db5 + | Note::Db6 + | Note::Db7 + | Note::Db8 + | Note::Db9 + | Note::DbMinus1 => Note::DbMinus1, + Note::E0 + | Note::E1 + | Note::E2 + | Note::E3 + | Note::E4 + | Note::E5 + | Note::E6 + | Note::E7 + | Note::E8 + | Note::E9 + | Note::EMinus1 => Note::EMinus1, + Note::Eb0 + | Note::Eb1 + | Note::Eb2 + | Note::Eb3 + | Note::Eb4 + | Note::Eb5 + | Note::Eb6 + | Note::Eb7 + | Note::Eb8 + | Note::Eb9 + | Note::EbMinus1 => Note::EbMinus1, + Note::F0 + | Note::F1 + | Note::F2 + | Note::F3 + | Note::F4 + | Note::F5 + | Note::F6 + | Note::F7 + | Note::F8 + | Note::F9 + | Note::FMinus1 => Note::FMinus1, + Note::G0 + | Note::G1 + | Note::G2 + | Note::G3 + | Note::G4 + | Note::G5 + | Note::G6 + | Note::G7 + | Note::G8 + | Note::G9 + | Note::GMinus1 => Note::GMinus1, + Note::Gb0 + | Note::Gb1 + | Note::Gb2 + | Note::Gb3 + | Note::Gb4 + | Note::Gb5 + | Note::Gb6 + | Note::Gb7 + | Note::Gb8 + | Note::Gb9 + | Note::GbMinus1 => Note::GbMinus1, + } +} + +fn find_closest_tonic(lowest_tonic: Note, voct: f32) -> Option { + let distance_in_full_octaves = (voct - lowest_tonic.to_voct()) as u8; + Note::try_from_u8(lowest_tonic.to_midi_id() + 12 * distance_in_full_octaves.min(11)) +} + +fn find_surrounding_notes_ascending( + mode: Mode, + voct: f32, + closest_tonic: Note, +) -> Option<((Note, Degree), (Note, Degree))> { + let mut below_note = closest_tonic; + let mut below_degree = 1; + let mut above_note = None; + let mut above_degree = None; + + let mut distance = 0; + let semitones = SEMITONES[mode as usize]; + // TODO: Move this to a function + let mut steps = [0; 7]; + for i in 0..steps.len() { + steps[i] = semitones.get(i + 1).unwrap_or(&12) - semitones[i]; + } + for (i, step) in steps.iter().enumerate() { + distance += step; + let note = Note::try_from_u8(closest_tonic.to_midi_id() + distance as u8)?; + let index = i as u8; + let degree = if index == 6 { 1 } else { index + 2 }; + if note.to_voct() > voct { + above_note = Some(note); + above_degree = Some(degree); + break; + } + below_note = note; + below_degree = degree; + } + + Some(((below_note, below_degree), (above_note?, above_degree?))) +} + pub fn quantize_linear(mode: Mode, root: Note, mut value: f32) -> (Note, Degree) { if value > 10.0 { value = 10.0 @@ -152,90 +363,190 @@ mod tests { fn quantize_voct_white_keys_in_c_major_with_root(root: Note) { let voct = 2.0; - assert_eq!(quantize_voct(Ionian, root, voct), (Note::C1, 1)); + assert_eq!(quantize_voct_white_keys(Ionian, root, voct), (Note::C1, 1)); + assert_eq!( + quantize_voct_white_keys(Ionian, root, voct - 0.2 / 12.0), + (Note::C1, 1) + ); + assert_eq!( + quantize_voct_white_keys(Ionian, root, voct + 0.2 / 12.0), + (Note::C1, 1) + ); + + let voct = 2.0 + 2.0 / 12.0; + assert_eq!(quantize_voct_white_keys(Ionian, root, voct), (Note::D1, 2)); + assert_eq!( + quantize_voct_white_keys(Ionian, root, voct - 0.2 / 12.0), + (Note::D1, 2) + ); + assert_eq!( + quantize_voct_white_keys(Ionian, root, voct + 0.2 / 12.0), + (Note::D1, 2) + ); + + let voct = 2.0 + 4.0 / 12.0; + assert_eq!(quantize_voct_white_keys(Ionian, root, voct), (Note::E1, 3)); + assert_eq!( + quantize_voct_white_keys(Ionian, root, voct - 0.2 / 12.0), + (Note::E1, 3) + ); + assert_eq!( + quantize_voct_white_keys(Ionian, root, voct + 0.2 / 12.0), + (Note::E1, 3) + ); + + let voct = 2.0 + 5.0 / 12.0; + assert_eq!(quantize_voct_white_keys(Ionian, root, voct), (Note::F1, 4)); + assert_eq!( + quantize_voct_white_keys(Ionian, root, voct - 0.2 / 12.0), + (Note::F1, 4) + ); + assert_eq!( + quantize_voct_white_keys(Ionian, root, voct + 0.2 / 12.0), + (Note::F1, 4) + ); + + let voct = 2.0 + 7.0 / 12.0; + assert_eq!(quantize_voct_white_keys(Ionian, root, voct), (Note::G1, 5)); + assert_eq!( + quantize_voct_white_keys(Ionian, root, voct - 0.2 / 12.0), + (Note::G1, 5) + ); + assert_eq!( + quantize_voct_white_keys(Ionian, root, voct + 0.2 / 12.0), + (Note::G1, 5) + ); + + let voct = 2.0 + 9.0 / 12.0; + assert_eq!(quantize_voct_white_keys(Ionian, root, voct), (Note::A1, 6)); + assert_eq!( + quantize_voct_white_keys(Ionian, root, voct - 0.2 / 12.0), + (Note::A1, 6) + ); + assert_eq!( + quantize_voct_white_keys(Ionian, root, voct + 0.2 / 12.0), + (Note::A1, 6) + ); + + let voct = 2.0 + 11.0 / 12.0; + assert_eq!(quantize_voct_white_keys(Ionian, root, voct), (Note::B1, 7)); + assert_eq!( + quantize_voct_white_keys(Ionian, root, voct - 0.2 / 12.0), + (Note::B1, 7) + ); + assert_eq!( + quantize_voct_white_keys(Ionian, root, voct + 0.2 / 12.0), + (Note::B1, 7) + ); + + let voct = 3.0; + assert_eq!(quantize_voct_white_keys(Ionian, root, voct), (Note::C2, 1)); + assert_eq!( + quantize_voct_white_keys(Ionian, root, voct - 0.2 / 12.0), + (Note::C2, 1) + ); + assert_eq!( + quantize_voct_white_keys(Ionian, root, voct + 0.2 / 12.0), + (Note::C2, 1) + ); + } + + #[test] + fn quantize_voct_center_in_c_major_with_root_below() { + quantize_voct_center_in_c_major_with_root(Note::C0); + } + + #[test] + fn quantize_voct_center_in_c_major_with_root_above() { + quantize_voct_center_in_c_major_with_root(Note::C4); + } + + fn quantize_voct_center_in_c_major_with_root(root: Note) { + let voct = 2.0; + assert_eq!(quantize_voct_center(Ionian, root, voct), (Note::C1, 1)); assert_eq!( - quantize_voct(Ionian, root, voct - 0.2 / 12.0), + quantize_voct_center(Ionian, root, voct - 0.4 / 12.0), (Note::C1, 1) ); assert_eq!( - quantize_voct(Ionian, root, voct + 0.2 / 12.0), + quantize_voct_center(Ionian, root, voct + 0.9 / 12.0), (Note::C1, 1) ); let voct = 2.0 + 2.0 / 12.0; - assert_eq!(quantize_voct(Ionian, root, voct), (Note::D1, 2)); + assert_eq!(quantize_voct_center(Ionian, root, voct), (Note::D1, 2)); assert_eq!( - quantize_voct(Ionian, root, voct - 0.2 / 12.0), + quantize_voct_center(Ionian, root, voct - 0.9 / 12.0), (Note::D1, 2) ); assert_eq!( - quantize_voct(Ionian, root, voct + 0.2 / 12.0), + quantize_voct_center(Ionian, root, voct + 0.9 / 12.0), (Note::D1, 2) ); let voct = 2.0 + 4.0 / 12.0; - assert_eq!(quantize_voct(Ionian, root, voct), (Note::E1, 3)); + assert_eq!(quantize_voct_center(Ionian, root, voct), (Note::E1, 3)); assert_eq!( - quantize_voct(Ionian, root, voct - 0.2 / 12.0), + quantize_voct_center(Ionian, root, voct - 0.9 / 12.0), (Note::E1, 3) ); assert_eq!( - quantize_voct(Ionian, root, voct + 0.2 / 12.0), + quantize_voct_center(Ionian, root, voct + 0.4 / 12.0), (Note::E1, 3) ); let voct = 2.0 + 5.0 / 12.0; - assert_eq!(quantize_voct(Ionian, root, voct), (Note::F1, 4)); + assert_eq!(quantize_voct_center(Ionian, root, voct), (Note::F1, 4)); assert_eq!( - quantize_voct(Ionian, root, voct - 0.2 / 12.0), + quantize_voct_center(Ionian, root, voct - 0.4 / 12.0), (Note::F1, 4) ); assert_eq!( - quantize_voct(Ionian, root, voct + 0.2 / 12.0), + quantize_voct_center(Ionian, root, voct + 0.9 / 12.0), (Note::F1, 4) ); let voct = 2.0 + 7.0 / 12.0; - assert_eq!(quantize_voct(Ionian, root, voct), (Note::G1, 5)); + assert_eq!(quantize_voct_center(Ionian, root, voct), (Note::G1, 5)); assert_eq!( - quantize_voct(Ionian, root, voct - 0.2 / 12.0), + quantize_voct_center(Ionian, root, voct - 0.9 / 12.0), (Note::G1, 5) ); assert_eq!( - quantize_voct(Ionian, root, voct + 0.2 / 12.0), + quantize_voct_center(Ionian, root, voct + 0.9 / 12.0), (Note::G1, 5) ); let voct = 2.0 + 9.0 / 12.0; - assert_eq!(quantize_voct(Ionian, root, voct), (Note::A1, 6)); + assert_eq!(quantize_voct_center(Ionian, root, voct), (Note::A1, 6)); assert_eq!( - quantize_voct(Ionian, root, voct - 0.2 / 12.0), + quantize_voct_center(Ionian, root, voct - 0.9 / 12.0), (Note::A1, 6) ); assert_eq!( - quantize_voct(Ionian, root, voct + 0.2 / 12.0), + quantize_voct_center(Ionian, root, voct + 0.9 / 12.0), (Note::A1, 6) ); let voct = 2.0 + 11.0 / 12.0; - assert_eq!(quantize_voct(Ionian, root, voct), (Note::B1, 7)); + assert_eq!(quantize_voct_center(Ionian, root, voct), (Note::B1, 7)); assert_eq!( - quantize_voct(Ionian, root, voct - 0.2 / 12.0), + quantize_voct_center(Ionian, root, voct - 0.9 / 12.0), (Note::B1, 7) ); assert_eq!( - quantize_voct(Ionian, root, voct + 0.2 / 12.0), + quantize_voct_center(Ionian, root, voct + 0.4 / 12.0), (Note::B1, 7) ); let voct = 3.0; - assert_eq!(quantize_voct(Ionian, root, voct), (Note::C2, 1)); + assert_eq!(quantize_voct_center(Ionian, root, voct), (Note::C2, 1)); assert_eq!( - quantize_voct(Ionian, root, voct - 0.2 / 12.0), + quantize_voct_center(Ionian, root, voct - 0.4 / 12.0), (Note::C2, 1) ); assert_eq!( - quantize_voct(Ionian, root, voct + 0.2 / 12.0), + quantize_voct_center(Ionian, root, voct + 0.9 / 12.0), (Note::C2, 1) ); } @@ -252,90 +563,108 @@ mod tests { fn quantize_voct_white_keys_in_f_sharp_major_with_root(root: Note) { let voct = 2.0; - assert_eq!(quantize_voct(Ionian, root, voct), (Note::CSharp1, 5)); assert_eq!( - quantize_voct(Ionian, root, voct - 0.2 / 12.0), + quantize_voct_white_keys(Ionian, root, voct), + (Note::CSharp1, 5) + ); + assert_eq!( + quantize_voct_white_keys(Ionian, root, voct - 0.2 / 12.0), (Note::CSharp1, 5) ); assert_eq!( - quantize_voct(Ionian, root, voct + 0.2 / 12.0), + quantize_voct_white_keys(Ionian, root, voct + 0.2 / 12.0), (Note::CSharp1, 5) ); let voct = 2.0 + 2.0 / 12.0; - assert_eq!(quantize_voct(Ionian, root, voct), (Note::DSharp1, 6)); assert_eq!( - quantize_voct(Ionian, root, voct - 0.2 / 12.0), + quantize_voct_white_keys(Ionian, root, voct), (Note::DSharp1, 6) ); assert_eq!( - quantize_voct(Ionian, root, voct + 0.2 / 12.0), + quantize_voct_white_keys(Ionian, root, voct - 0.2 / 12.0), + (Note::DSharp1, 6) + ); + assert_eq!( + quantize_voct_white_keys(Ionian, root, voct + 0.2 / 12.0), (Note::DSharp1, 6) ); let voct = 2.0 + 4.0 / 12.0; - assert_eq!(quantize_voct(Ionian, root, voct), (Note::F1, 7)); + assert_eq!(quantize_voct_white_keys(Ionian, root, voct), (Note::F1, 7)); assert_eq!( - quantize_voct(Ionian, root, voct - 0.2 / 12.0), + quantize_voct_white_keys(Ionian, root, voct - 0.2 / 12.0), (Note::F1, 7) ); assert_eq!( - quantize_voct(Ionian, root, voct + 0.2 / 12.0), + quantize_voct_white_keys(Ionian, root, voct + 0.2 / 12.0), (Note::F1, 7) ); let voct = 2.0 + 5.0 / 12.0; - assert_eq!(quantize_voct(Ionian, root, voct), (Note::FSharp1, 1)); assert_eq!( - quantize_voct(Ionian, root, voct - 0.2 / 12.0), + quantize_voct_white_keys(Ionian, root, voct), + (Note::FSharp1, 1) + ); + assert_eq!( + quantize_voct_white_keys(Ionian, root, voct - 0.2 / 12.0), (Note::FSharp1, 1) ); assert_eq!( - quantize_voct(Ionian, root, voct + 0.2 / 12.0), + quantize_voct_white_keys(Ionian, root, voct + 0.2 / 12.0), (Note::FSharp1, 1) ); let voct = 2.0 + 7.0 / 12.0; - assert_eq!(quantize_voct(Ionian, root, voct), (Note::GSharp1, 2)); assert_eq!( - quantize_voct(Ionian, root, voct - 0.2 / 12.0), + quantize_voct_white_keys(Ionian, root, voct), + (Note::GSharp1, 2) + ); + assert_eq!( + quantize_voct_white_keys(Ionian, root, voct - 0.2 / 12.0), (Note::GSharp1, 2) ); assert_eq!( - quantize_voct(Ionian, root, voct + 0.2 / 12.0), + quantize_voct_white_keys(Ionian, root, voct + 0.2 / 12.0), (Note::GSharp1, 2) ); let voct = 2.0 + 9.0 / 12.0; - assert_eq!(quantize_voct(Ionian, root, voct), (Note::ASharp1, 3)); assert_eq!( - quantize_voct(Ionian, root, voct - 0.2 / 12.0), + quantize_voct_white_keys(Ionian, root, voct), + (Note::ASharp1, 3) + ); + assert_eq!( + quantize_voct_white_keys(Ionian, root, voct - 0.2 / 12.0), (Note::ASharp1, 3) ); assert_eq!( - quantize_voct(Ionian, root, voct + 0.2 / 12.0), + quantize_voct_white_keys(Ionian, root, voct + 0.2 / 12.0), (Note::ASharp1, 3) ); let voct = 2.0 + 11.0 / 12.0; - assert_eq!(quantize_voct(Ionian, root, voct), (Note::B1, 4)); + assert_eq!(quantize_voct_white_keys(Ionian, root, voct), (Note::B1, 4)); assert_eq!( - quantize_voct(Ionian, root, voct - 0.2 / 12.0), + quantize_voct_white_keys(Ionian, root, voct - 0.2 / 12.0), (Note::B1, 4) ); assert_eq!( - quantize_voct(Ionian, root, voct + 0.2 / 12.0), + quantize_voct_white_keys(Ionian, root, voct + 0.2 / 12.0), (Note::B1, 4) ); let voct = 3.0; - assert_eq!(quantize_voct(Ionian, root, voct), (Note::CSharp2, 5)); assert_eq!( - quantize_voct(Ionian, root, voct - 0.2 / 12.0), + quantize_voct_white_keys(Ionian, root, voct), (Note::CSharp2, 5) ); assert_eq!( - quantize_voct(Ionian, root, voct + 0.2 / 12.0), + quantize_voct_white_keys(Ionian, root, voct - 0.2 / 12.0), + (Note::CSharp2, 5) + ); + assert_eq!( + quantize_voct_white_keys(Ionian, root, voct + 0.2 / 12.0), (Note::CSharp2, 5) ); } @@ -352,57 +681,57 @@ mod tests { fn quantize_voct_black_keys_in_c_major_with_root(root: Note) { let voct = 2.0 + 1.0 / 12.0; - assert_eq!(quantize_voct(Ionian, root, voct), (Note::C1, 1)); + assert_eq!(quantize_voct_white_keys(Ionian, root, voct), (Note::C1, 1)); assert_eq!( - quantize_voct(Ionian, root, voct - 0.05 / 12.0), + quantize_voct_white_keys(Ionian, root, voct - 0.05 / 12.0), (Note::C1, 1) ); assert_eq!( - quantize_voct(Ionian, root, voct + 0.05 / 12.0), + quantize_voct_white_keys(Ionian, root, voct + 0.05 / 12.0), (Note::C1, 1) ); let voct = 2.0 + 3.0 / 12.0; - assert_eq!(quantize_voct(Ionian, root, voct), (Note::D1, 2)); + assert_eq!(quantize_voct_white_keys(Ionian, root, voct), (Note::D1, 2)); assert_eq!( - quantize_voct(Ionian, root, voct - 0.05 / 12.0), + quantize_voct_white_keys(Ionian, root, voct - 0.05 / 12.0), (Note::D1, 2) ); assert_eq!( - quantize_voct(Ionian, root, voct + 0.05 / 12.0), + quantize_voct_white_keys(Ionian, root, voct + 0.05 / 12.0), (Note::D1, 2) ); let voct = 2.0 + 6.0 / 12.0; - assert_eq!(quantize_voct(Ionian, root, voct), (Note::F1, 4)); + assert_eq!(quantize_voct_white_keys(Ionian, root, voct), (Note::F1, 4)); assert_eq!( - quantize_voct(Ionian, root, voct - 0.05 / 12.0), + quantize_voct_white_keys(Ionian, root, voct - 0.05 / 12.0), (Note::F1, 4) ); assert_eq!( - quantize_voct(Ionian, root, voct + 0.05 / 12.0), + quantize_voct_white_keys(Ionian, root, voct + 0.05 / 12.0), (Note::F1, 4) ); let voct = 2.0 + 8.0 / 12.0; - assert_eq!(quantize_voct(Ionian, root, voct), (Note::G1, 5)); + assert_eq!(quantize_voct_white_keys(Ionian, root, voct), (Note::G1, 5)); assert_eq!( - quantize_voct(Ionian, root, voct - 0.05 / 12.0), + quantize_voct_white_keys(Ionian, root, voct - 0.05 / 12.0), (Note::G1, 5) ); assert_eq!( - quantize_voct(Ionian, root, voct + 0.05 / 12.0), + quantize_voct_white_keys(Ionian, root, voct + 0.05 / 12.0), (Note::G1, 5) ); let voct = 2.0 + 10.0 / 12.0; - assert_eq!(quantize_voct(Ionian, root, voct), (Note::A1, 6)); + assert_eq!(quantize_voct_white_keys(Ionian, root, voct), (Note::A1, 6)); assert_eq!( - quantize_voct(Ionian, root, voct - 0.05 / 12.0), + quantize_voct_white_keys(Ionian, root, voct - 0.05 / 12.0), (Note::A1, 6) ); assert_eq!( - quantize_voct(Ionian, root, voct + 0.05 / 12.0), + quantize_voct_white_keys(Ionian, root, voct + 0.05 / 12.0), (Note::A1, 6) ); } @@ -419,57 +748,72 @@ mod tests { fn quantize_voct_black_keys_in_f_sharp_major_with_root(root: Note) { let voct = 2.0 + 1.0 / 12.0; - assert_eq!(quantize_voct(Ionian, root, voct), (Note::CSharp1, 5)); assert_eq!( - quantize_voct(Ionian, root, voct - 0.05 / 12.0), + quantize_voct_white_keys(Ionian, root, voct), + (Note::CSharp1, 5) + ); + assert_eq!( + quantize_voct_white_keys(Ionian, root, voct - 0.05 / 12.0), (Note::CSharp1, 5) ); assert_eq!( - quantize_voct(Ionian, root, voct + 0.05 / 12.0), + quantize_voct_white_keys(Ionian, root, voct + 0.05 / 12.0), (Note::CSharp1, 5) ); let voct = 2.0 + 3.0 / 12.0; - assert_eq!(quantize_voct(Ionian, root, voct), (Note::DSharp1, 6)); assert_eq!( - quantize_voct(Ionian, root, voct - 0.05 / 12.0), + quantize_voct_white_keys(Ionian, root, voct), + (Note::DSharp1, 6) + ); + assert_eq!( + quantize_voct_white_keys(Ionian, root, voct - 0.05 / 12.0), (Note::DSharp1, 6) ); assert_eq!( - quantize_voct(Ionian, root, voct + 0.05 / 12.0), + quantize_voct_white_keys(Ionian, root, voct + 0.05 / 12.0), (Note::DSharp1, 6) ); let voct = 2.0 + 6.0 / 12.0; - assert_eq!(quantize_voct(Ionian, root, voct), (Note::FSharp1, 1)); assert_eq!( - quantize_voct(Ionian, root, voct - 0.05 / 12.0), + quantize_voct_white_keys(Ionian, root, voct), + (Note::FSharp1, 1) + ); + assert_eq!( + quantize_voct_white_keys(Ionian, root, voct - 0.05 / 12.0), (Note::FSharp1, 1) ); assert_eq!( - quantize_voct(Ionian, root, voct + 0.05 / 12.0), + quantize_voct_white_keys(Ionian, root, voct + 0.05 / 12.0), (Note::FSharp1, 1) ); let voct = 2.0 + 8.0 / 12.0; - assert_eq!(quantize_voct(Ionian, root, voct), (Note::GSharp1, 2)); assert_eq!( - quantize_voct(Ionian, root, voct - 0.05 / 12.0), + quantize_voct_white_keys(Ionian, root, voct), (Note::GSharp1, 2) ); assert_eq!( - quantize_voct(Ionian, root, voct + 0.05 / 12.0), + quantize_voct_white_keys(Ionian, root, voct - 0.05 / 12.0), + (Note::GSharp1, 2) + ); + assert_eq!( + quantize_voct_white_keys(Ionian, root, voct + 0.05 / 12.0), (Note::GSharp1, 2) ); let voct = 2.0 + 10.0 / 12.0; - assert_eq!(quantize_voct(Ionian, root, voct), (Note::ASharp1, 3)); assert_eq!( - quantize_voct(Ionian, root, voct - 0.05 / 12.0), + quantize_voct_white_keys(Ionian, root, voct), + (Note::ASharp1, 3) + ); + assert_eq!( + quantize_voct_white_keys(Ionian, root, voct - 0.05 / 12.0), (Note::ASharp1, 3) ); assert_eq!( - quantize_voct(Ionian, root, voct + 0.05 / 12.0), + quantize_voct_white_keys(Ionian, root, voct + 0.05 / 12.0), (Note::ASharp1, 3) ); } @@ -478,7 +822,7 @@ mod tests { fn quantize_voct_with_note_below_the_lowest_root() { let voct = 1.0 / 12.0; assert_eq!( - quantize_voct(Ionian, Note::A0, voct), + quantize_voct_white_keys(Ionian, Note::A0, voct), (Note::CSharpMinus1, 3) ); } @@ -486,7 +830,10 @@ mod tests { #[test] fn quantize_voct_note_over_limit_and_stay_in_scale() { let voct = 100.0; - assert_eq!(quantize_voct(Ionian, Note::B0, voct), (Note::FSharp9, 5)); + assert_eq!( + quantize_voct_white_keys(Ionian, Note::B0, voct), + (Note::FSharp9, 5) + ); } #[test]