1use super::*;
6
7impl Chord {
8 pub fn has_component_volumes(&self) -> bool {
13 self.notes.iter().all(Note::has_volume_information)
14 }
15
16 pub fn volume(&self) -> Volume {
20 if let Some(volume) = &self.volume {
24 return volume.clone();
25 }
26 if !self.has_component_volumes() {
27 return Volume::default();
28 }
29 let velocities: Vec<IntegerType> = self
30 .notes
31 .iter()
32 .filter_map(|note| note.volume().velocity())
33 .collect();
34 if velocities.is_empty() {
35 return Volume::default();
36 }
37 let total: IntegerType = velocities.iter().sum();
38 let mean = FloatType::from(total) / velocities.len() as FloatType;
39 Volume::from_velocity(mean.round_ties_even() as IntegerType)
40 }
41
42 pub fn grace_note(&self, appoggiatura: bool) -> Self {
45 let written = self.duration().cloned().unwrap_or_default();
46 let mut grace = self.clone();
47 grace.set_duration(written.grace_duration_as(appoggiatura));
48 grace
49 }
50
51 pub fn stored_instrument(&self) -> Option<&crate::instrument::Instrument> {
54 self.stored_instrument.as_deref()
55 }
56
57 pub fn set_stored_instrument(&mut self, instrument: Option<crate::instrument::Instrument>) {
59 self.stored_instrument = instrument.map(Box::new);
60 }
61
62 pub fn instrument(&self) -> Option<&crate::instrument::Instrument> {
66 self.stored_instrument()
67 }
68
69 pub fn has_volume_information(&self) -> bool {
72 self.volume.is_some()
73 }
74
75 pub fn set_volume(&mut self, volume: Option<Volume>) {
77 for note in &mut self.notes {
78 note.set_volume(None);
79 }
80 self.volume = volume;
81 }
82
83 pub fn set_volumes(&mut self, volumes: &[Volume]) -> Result<()> {
87 if volumes.is_empty() {
88 return Err(Error::Chord(
89 "setVolumes needs at least one volume".to_string(),
90 ));
91 }
92 self.volume = None;
93 for (index, note) in self.notes.iter_mut().enumerate() {
94 note.set_volume(Some(volumes[index % volumes.len()].clone()));
95 }
96 Ok(())
97 }
98
99 pub fn color(&self) -> Option<&str> {
102 self.color.as_deref()
103 }
104
105 pub fn set_color(&mut self, color: Option<String>) {
107 self.color = color;
108 }
109
110 pub fn notehead(&self) -> Notehead {
113 self.notehead
114 }
115
116 pub fn set_notehead(&mut self, notehead: Notehead) {
118 self.notehead = notehead;
119 }
120
121 pub fn notehead_fill(&self) -> Option<bool> {
123 self.notehead_fill
124 }
125
126 pub fn set_notehead_fill(&mut self, fill: Option<bool>) {
128 self.notehead_fill = fill;
129 }
130
131 pub fn notehead_parenthesis(&self) -> bool {
133 self.notehead_parenthesis
134 }
135
136 pub fn set_notehead_parenthesis(&mut self, parenthesis: bool) {
138 self.notehead_parenthesis = parenthesis;
139 }
140
141 pub fn stem_direction(&self) -> StemDirection {
143 self.stem_direction
144 }
145
146 pub fn set_stem_direction(&mut self, direction: StemDirection) {
148 self.stem_direction = direction;
149 }
150
151 pub fn beams(&self) -> &Beams {
153 &self.beams
154 }
155
156 pub fn set_beams(&mut self, beams: Beams) {
158 self.beams = beams;
159 }
160
161 pub fn color_of_pitch(&self, pitch: &Pitch) -> Option<&str> {
164 self.note_for_pitch(pitch)
165 .and_then(Note::color)
166 .or(self.color())
167 }
168
169 pub fn tie(&self) -> Option<&Tie> {
172 self.notes.iter().find_map(Note::tie)
173 }
174
175 pub fn set_tie(&mut self, tie: Option<Tie>) {
177 for note in &mut self.notes {
178 note.set_tie(tie.clone());
179 }
180 }
181
182 pub fn expressions(&self) -> &[crate::expressions::Expression] {
185 &self.expressions
186 }
187
188 pub fn expressions_mut(&mut self) -> &mut Vec<crate::expressions::Expression> {
190 &mut self.expressions
191 }
192
193 pub fn articulations(&self) -> &[crate::articulations::Articulation] {
196 &self.articulations
197 }
198
199 pub fn articulations_mut(&mut self) -> &mut Vec<crate::articulations::Articulation> {
201 &mut self.articulations
202 }
203
204 pub fn lyrics(&self) -> &[Lyric] {
207 self.notes.first().map_or(&[], Note::lyrics)
208 }
209
210 pub fn add_lyric(
212 &mut self,
213 text: &str,
214 number: Option<IntegerType>,
215 apply_raw: bool,
216 ) -> Result<()> {
217 match self.notes.first_mut() {
218 Some(note) => note.add_lyric(text, number, apply_raw),
219 None => Err(Error::Chord(
220 "an empty chord has nothing to sing".to_string(),
221 )),
222 }
223 }
224
225 pub fn annotate_intervals(
233 &self,
234 strip_specifiers: bool,
235 sort_pitches: bool,
236 ) -> Result<Vec<String>> {
237 let mut reduced = self.remove_redundant_pitches();
238 if sort_pitches {
239 reduced = reduced.sort_ascending();
240 }
241 let pitches = reduced.pitches();
242 let Some(lowest) = pitches.first() else {
243 return Ok(Vec::new());
244 };
245 let mut names = Vec::with_capacity(pitches.len().saturating_sub(1));
246 for pitch in pitches.iter().skip(1).rev() {
247 let interval = Interval::between_pitches(lowest, pitch)?;
248 names.push(if strip_specifiers {
249 interval.generic().semi_simple_undirected().to_string()
250 } else {
251 interval.semi_simple_name()
252 });
253 }
254 if strip_specifiers && sort_pitches {
255 names.sort_by(|left, right| right.cmp(left));
256 }
257 Ok(names)
258 }
259
260 pub fn annotated_with_intervals(
263 &self,
264 strip_specifiers: bool,
265 sort_pitches: bool,
266 ) -> Result<Self> {
267 let names = self.annotate_intervals(strip_specifiers, sort_pitches)?;
268 let mut annotated = self.clone();
269 for name in names {
270 annotated.add_lyric(&name, None, false)?;
271 }
272 Ok(annotated)
273 }
274
275 pub fn string_harmonic(&self) -> Result<Option<Self>> {
283 let [stopped, touched] = match self.notes.get(..2) {
284 Some([first, second]) => [first, second],
285 _ => return Ok(None),
286 };
287 if touched.notehead() != Notehead::Diamond {
288 return Ok(None);
289 }
290 let distance = crate::interval::notes_to_chromatic(&stopped.pitch, &touched.pitch)?;
291 let harmonic = match distance.interval_class() {
292 0 => 2,
293 7 => 3,
294 5 => 4,
295 4 => 5,
296 3 => 6,
297 6 => 7,
298 _ => 1,
299 };
300 let sounding = if harmonic == 1 {
301 stopped.pitch.clone()
302 } else {
303 stopped.pitch.harmonic(harmonic)?
304 };
305 let mut sounding_note = Note::from_pitch(sounding);
306 sounding_note.set_notehead_parenthesis(true);
307 sounding_note.set_notehead_fill(Some(true));
308 sounding_note.set_stem_direction(crate::notation::StemDirection::NoStem);
309 let mut touched_note = Note::from_pitch(touched.pitch.clone());
310 touched_note.set_notehead(touched.notehead());
311 let notes = vec![
312 Note::from_pitch(stopped.pitch.clone()),
313 touched_note,
314 sounding_note,
315 ];
316 Ok(Some(Self::new(notes.as_slice())?))
317 }
318}
319
320#[cfg(test)]
321mod tests {
322 use super::*;
323 use crate::notation::TieType;
324
325 #[test]
326 fn annotate_intervals_matches_music21() {
327 let triad = Chord::new("C4 E4 G4").unwrap();
328 assert_eq!(triad.annotate_intervals(true, true).unwrap(), ["5", "3"]);
329 assert_eq!(triad.annotate_intervals(false, true).unwrap(), ["P5", "M3"]);
330 let with_octave = Chord::new("C4 E4 G4 C5").unwrap();
331 assert_eq!(
332 with_octave.annotate_intervals(true, true).unwrap(),
333 ["8", "5", "3"]
334 );
335 assert_eq!(
336 with_octave.annotate_intervals(false, true).unwrap(),
337 ["P8", "P5", "M3"]
338 );
339 let doubled = Chord::new("C4 E4 G4 E4").unwrap();
343 assert_eq!(doubled.annotate_intervals(true, true).unwrap(), ["5", "3"]);
344 let spread = Chord::new("C4 E4 G4 E5").unwrap();
345 assert_eq!(
346 spread.annotate_intervals(true, true).unwrap(),
347 ["5", "3", "3"]
348 );
349 assert!(
350 Chord::empty()
351 .annotate_intervals(true, true)
352 .unwrap()
353 .is_empty()
354 );
355 }
356
357 #[test]
358 fn annotated_intervals_become_lyrics() {
359 let annotated = Chord::new("C4 E4 G4")
360 .unwrap()
361 .annotated_with_intervals(true, true)
362 .unwrap();
363 let texts: Vec<String> = annotated.lyrics().iter().map(Lyric::text).collect();
364 assert_eq!(texts, ["5", "3"]);
365 assert_eq!(annotated.lyrics()[1].number(), 2);
366 }
367
368 #[test]
369 fn component_volumes_average_into_the_chord() {
370 let mut chord = Chord::new("C4 E4 G4").unwrap();
371 assert!(!chord.has_component_volumes());
372 assert_eq!(chord.volume().velocity(), None);
373
374 chord
375 .set_volumes(&[
376 Volume::from_velocity(60),
377 Volume::from_velocity(20),
378 Volume::from_velocity(120),
379 ])
380 .unwrap();
381 assert!(chord.has_component_volumes());
382 let velocities: Vec<Option<IntegerType>> = chord
383 .notes()
384 .iter()
385 .map(|note| note.volume().velocity())
386 .collect();
387 assert_eq!(velocities, [Some(60), Some(20), Some(120)]);
388 assert_eq!(chord.volume().velocity(), Some(67));
389
390 chord.set_volume(Some(Volume::from_velocity(90)));
392 assert!(!chord.has_component_volumes());
393 assert_eq!(chord.volume().velocity(), Some(90));
394 assert!(chord.set_volumes(&[]).is_err());
395 }
396
397 #[test]
398 fn a_shorter_volume_list_cycles() {
399 let mut chord = Chord::new("C4 E4 G4 B-4").unwrap();
400 chord
401 .set_volumes(&[Volume::from_velocity(40), Volume::from_velocity(80)])
402 .unwrap();
403 let velocities: Vec<Option<IntegerType>> = chord
404 .notes()
405 .iter()
406 .map(|note| note.volume().velocity())
407 .collect();
408 assert_eq!(velocities, [Some(40), Some(80), Some(40), Some(80)]);
409 }
410
411 #[test]
412 fn ties_and_colours_reach_the_notes() {
413 let mut chord = Chord::new("C4 E4 G4").unwrap();
414 assert!(chord.tie().is_none());
415 chord.set_tie(Some(Tie::new(TieType::Start)));
416 assert_eq!(chord.tie().map(Tie::tie_type), Some(TieType::Start));
417 assert!(chord.notes().iter().all(|note| note.tie().is_some()));
418 chord.set_tie(None);
419 assert!(chord.tie().is_none());
420
421 let e4 = Pitch::from_name("E4").unwrap();
422 chord.set_color(Some("blue".to_string()));
423 assert_eq!(chord.color_of_pitch(&e4), Some("blue"));
424 chord
425 .note_for_pitch_mut(&e4)
426 .unwrap()
427 .set_color(Some("red".to_string()));
428 assert_eq!(chord.color_of_pitch(&e4), Some("red"));
429 let c4 = Pitch::from_name("C4").unwrap();
430 assert_eq!(chord.color_of_pitch(&c4), Some("blue"));
431 }
432
433 #[test]
434 fn a_diamond_notehead_reads_as_a_string_harmonic() {
435 let mut chord = Chord::new("D3 G3").unwrap();
436 assert!(chord.string_harmonic().unwrap().is_none());
437 chord.notes_mut()[1].set_notehead(Notehead::Diamond);
438 let sounded = chord.string_harmonic().unwrap().unwrap();
439 assert_eq!(
440 sounded
441 .pitches()
442 .iter()
443 .map(Pitch::name_with_octave)
444 .collect::<Vec<_>>(),
445 ["D3", "G3", "D5"]
446 );
447 let sounding = &sounded.notes()[2];
448 assert!(sounding.notehead_parenthesis());
449 assert_eq!(sounding.stem_direction(), StemDirection::NoStem);
450 assert_eq!(sounded.notes()[1].notehead(), Notehead::Diamond);
451 }
452}