Skip to main content

music21_rs/note/
mod.rs

1use crate::articulations::Articulation;
2use crate::defaults::{FloatType, IntegerType};
3use crate::duration::Duration;
4use crate::error::Result;
5use crate::expressions::Expression;
6use crate::instrument::Instrument;
7use crate::interval::Interval;
8use crate::notation::{Beams, Lyric, NoteSize, Notehead, StemDirection, Tie, verses};
9use crate::pitch::Pitch;
10use crate::volume::Volume;
11
12use std::fmt::{Display, Formatter};
13use std::str::FromStr;
14
15#[derive(Clone, Debug)]
16#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
17/// A pitched note.
18#[must_use]
19pub struct Note {
20    pub(crate) pitch: Pitch,
21    duration: Option<Duration>,
22    #[cfg_attr(feature = "serde", serde(default))]
23    notation: Notation,
24}
25
26/// The notation a note carries besides its pitch and duration: how it is
27/// tied, drawn, stemmed, coloured, sung and sounded.
28#[derive(Clone, Debug, Default, PartialEq)]
29#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
30struct Notation {
31    tie: Option<Tie>,
32    notehead: Notehead,
33    notehead_fill: Option<bool>,
34    notehead_parenthesis: bool,
35    stem_direction: StemDirection,
36    color: Option<String>,
37    #[cfg_attr(feature = "serde", serde(default))]
38    size: Option<NoteSize>,
39    volume: Option<Volume>,
40    lyrics: Vec<Lyric>,
41    beams: Beams,
42    /// Boxed: an instrument is large and most notes carry none.
43    stored_instrument: Option<Box<Instrument>>,
44    expressions: Vec<Expression>,
45    articulations: Vec<Articulation>,
46    hidden: bool,
47    #[cfg_attr(feature = "serde", serde(default))]
48    id: Option<String>,
49}
50
51impl Note {
52    /// Builds a note from a pitch name such as `"C#4"` or `"E-"`.
53    pub fn from_name(name: impl AsRef<str>) -> Result<Self> {
54        Pitch::from_name(name).map(Self::from_pitch)
55    }
56
57    /// Builds a note from a pitch-space number, where 60 is middle C.
58    pub fn from_number(number: FloatType) -> Result<Self> {
59        Pitch::from_number(number).map(Self::from_pitch)
60    }
61
62    /// Builds a note from an existing [`Pitch`].
63    pub fn from_pitch(pitch: Pitch) -> Self {
64        Self {
65            pitch,
66            duration: None,
67            notation: Notation::default(),
68        }
69    }
70
71    /// Returns the note's pitch.
72    pub fn pitch(&self) -> &Pitch {
73        &self.pitch
74    }
75
76    /// Sets the note's pitch, keeping its duration and notation: music21's
77    /// `Note.pitch` setter.
78    pub fn set_pitch(&mut self, pitch: Pitch) {
79        self.pitch = pitch;
80    }
81
82    /// Returns the pitch name without an octave, such as `"C#"` or `"E-"`.
83    pub fn pitch_name(&self) -> String {
84        self.pitch.name()
85    }
86
87    /// Returns the pitch name with an octave when one is set.
88    pub fn pitch_name_with_octave(&self) -> String {
89        self.pitch.name_with_octave()
90    }
91
92    /// The pitch's step letter.
93    pub fn step(&self) -> char {
94        self.pitch.step().as_char()
95    }
96
97    /// The pitch's octave, if it has one.
98    pub fn octave(&self) -> crate::defaults::Octave {
99        self.pitch.octave()
100    }
101
102    /// The note's one pitch as a list, the shape a chord's `pitches` has.
103    pub fn pitches(&self) -> Vec<Pitch> {
104        vec![self.pitch.clone()]
105    }
106
107    /// music21's `fullName`: `E-flat in octave 4 Quarter Note`, with the
108    /// duration's name left out when the note has none.
109    pub fn full_name(&self) -> String {
110        match self.duration.as_ref() {
111            Some(duration) => format!("{} {} Note", self.pitch.full_name(), duration.full_name()),
112            None => format!("{} Note", self.pitch.full_name()),
113        }
114    }
115
116    /// Returns the note duration when one has been assigned.
117    pub fn duration(&self) -> Option<&Duration> {
118        self.duration.as_ref()
119    }
120
121    /// Assigns a duration to the note.
122    pub fn set_duration(&mut self, duration: Duration) {
123        self.duration = Some(duration);
124    }
125
126    /// Returns a copy of this note with the supplied duration.
127    pub fn with_duration(mut self, duration: Duration) -> Self {
128        self.set_duration(duration);
129        self
130    }
131
132    /// This note as a grace note: written as it is, sounding for no time,
133    /// music21's `getGrace`. A grace note is slashed and takes no time, and
134    /// an `appoggiatura` is unslashed and takes its time from the note it
135    /// leans on.
136    ///
137    /// ```
138    /// use music21_rs::{Duration, DurationType, Note};
139    ///
140    /// let note = Note::from_name("G4")?.with_duration(Duration::half());
141    /// let grace = note.grace_note(false);
142    /// let duration = grace.duration().unwrap();
143    /// assert_eq!(duration.quarter_length(), 0.0);
144    /// assert_eq!(duration.components(), [(DurationType::Half, 0)]);
145    /// assert!(duration.grace().unwrap().slash());
146    /// assert!(!note.grace_note(true).duration().unwrap().grace().unwrap().slash());
147    /// # Ok::<(), music21_rs::Error>(())
148    /// ```
149    pub fn grace_note(&self, appoggiatura: bool) -> Self {
150        let written = self.duration.clone().unwrap_or_default();
151        self.clone()
152            .with_duration(written.grace_duration_as(appoggiatura))
153    }
154
155    /// Returns this note transposed by the interval, keeping everything but
156    /// its pitch.
157    pub fn transpose(&self, interval: &Interval) -> Result<Self> {
158        Ok(Self {
159            pitch: interval.transpose_pitch(&self.pitch)?,
160            duration: self.duration.clone(),
161            notation: self.notation.clone(),
162        })
163    }
164
165    // ---- notation --------------------------------------------------------
166
167    /// The beams joining this note's flags to its neighbours': music21's
168    /// `beams`.
169    pub fn beams(&self) -> &Beams {
170        &self.notation.beams
171    }
172
173    /// The same, to be changed.
174    pub fn beams_mut(&mut self) -> &mut Beams {
175        &mut self.notation.beams
176    }
177
178    /// Replaces the beams.
179    pub fn set_beams(&mut self, beams: Beams) {
180        self.notation.beams = beams;
181    }
182
183    /// The tie joining this note to its neighbours, if any.
184    pub fn tie(&self) -> Option<&Tie> {
185        self.notation.tie.as_ref()
186    }
187
188    /// Ties this note, or unties it with `None`.
189    pub fn set_tie(&mut self, tie: Option<Tie>) {
190        self.notation.tie = tie;
191    }
192
193    /// The shape the note head is drawn with.
194    pub fn notehead(&self) -> Notehead {
195        self.notation.notehead
196    }
197
198    /// Sets the shape the note head is drawn with.
199    pub fn set_notehead(&mut self, notehead: Notehead) {
200        self.notation.notehead = notehead;
201    }
202
203    /// Whether the note head is filled in: `Some(true)` filled,
204    /// `Some(false)` hollow, `None` to let the duration decide, which is
205    /// music21's default.
206    pub fn notehead_fill(&self) -> Option<bool> {
207        self.notation.notehead_fill
208    }
209
210    /// Sets whether the note head is filled in.
211    pub fn set_notehead_fill(&mut self, fill: Option<bool>) {
212        self.notation.notehead_fill = fill;
213    }
214
215    /// Whether the note head is written in parentheses.
216    pub fn notehead_parenthesis(&self) -> bool {
217        self.notation.notehead_parenthesis
218    }
219
220    /// Sets whether the note head is written in parentheses.
221    pub fn set_notehead_parenthesis(&mut self, parenthesis: bool) {
222        self.notation.notehead_parenthesis = parenthesis;
223    }
224
225    /// Which way the stem points.
226    pub fn stem_direction(&self) -> StemDirection {
227        self.notation.stem_direction
228    }
229
230    /// Sets which way the stem points.
231    pub fn set_stem_direction(&mut self, direction: StemDirection) {
232        self.notation.stem_direction = direction;
233    }
234
235    /// The colour the note is written in, if one was said: music21 keeps
236    /// this on the note's style.
237    pub fn color(&self) -> Option<&str> {
238        self.notation.color.as_deref()
239    }
240
241    /// Sets the colour the note is written in.
242    pub fn set_color(&mut self, color: Option<String>) {
243        self.notation.color = color;
244    }
245
246    /// How large the note is drawn, where a score says: music21 keeps this
247    /// on the note's style as `noteSize`.
248    pub fn size(&self) -> Option<NoteSize> {
249        self.notation.size
250    }
251
252    /// Says how large the note is drawn.
253    pub fn set_size(&mut self, size: Option<NoteSize>) {
254        self.notation.size = size;
255    }
256
257    /// How loud the note is. music21 makes a volume on first access, so this
258    /// answers a default one for a note nobody has marked; see
259    /// [`Self::has_volume_information`] to tell the two apart.
260    pub fn volume(&self) -> Volume {
261        self.notation.volume.clone().unwrap_or_default()
262    }
263
264    /// Sets how loud the note is, or clears it.
265    pub fn set_volume(&mut self, volume: Option<Volume>) {
266        self.notation.volume = volume;
267    }
268
269    /// The instrument this one note is played on, over whichever is in force
270    /// where it stands: music21's `storedInstrument`.
271    pub fn stored_instrument(&self) -> Option<&Instrument> {
272        self.notation.stored_instrument.as_deref()
273    }
274
275    /// Sets the instrument this one note is played on, or clears it.
276    pub fn set_stored_instrument(&mut self, instrument: Option<Instrument>) {
277        self.notation.stored_instrument = instrument.map(Box::new);
278    }
279
280    /// The instrument that plays this note, as far as the note itself can
281    /// say: music21's `getInstrument`, whose search of the streams around a
282    /// note is a question for whoever holds the stream.
283    pub fn instrument(&self) -> Option<&Instrument> {
284        self.stored_instrument()
285    }
286
287    /// Whether a volume was ever set on this note: music21's
288    /// `hasVolumeInformation`, which asks only whether the object is there
289    /// and not whether a velocity was written on it, so a bare
290    /// `Volume::new()` set on a note counts.
291    pub fn has_volume_information(&self) -> bool {
292        self.notation.volume.is_some()
293    }
294
295    /// The fermatas, ornaments and other marks written on the note:
296    /// music21's `expressions`.
297    pub fn expressions(&self) -> &[Expression] {
298        &self.notation.expressions
299    }
300
301    /// The same, to be changed.
302    pub fn expressions_mut(&mut self) -> &mut Vec<Expression> {
303        &mut self.notation.expressions
304    }
305
306    /// Whether it is left off the printed page, keeping its place and its
307    /// time: music21's `style.hideObjectOnPrint`, which a score uses for a
308    /// rest that only fills out a voice.
309    pub fn hidden(&self) -> bool {
310        self.notation.hidden
311    }
312
313    /// Says whether it is left off the printed page.
314    pub fn set_hidden(&mut self, hidden: bool) {
315        self.notation.hidden = hidden;
316    }
317
318    /// The name the note is known by, where it has been given one:
319    /// music21's `id`, when that is a name and not a number of its own
320    /// making.
321    pub fn id(&self) -> Option<&str> {
322        self.notation.id.as_deref()
323    }
324
325    /// Names the note, or takes its name away.
326    pub fn set_id(&mut self, id: Option<String>) {
327        self.notation.id = id;
328    }
329
330    /// How the note is played: its accents, staccatos, bowings and
331    /// fingerings, music21's `articulations`.
332    pub fn articulations(&self) -> &[Articulation] {
333        &self.notation.articulations
334    }
335
336    /// The same, to be changed.
337    pub fn articulations_mut(&mut self) -> &mut Vec<Articulation> {
338        &mut self.notation.articulations
339    }
340
341    /// The syllables sung on this note, one per verse.
342    pub fn lyrics(&self) -> &[Lyric] {
343        &self.notation.lyrics
344    }
345
346    /// The syllables sung on this note, for editing in place.
347    pub fn lyrics_mut(&mut self) -> &mut Vec<Lyric> {
348        &mut self.notation.lyrics
349    }
350
351    /// The text of the first verse, with the hyphens of every further verse
352    /// joined by newlines: music21's `lyric`.
353    pub fn lyric(&self) -> Option<String> {
354        verses::joined(&self.notation.lyrics)
355    }
356
357    /// Replaces every lyric with one verse of text, or clears them with
358    /// `None`. Newlines start further verses, and hyphens say where each
359    /// syllable falls in its word: music21's `lyric` setter.
360    pub fn set_lyric(&mut self, lyric: Option<&str>) -> Result<()> {
361        verses::set(&mut self.notation.lyrics, lyric);
362        Ok(())
363    }
364
365    /// Adds a syllable as a verse: music21's `addLyric`.
366    ///
367    /// With no `number` it becomes the next verse. With one, it *replaces*
368    /// the text of the verse already carrying that number — leaving where
369    /// that syllable falls in its word alone, as music21's plain text
370    /// assignment does — and only becomes a new verse when no verse has it.
371    /// Hyphens in the text say where the syllable falls in its word unless
372    /// `apply_raw` is set, which takes the text as written.
373    pub fn add_lyric(
374        &mut self,
375        text: &str,
376        number: Option<IntegerType>,
377        apply_raw: bool,
378    ) -> Result<()> {
379        verses::add(&mut self.notation.lyrics, text, number, apply_raw);
380        Ok(())
381    }
382
383    /// Puts a syllable in front of the verse at `index`, moving the verses
384    /// from there on down a line: music21's `insertLyric`.
385    ///
386    /// An index past the end appends, as inserting into a list does.
387    pub fn insert_lyric(&mut self, text: &str, index: usize, apply_raw: bool) -> Result<()> {
388        verses::insert(&mut self.notation.lyrics, text, index, apply_raw);
389        Ok(())
390    }
391}
392
393impl FromStr for Note {
394    type Err = crate::error::Error;
395
396    fn from_str(value: &str) -> Result<Self> {
397        Self::from_name(value)
398    }
399}
400
401impl TryFrom<&str> for Note {
402    type Error = crate::error::Error;
403
404    fn try_from(value: &str) -> Result<Self> {
405        Self::from_name(value)
406    }
407}
408
409impl TryFrom<String> for Note {
410    type Error = crate::error::Error;
411
412    fn try_from(value: String) -> Result<Self> {
413        Self::from_name(value)
414    }
415}
416
417impl From<Pitch> for Note {
418    fn from(value: Pitch) -> Self {
419        Self::from_pitch(value)
420    }
421}
422
423impl From<&Pitch> for Note {
424    fn from(value: &Pitch) -> Self {
425        Self::from_pitch(value.clone())
426    }
427}
428
429impl TryFrom<IntegerType> for Note {
430    type Error = crate::error::Error;
431
432    fn try_from(value: IntegerType) -> Result<Self> {
433        Self::from_number(value as FloatType)
434    }
435}
436
437impl Display for Note {
438    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
439        write!(f, "{}", self.pitch_name_with_octave())
440    }
441}
442
443/// Converts a single note-like value into a [`Note`].
444///
445/// This is useful when constructing vectors or other collections that are
446/// later passed to APIs such as `Chord::new`.
447pub trait IntoNote {
448    /// Whether this value came from an integer pitch class or MIDI-like number.
449    const FROM_INTEGER_PITCH: bool = false;
450
451    /// Converts the value into a note.
452    fn try_into_note(self) -> Result<Note>;
453}
454
455impl IntoNote for Note {
456    fn try_into_note(self) -> Result<Note> {
457        Ok(self)
458    }
459}
460
461impl IntoNote for &Note {
462    fn try_into_note(self) -> Result<Note> {
463        Ok(self.clone())
464    }
465}
466
467impl IntoNote for Pitch {
468    fn try_into_note(self) -> Result<Note> {
469        Ok(Note::from_pitch(self))
470    }
471}
472
473impl IntoNote for &Pitch {
474    fn try_into_note(self) -> Result<Note> {
475        Ok(Note::from_pitch(self.clone()))
476    }
477}
478
479impl IntoNote for String {
480    fn try_into_note(self) -> Result<Note> {
481        Note::from_name(self)
482    }
483}
484
485impl IntoNote for &String {
486    fn try_into_note(self) -> Result<Note> {
487        Note::from_name(self.as_str())
488    }
489}
490
491impl IntoNote for &str {
492    fn try_into_note(self) -> Result<Note> {
493        Note::from_name(self)
494    }
495}
496
497impl IntoNote for IntegerType {
498    const FROM_INTEGER_PITCH: bool = true;
499
500    fn try_into_note(self) -> Result<Note> {
501        Note::from_number(self as FloatType)
502    }
503}
504
505#[cfg(test)]
506mod tests {
507    #[test]
508    fn a_note_keeps_the_instrument_it_is_played_on() {
509        let mut note = super::Note::from_name("C4").unwrap();
510        assert!(note.instrument().is_none());
511        let piccolo = crate::Instrument::of_kind("Piccolo").unwrap();
512        note.set_stored_instrument(Some(piccolo.clone()));
513        assert_eq!(note.stored_instrument(), Some(&piccolo));
514        assert_eq!(note.instrument(), Some(&piccolo));
515        note.set_stored_instrument(None);
516        assert!(note.instrument().is_none());
517    }
518
519    #[test]
520    fn a_note_is_built_from_a_name_or_a_pitch_and_carries_beams_and_lyrics() {
521        use super::Note;
522        use crate::notation::{BeamType, Beams, Lyric};
523        use crate::pitch::Pitch;
524
525        let note = Note::try_from("Eb4".to_string()).unwrap();
526        assert_eq!(note.pitch_name(), "Eb");
527        let pitch = Pitch::from_name("G#3").unwrap();
528        let mut from_pitch = Note::from(&pitch);
529        assert_eq!(from_pitch.pitch_name(), "G#");
530        assert_eq!(from_pitch.notehead_fill(), None);
531        from_pitch.set_notehead_fill(Some(true));
532        assert_eq!(from_pitch.notehead_fill(), Some(true));
533
534        assert!(from_pitch.beams().is_empty());
535        from_pitch.beams_mut().append(BeamType::Start, None);
536        assert_eq!(from_pitch.beams().beams().len(), 1);
537        from_pitch.set_beams(Beams::default());
538        assert!(from_pitch.beams().is_empty());
539
540        from_pitch.lyrics_mut().push(Lyric::new("la"));
541        assert_eq!(from_pitch.lyrics().len(), 1);
542        assert_eq!(from_pitch.lyrics()[0].text(), "la");
543    }
544
545    #[test]
546    fn full_name_step_and_octave_match_music21() {
547        let flat = Note::from_name("Eb4").unwrap();
548        assert_eq!(flat.full_name(), "E-flat in octave 4 Note");
549        assert_eq!(flat.step(), 'E');
550        assert_eq!(flat.octave(), Some(4));
551        assert_eq!(flat.pitches()[0].name_with_octave(), "Eb4");
552        let dotted = Note::from_name("C#5")
553            .unwrap()
554            .with_duration(crate::Duration::new(1.5).unwrap());
555        assert_eq!(
556            dotted.full_name(),
557            "C-sharp in octave 5 Dotted Quarter Note"
558        );
559        let bare = Note::from_name("G").unwrap();
560        assert_eq!(bare.octave(), None);
561        assert_eq!(bare.full_name(), "G Note");
562    }
563    use super::{IntoNote, Note};
564    use crate::defaults::IntegerType;
565    use crate::pitch::Pitch;
566
567    #[test]
568    fn into_note_accepts_note_like_inputs() {
569        fn from_integer_pitch<T: IntoNote>() -> bool {
570            T::FROM_INTEGER_PITCH
571        }
572
573        assert!(!from_integer_pitch::<&str>());
574        assert!(from_integer_pitch::<IntegerType>());
575
576        let note = Note::from_name("C4").unwrap();
577        assert_eq!(
578            note.clone()
579                .try_into_note()
580                .unwrap()
581                .pitch_name_with_octave(),
582            "C4"
583        );
584
585        let borrowed_note = Note::from_name("D4").unwrap();
586        assert_eq!(
587            (&borrowed_note)
588                .try_into_note()
589                .unwrap()
590                .pitch_name_with_octave(),
591            "D4"
592        );
593
594        let pitch = Pitch::from_name("E4").unwrap();
595        assert_eq!(
596            pitch.try_into_note().unwrap().pitch_name_with_octave(),
597            "E4"
598        );
599
600        let borrowed_pitch = Pitch::from_name("F4").unwrap();
601        assert_eq!(
602            (&borrowed_pitch)
603                .try_into_note()
604                .unwrap()
605                .pitch_name_with_octave(),
606            "F4"
607        );
608
609        assert_eq!(
610            "G4".to_string()
611                .try_into_note()
612                .unwrap()
613                .pitch_name_with_octave(),
614            "G4"
615        );
616
617        let owned_name = "A4".to_string();
618        assert_eq!(
619            (&owned_name)
620                .try_into_note()
621                .unwrap()
622                .pitch_name_with_octave(),
623            "A4"
624        );
625
626        assert_eq!("B4".try_into_note().unwrap().pitch_name_with_octave(), "B4");
627
628        assert_eq!(
629            (60 as IntegerType)
630                .try_into_note()
631                .unwrap()
632                .pitch_name_with_octave(),
633            "C4"
634        );
635    }
636
637    #[test]
638    fn transposing_a_note_keeps_its_duration() {
639        let note = Note::from_name("C4")
640            .unwrap()
641            .with_duration(crate::Duration::half());
642        let moved = note
643            .transpose(&crate::Interval::from_name("M3").unwrap())
644            .unwrap();
645        assert_eq!(moved.pitch_name_with_octave(), "E4");
646        assert_eq!(moved.duration().unwrap().quarter_length(), 2.0);
647    }
648
649    #[test]
650    fn setting_a_pitch_keeps_the_duration_and_the_notation() {
651        let mut note = Note::from_name("C4")
652            .unwrap()
653            .with_duration(crate::Duration::half());
654        note.set_notehead(crate::Notehead::Diamond);
655        note.set_pitch(crate::Pitch::from_name("Eb5").unwrap());
656        assert_eq!(note.pitch_name_with_octave(), "Eb5");
657        assert_eq!(note.duration().unwrap().quarter_length(), 2.0);
658        assert_eq!(note.notehead(), crate::Notehead::Diamond);
659    }
660
661    #[test]
662    fn a_numbered_lyric_replaces_the_verse_that_has_that_number() {
663        let mut note = Note::from_name("C4").unwrap();
664        note.add_lyric("hello", None, false).unwrap();
665        note.add_lyric("bye", Some(3), false).unwrap();
666        assert_eq!(
667            note.lyrics()
668                .iter()
669                .map(|lyric| (lyric.number(), lyric.text()))
670                .collect::<Vec<_>>(),
671            [(1, "hello".to_string()), (3, "bye".to_string())]
672        );
673
674        // the same number again replaces the text, and leaves where the
675        // syllable falls in its word alone
676        note.add_lyric("ciao", Some(3), false).unwrap();
677        assert_eq!(note.lyrics().len(), 2);
678        assert_eq!(note.lyrics()[1].text(), "ciao");
679        assert_eq!(note.lyrics()[1].number(), 3);
680
681        // and `lyric` reads the syllables, not their hyphenated spellings
682        let mut hyphenated = Note::from_name("C4").unwrap();
683        hyphenated.set_lyric(Some("hel-")).unwrap();
684        assert_eq!(hyphenated.lyrics()[0].raw_text(), "hel-");
685        assert_eq!(hyphenated.lyric().as_deref(), Some("hel"));
686    }
687
688    #[test]
689    fn inserting_a_lyric_moves_the_verses_after_it_down() {
690        let mut note = Note::from_name("C4").unwrap();
691        note.add_lyric("second", None, false).unwrap();
692        note.insert_lyric("first", 0, false).unwrap();
693        note.insert_lyric("newSecond", 1, false).unwrap();
694        assert_eq!(
695            note.lyrics()
696                .iter()
697                .map(|lyric| (lyric.number(), lyric.text()))
698                .collect::<Vec<_>>(),
699            [
700                (1, "first".to_string()),
701                (2, "newSecond".to_string()),
702                (3, "second".to_string())
703            ]
704        );
705        // an index past the end appends, as inserting into a list does
706        note.insert_lyric("last", 99, false).unwrap();
707        assert_eq!(note.lyrics()[3].number(), 4);
708        assert_eq!(note.lyrics()[3].text(), "last");
709    }
710
711    #[test]
712    fn note_supports_rust_conversion_traits() {
713        let parsed: Note = "C#4".parse().unwrap();
714        assert_eq!(parsed.to_string(), "C#4");
715
716        let from_pitch = Note::from(Pitch::from_name("D4").unwrap());
717        assert_eq!(from_pitch.pitch_name_with_octave(), "D4");
718
719        let from_integer = Note::try_from(60 as IntegerType).unwrap();
720        assert_eq!(from_integer.pitch_name_with_octave(), "C4");
721    }
722}