diff --git a/src/formatters.rs b/src/formatters.rs index 00a50796..2035707c 100644 --- a/src/formatters.rs +++ b/src/formatters.rs @@ -180,17 +180,18 @@ pub fn s2v_f32_hz_then_khz() -> Arc Option + Send + Sync> { Arc::new(move |string| { let string = string.trim(); - // If the user inputs a note representation, then we'll use that - if let Some(midi_note_number) = note_formatter(string) { - return Some(util::f32_midi_note_to_freq(midi_note_number as f32)); - } + // The input can contain a frequency in Hz or kHz, a note name, a note name and cents, or + // one of those two combined with a frequency. In the last case we'll ignore the frequency. + // If the string cannot be parsed as a note name, we'll try parsing it as a frequency + // instead. This is needed for the formatting roundtrip to work correctly. The input will + // consists of 1 to three segments, so we'll try to unpack them like this so we can pattern + // match on them + let mut segments = string.split(','); + let segments = (segments.next(), segments.next(), segments.next()); - // This can also contain semitones. If we cannot parse this as a note name, we'll try - // parsing it as a frequency instead. We'll also `XXX Hz, C3` where `XXX Hz` does not match - // C3 since the user may just edit that of the text input box while leaving the note name - // untouched. - let string = if let Some((midi_note_number_str, cents_str)) = string.split_once(',') { - // If it contains a comma we'll also try parsing cents + if let (_, Some(midi_note_number_str), Some(cents_str)) + | (Some(midi_note_number_str), Some(cents_str), None) = segments + { let cents_str = cents_str .trim_start_matches([' ', '+']) .trim_end_matches([' ', 'C', 'c', 'E', 'e', 'N', 'n', 'T', 't', 'S', 's', '.']); @@ -203,22 +204,23 @@ pub fn s2v_f32_hz_then_khz() -> Arc Option + Send + Sync> { let cents_multiplier = 2.0f32.powf(cents as f32 / 100.0 / 12.0); return Some(plain_note_freq * cents_multiplier); } + } - // NOTE: As mentioned above, if the input contained a `,` we'll try parsing the part - // before the comma as a frequency string if we could not parse the right hand - // side has a note string. This can happen if the user edits the output of - // `v2s_f32_hz_then_khz_with_note_name()`. - midi_note_number_str - } else { - string - }; + if let (_, Some(midi_note_number_str), _) | (Some(midi_note_number_str), None, None) = + segments + { + if let Some(midi_note_number) = note_formatter(midi_note_number_str) { + return Some(util::f32_midi_note_to_freq(midi_note_number as f32)); + } + } // Otherwise we'll accept values in either Hz (with or without unit) or kHz - let cleaned_string = string + let frequency_segment = segments.0?; + let cleaned_string = frequency_segment .trim_end_matches([' ', 'k', 'K', 'h', 'H', 'z', 'Z']) .parse() .ok(); - match string.get(string.len().saturating_sub(3)..) { + match frequency_segment.get(frequency_segment.len().saturating_sub(3)..) { Some(unit) if unit.eq_ignore_ascii_case("khz") => cleaned_string.map(|x| x * 1000.0), // Even if there's no unit at all, just assume the input is in Hertz _ => cleaned_string,