Skip to main content

music21_rs/pitch/
display.rs

1//! Whether a written accidental is needed: music21's
2//! `updateAccidentalDisplay`, with its rules for repeats, octaves, ties,
3//! simultaneities and key signatures.
4
5use super::*;
6
7impl Pitch {
8    /// music21's `_nameInKeySignature`: whether one of a key signature's
9    /// altered pitches has this pitch's step and the same accidental. A
10    /// pitch with no accidental object never matches.
11    pub fn name_in_key_signature(&self, altered_pitches: &[Pitch]) -> bool {
12        if !self.has_accidental {
13            return false;
14        }
15        altered_pitches.iter().any(|p| {
16            p.step == self.step && p.has_accidental && p.accidental.name() == self.accidental.name()
17        })
18    }
19
20    /// music21's `_stepInKeySignature`: whether a key signature alters this
21    /// pitch's step at all, whatever the accidental.
22    pub fn step_in_key_signature(&self, altered_pitches: &[Pitch]) -> bool {
23        altered_pitches.iter().any(|p| p.step == self.step)
24    }
25
26    pub(super) fn set_display_status_creating(&mut self, status: bool) {
27        if !self.has_accidental {
28            self.accidental = Accidental::natural();
29            self.has_accidental = true;
30        }
31        self.accidental.set_display_status(Some(status));
32    }
33
34    /// Decides whether this pitch's accidental should be shown, given the
35    /// pitches before it, and records the answer as the accidental's
36    /// `display_status`, adding a natural where a cautionary one is called
37    /// for: music21's `updateAccidentalDisplay`, with the same rules for
38    /// repeats, octaves, ties, key signatures and simultaneities.
39    pub fn update_accidental_display(&mut self, options: &AccidentalDisplayOptions<'_>) {
40        let altered = options.altered_pitches;
41        let past_all: Vec<&Pitch> = options
42            .pitch_past_measure
43            .iter()
44            .chain(options.pitch_past.iter())
45            .collect();
46        let mut display_if_no_previous_accidentals = false;
47
48        if !options.override_status
49            && self.has_accidental
50            && self.accidental.display_status().is_some()
51        {
52            return;
53        }
54        if self.has_accidental && self.accidental.display_type() == "never" {
55            self.accidental.set_display_status(Some(false));
56            return;
57        }
58        if options.last_note_was_tied {
59            if self.has_accidental {
60                let even_tied = self.accidental.display_type() == "even-tied";
61                self.accidental.set_display_status(Some(even_tied));
62            }
63            return;
64        }
65        if options.cautionary_pitch_class
66            && options
67                .other_simultaneous_pitches
68                .iter()
69                .any(|p| p.step == self.step && p.pitch_class() != self.pitch_class())
70        {
71            self.set_display_status_creating(true);
72            return;
73        }
74        if options.cautionary_all
75            || (self.has_accidental
76                && matches!(self.accidental.display_type(), "even-tied" | "always"))
77        {
78            self.set_display_status_creating(true);
79            return;
80        }
81        if past_all.is_empty() {
82            if self.has_accidental
83                && (options.override_status || self.accidental.display_status() != Some(true))
84            {
85                let status = if self.accidental.name() == "natural" {
86                    self.step_in_key_signature(altered)
87                } else {
88                    !self.name_in_key_signature(altered)
89                };
90                self.accidental.set_display_status(Some(status));
91            } else if self.has_accidental
92                && self.accidental.display_status() == Some(true)
93                && self.name_in_key_signature(altered)
94            {
95                self.accidental.set_display_status(Some(false));
96            } else if (!self.has_accidental || self.accidental.name() == "natural")
97                && self.step_in_key_signature(altered)
98            {
99                self.set_display_status_creating(true);
100            }
101            return;
102        }
103        for past in options.pitch_past.iter().rev() {
104            if past.step == self.step && past.octave == self.octave {
105                if past.name() != self.name() {
106                    self.set_display_status_creating(true);
107                    return;
108                }
109                break;
110            }
111        }
112        let mut set_from_pitch_past = false;
113        let out_of_measure_length = options.pitch_past_measure.len();
114        let self_name = self.name();
115        let self_name_with_octave = self.name_with_octave();
116        let name_in_key = self.name_in_key_signature(altered);
117        let step_in_key = self.step_in_key_signature(altered);
118        let if_absolutely_necessary =
119            self.has_accidental && self.accidental.display_type() == "if-absolutely-necessary";
120        for i in (0..past_all.len()).rev() {
121            let past_in_measure = i >= out_of_measure_length;
122            let continuous_repeats_in_measure = past_in_measure
123                && past_all[i..]
124                    .iter()
125                    .all(|p| p.name_with_octave() == self_name_with_octave);
126            if !past_in_measure && if_absolutely_necessary {
127                break;
128            }
129            if !past_in_measure
130                && self.has_accidental
131                && self.accidental.name() != "natural"
132                && !name_in_key
133            {
134                self.accidental.set_display_status(Some(true));
135                return;
136            }
137            let past = past_all[i];
138            if past.step != self.step {
139                continue;
140            }
141            let octave_match = self.octave == past.octave;
142            let past_acc = past.explicit_accidental();
143            let past_name = past_acc.map(Accidental::name);
144            let past_status = past_acc.and_then(Accidental::display_status);
145            let self_acc_name = self
146                .has_accidental
147                .then(|| self.accidental.name().to_string());
148            let self_status = self
149                .has_accidental
150                .then(|| self.accidental.display_status())
151                .flatten();
152
153            if continuous_repeats_in_measure && past_status == Some(true) {
154                if self.has_accidental {
155                    self.accidental.set_display_status(Some(false));
156                }
157                return;
158            } else if continuous_repeats_in_measure
159                && past_acc.is_some()
160                && self_acc_name.is_some()
161                && past_name == self_acc_name.as_deref()
162            {
163                if !name_in_key && (!octave_match || past_status == Some(false)) {
164                    display_if_no_previous_accidentals = true;
165                    continue;
166                }
167                self.accidental.set_display_status(Some(false));
168                set_from_pitch_past = true;
169                break;
170            } else if past_name == Some("natural")
171                && (self_acc_name.is_none() || self_acc_name.as_deref() == Some("natural"))
172            {
173                if continuous_repeats_in_measure {
174                    if step_in_key && !octave_match {
175                        self.set_display_status_creating(true);
176                    } else if self.has_accidental {
177                        self.accidental.set_display_status(Some(false));
178                    }
179                } else if step_in_key
180                    && (options.cautionary_not_immediate_repeat || !past_in_measure)
181                {
182                    self.set_display_status_creating(true);
183                } else if self.has_accidental {
184                    self.accidental.set_display_status(Some(false));
185                }
186                set_from_pitch_past = true;
187                break;
188            } else if past_acc.is_some()
189                && past.name() != self_name
190                && past_name != Some("natural")
191                && (self_acc_name.is_none() || self_status == Some(false))
192            {
193                if !octave_match && !options.cautionary_pitch_class {
194                    continue;
195                }
196                if !octave_match && if_absolutely_necessary {
197                    continue;
198                }
199                self.set_display_status_creating(true);
200                set_from_pitch_past = true;
201                break;
202            } else if self_acc_name.is_some()
203                && (((past_acc.is_none() || past_name == Some("natural"))
204                    && self_acc_name.as_deref() != Some("natural"))
205                    || (past_acc.is_some()
206                        && past_name != self_acc_name.as_deref()
207                        && (octave_match || !if_absolutely_necessary)))
208            {
209                self.accidental.set_display_status(Some(true));
210                set_from_pitch_past = true;
211                break;
212            } else if past_acc.is_none() && self_acc_name.is_some() {
213                let status = if self_acc_name.as_deref() == Some("natural") {
214                    step_in_key
215                } else {
216                    true
217                };
218                self.accidental.set_display_status(Some(status));
219                set_from_pitch_past = true;
220                break;
221            } else if !continuous_repeats_in_measure
222                && past_acc.is_some()
223                && self_acc_name.is_some()
224                && past_name == self_acc_name.as_deref()
225                && octave_match
226            {
227                if !options.cautionary_not_immediate_repeat && past_status != Some(false) {
228                    self.accidental.set_display_status(Some(false));
229                    display_if_no_previous_accidentals = false;
230                    set_from_pitch_past = true;
231                    break;
232                } else if past_status == Some(false) {
233                    display_if_no_previous_accidentals = true;
234                } else {
235                    self.accidental.set_display_status(Some(!name_in_key));
236                    return;
237                }
238            }
239        }
240        if display_if_no_previous_accidentals {
241            if !name_in_key {
242                self.set_display_status_creating(true);
243            } else if self.has_accidental {
244                self.accidental.set_display_status(Some(false));
245            }
246        } else if !set_from_pitch_past && self.has_accidental {
247            let status = if self.accidental.name() == "natural" {
248                step_in_key
249            } else {
250                !name_in_key
251            };
252            self.accidental.set_display_status(Some(status));
253        } else if !set_from_pitch_past && step_in_key {
254            self.set_display_status_creating(true);
255        }
256    }
257}
258
259/// The context music21's `updateAccidentalDisplay` reads: the pitches that
260/// came before, in this measure and the previous one, the other pitches
261/// sounding at the same time, the key signature's altered pitches, and the
262/// cautionary-accidental switches, with music21's defaults.
263#[derive(Clone, Debug)]
264pub struct AccidentalDisplayOptions<'a> {
265    /// Pitches preceding this one in the same measure.
266    pub pitch_past: &'a [Pitch],
267    /// Pitches preceding this one in the previous measure.
268    pub pitch_past_measure: &'a [Pitch],
269    /// Other pitches in the same simultaneity.
270    pub other_simultaneous_pitches: &'a [Pitch],
271    /// The key signature's altered pitches.
272    pub altered_pitches: &'a [Pitch],
273    /// Whether a past accidental in any octave calls for a cautionary one.
274    pub cautionary_pitch_class: bool,
275    /// Whether every accidental is shown.
276    pub cautionary_all: bool,
277    /// Whether an already decided `display_status` is decided again.
278    pub override_status: bool,
279    /// Whether an altered pitch shows its accidental again unless it
280    /// immediately repeats.
281    pub cautionary_not_immediate_repeat: bool,
282    /// Whether this pitch follows a tie.
283    pub last_note_was_tied: bool,
284}
285
286impl Default for AccidentalDisplayOptions<'_> {
287    fn default() -> Self {
288        Self {
289            pitch_past: &[],
290            pitch_past_measure: &[],
291            other_simultaneous_pitches: &[],
292            altered_pitches: &[],
293            cautionary_pitch_class: true,
294            cautionary_all: false,
295            override_status: false,
296            cautionary_not_immediate_repeat: true,
297            last_note_was_tied: false,
298        }
299    }
300}