public void Properties() { ChordPattern cp = new ChordPattern("a", "b", new int[] { 0, 1, 2, 3 }, new int[] { 0, 1, 2, 3 }); Assert.AreEqual(cp.Name, "a"); Assert.AreEqual(cp.Abbreviation, "b"); Assert.AreEqual(cp.Ascent.Length, 4); Assert.AreEqual(cp.LetterOffsets.Length, 4); }
/// <summary> /// Constructs a chord from its root note, pattern, and inversion. /// </summary> /// <param name="root">The root note of the chord.</param> /// <param name="pattern">The chord pattern.</param> /// <param name="inversion">The inversion, in [0..N-1] where N is the number of notes /// in pattern.</param> /// <exception cref="ArgumentNullException">pattern is null.</exception> /// <exception cref="ArgumentOutOfRangeException">inversion is out of range.</exception> public Chord(Note root, ChordPattern pattern, int inversion) { if (pattern == null) { throw new ArgumentNullException(); } if (inversion < 0 || inversion >= pattern.Ascent.Length) { throw new ArgumentOutOfRangeException("inversion out of range."); } this.root = root; this.pattern = pattern; this.inversion = inversion; this.positionInOctaveToContains = new bool[12]; Note[] uninvertedSequence = new Note[pattern.Ascent.Length]; Build(root, pattern, this.positionInOctaveToContains, uninvertedSequence); this.noteSequence = new Note[pattern.Ascent.Length]; RotateArrayLeft(uninvertedSequence, this.noteSequence, inversion); }
private static void Build(Note root, ChordPattern pattern, bool[] positionInOctaveToContains, Note[] noteSequence) { for (int i = 0; i < 12; ++i) { positionInOctaveToContains[i] = false; } Pitch rootPitch = root.PitchInOctave(0); for (int i = 0; i < pattern.Ascent.Length; ++i) { Pitch pitch = rootPitch + pattern.Ascent[i]; char letter = (char)(pattern.LetterOffsets[i] + (int)(root.Letter)); while (letter > 'G') { letter = (char)((int)letter - 7); } noteSequence[i] = pitch.NoteWithLetter(letter); positionInOctaveToContains[pitch.PositionInOctave()] = true; } }
/// <summary> /// Value equality. /// </summary> public override bool Equals(System.Object obj) { ChordPattern other = obj as ChordPattern; if ((Object)other == null) { return(false); } if (!this.name.Equals(other.name)) { return(false); } if (!this.abbreviation.Equals(other.abbreviation)) { return(false); } if (this.ascent.Length != other.ascent.Length) { return(false); } for (int i = 0; i < this.ascent.Length; ++i) { if (this.ascent[i] != other.ascent[i]) { return(false); } } if (this.letterOffsets.Length != other.letterOffsets.Length) { return(false); } for (int i = 0; i < this.letterOffsets.Length; ++i) { if (this.letterOffsets[i] != other.letterOffsets[i]) { return(false); } } return(true); }
/// <summary> /// Constructs a chord from a string. /// </summary> /// <param name="name">The name to parse. This is the same format as the Name property: /// a letter in ['A'..'G'], an optional series of accidentals (#'s or b's), then an /// optional inversion specified as a '/' followed by another note name. If the /// inversion is present it must be one of the notes in the chord.</param> /// <exception cref="ArgumentNullException">name is null.</exception> /// <exception cref="ArgumentException">cannot parse a chord from name.</exception> public Chord(string name) { if (name == null) { throw new ArgumentNullException("name is null."); } if (name.Length == 0) { throw new ArgumentException("name is empty."); } int pos = 0; this.root = Note.ParseNote(name, ref pos); this.pattern = null; foreach (ChordPattern p in Chord.Patterns) { if (pos + p.Abbreviation.Length > name.Length) { continue; } if (String.Compare(name, pos, p.Abbreviation, 0, p.Abbreviation.Length) != 0) { continue; } if (pos + p.Abbreviation.Length == name.Length || name[pos+p.Abbreviation.Length] == '/') { pos += p.Abbreviation.Length; this.pattern = p; break; } } if (this.pattern == null) { throw new ArgumentException("name does not match a known chord pattern."); } // At this point, we know the note and pattern (but not yet the inversion). Build // the chord prior to inversion. this.positionInOctaveToContains = new bool[12]; Note[] uninvertedSequence = new Note[pattern.Ascent.Length]; Build(root, pattern, this.positionInOctaveToContains, uninvertedSequence); this.noteSequence = new Note[pattern.Ascent.Length]; // Now see if there's an inversion. this.inversion = 0; if (pos < name.Length) { if (name[pos] != '/') { throw new ArgumentException(String.Format("unexpected character '{0}' in name.", name[pos])); } pos++; Note bass = Note.ParseNote(name, ref pos); if (name.Length > pos) { throw new ArgumentException(String.Format("unexpected character '{0}' in name.", name[pos])); } this.inversion = Array.IndexOf(uninvertedSequence, bass); if (inversion == -1) { throw new ArgumentException("invalid bass note for inversion."); } } RotateArrayLeft(uninvertedSequence, this.noteSequence, inversion); }