feat(crucible): name slices only by a sure note, spelled for the song key

A slice gets a note name only when at least 60 % of up to twelve reads
across its body sit on one note; growls, pitch drops and noise get no name
rather than a wrong one. An optional song key spells names with flats in
flat keys (Eb2, not D#2).
This commit is contained in:
nvrl committed 2026-11-06 21:05:31 +01:00
1 parent adfb2c0287
commit d6939a753a
8 files changed
+143 -19

No files matched your search

+18 -3
View File
@@ -218,10 +218,18 @@ pub fn hz_to_midi(hz: f32) -> f32 {
}
/// Scientific note name: MIDI 60 = C4, 43.65 Hz = F1. FL Studio labels MIDI 60 as C5.
pub fn note_name(note: u8) -> String {
const NAMES: [&str; 12] = [
note_name_spelled(note, false)
}
/// Note name with sharps, or with flats (Db, Eb, Gb, Ab, Bb) for keys written in flats.
pub fn note_name_spelled(note: u8, flats: bool) -> String {
const SHARPS: [&str; 12] = [
"C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B",
];
format!("{}{}", NAMES[note as usize % 12], note as i32 / 12 - 1)
const FLATS: [&str; 12] = [
"C", "Db", "D", "Eb", "E", "F", "Gb", "G", "Ab", "A", "Bb", "B",
];
let names = if flats { FLATS } else { SHARPS };
format!("{}{}", names[note as usize % 12], note as i32 / 12 - 1)
}
/// Reads a scientific note name such as "F1" or "c#2", the inverse of [`note_name`].
pub fn parse_note(text: &str) -> Option<u8> {
@@ -231,7 +239,14 @@ pub fn parse_note(text: &str) -> Option<u8> {
let text = text.trim();
let split = text.find(|c: char| c.is_ascii_digit() || c == '-')?;
let (pitch, octave) = text.split_at(split);
let class = NAMES.iter().position(|n| n.eq_ignore_ascii_case(pitch))? as i32;
const FLATS: [&str; 12] = [
"C", "Db", "D", "Eb", "E", "F", "Gb", "G", "Ab", "A", "Bb", "B",
];
let class = NAMES
.iter()
.position(|n| n.eq_ignore_ascii_case(pitch))
.or_else(|| FLATS.iter().position(|n| n.eq_ignore_ascii_case(pitch)))?
as i32;
let octave: i32 = octave.parse().ok()?;
u8::try_from((octave + 1) * 12 + class)
.ok()
+2
View File
@@ -374,5 +374,7 @@ fn note_names_parse_back() {
assert_eq!(common_dsp::parse_note(&common_dsp::note_name(n)), Some(n));
}
assert_eq!(common_dsp::parse_note(" f1 "), Some(29));
assert_eq!(common_dsp::note_name_spelled(30, true), "Gb1");
assert_eq!(common_dsp::parse_note("Gb1"), Some(30));
assert_eq!(common_dsp::parse_note("H2"), None);
}