private static IDictionary <NoteName, int> GetExtensionNotes( Quality?quality, NoteName rootNoteName, int extensionIntervalNumber, IntervalQuality?extensionIntervalQuality) { var result = new Dictionary <NoteName, int>(); var intervalQualities = Enumerable .Range(0, extensionIntervalNumber + 1) .Select(i => i > 2 && Interval.IsPerfect(i) ? IntervalQuality.Perfect : IntervalQuality.Major) .ToArray(); if (extensionIntervalNumber >= 7) { if (extensionIntervalQuality == null && quality == null) { intervalQualities[7] = IntervalQuality.Minor; } else if (extensionIntervalQuality != null) { intervalQualities[7] = extensionIntervalQuality.Value; intervalQualities[extensionIntervalNumber] = extensionIntervalQuality.Value; } else { intervalQualities[7] = quality == Quality.HalfDiminished || quality == Quality.Dominant || quality == Quality.Augmented ? IntervalQuality.Minor : ChordToIntervalQualities[quality.Value]; } for (var i = 7; i <= extensionIntervalNumber; i += 2) { result.Add(rootNoteName.Transpose(Interval.Get(intervalQualities[i], i)), i); } } else { if (extensionIntervalQuality != null) { intervalQualities[extensionIntervalNumber] = extensionIntervalQuality.Value; } result.Add( rootNoteName.Transpose(Interval.Get(intervalQualities[extensionIntervalNumber], extensionIntervalNumber)), extensionIntervalNumber); } return(result); }
public void Transpose(NoteName noteName, int halfSteps, NoteName expectedNoteName) { var interval = Interval.FromHalfSteps(halfSteps); var actualNoteName = noteName.Transpose(interval); Assert.AreEqual(expectedNoteName, actualNoteName, "Transposed note name is invalid."); }
/// <summary> /// Initializes a new instance of the <see cref="Chord"/> with the specified root note name and /// intervals from root one. /// </summary> /// <param name="rootNoteName">The root note's name.</param> /// <param name="intervalsFromRoot">Intervals from root note.</param> /// <exception cref="InvalidEnumArgumentException"><paramref name="rootNoteName"/> specified an invalid value.</exception> /// <exception cref="ArgumentNullException"><paramref name="intervalsFromRoot"/> is <c>null</c>.</exception> public Chord(NoteName rootNoteName, IEnumerable <Interval> intervalsFromRoot) { ThrowIfArgument.IsInvalidEnumValue(nameof(rootNoteName), rootNoteName); ThrowIfArgument.IsNull(nameof(intervalsFromRoot), intervalsFromRoot); NotesNames = new[] { Interval.Zero }.Concat(intervalsFromRoot) .Where(i => i != null) .OrderBy(i => i.HalfSteps) .Select(i => rootNoteName.Transpose(i)) .ToArray(); }
public static NoteName[] GetChordNotesNames(NoteName rootNoteName, string chordCharacteristic, NoteName?bassNoteName) { var notesNames = new List <NoteName>(); if (bassNoteName != null) { notesNames.Add(bassNoteName.Value); } var definition = NamesDefinitions.FirstOrDefault(d => d.Names.Contains(chordCharacteristic.Replace(" ", string.Empty))); if (definition != null) { notesNames.AddRange(definition.Intervals.First().Select(i => rootNoteName.Transpose(Interval.FromHalfSteps(i)))); } return(notesNames.ToArray()); }
internal static ParsingResult TryParseChordName(Match match, NoteName rootNoteName, out Chord chord) { chord = null; // Quality?quality = null; var qualityGroup = match.Groups[ChordQualityGroupName]; if (qualityGroup.Success) { quality = GroupsQualities.FirstOrDefault(gq => match.Groups[gq.Key].Success).Value; } // var extensionIntervalNumber = -1; IntervalQuality?extensionIntervalQuality = null; var extensionGroup = match.Groups[ExtensionGroupName]; if (extensionGroup.Success && !string.IsNullOrWhiteSpace(extensionGroup.Value)) { if (match.Groups[ExtensionQualityGroupName].Success) { extensionIntervalQuality = GroupsExtensionQualities.FirstOrDefault(gq => match.Groups[gq.Key].Success).Value; } if (!ParsingUtilities.ParseInt(match, ExtensionNumberGroupName, -1, out extensionIntervalNumber) || extensionIntervalNumber < 5) { return(ParsingResult.Error(ExtensionNumberIsOutOfRange)); } } if (quality == Quality.HalfDiminished || quality == Quality.Dominant) { if (extensionIntervalNumber >= 0 && extensionIntervalNumber != 7) { return(ParsingResult.Error(HalfDiminishedOrDominantIsNotSeventh)); } if (extensionIntervalNumber < 0) { extensionIntervalNumber = 7; } } var extensionNotes = new List <NoteName>(); var extensionNumbers = new List <int>(); if (extensionIntervalNumber >= 0) { var extensionNotesDictionary = GetExtensionNotes(quality, rootNoteName, extensionIntervalNumber, extensionIntervalQuality); extensionNotes.AddRange(extensionNotesDictionary.Keys); extensionNumbers.AddRange(extensionNotesDictionary.Values); } if (quality == Quality.HalfDiminished) { quality = Quality.Diminished; } else if (quality == Quality.Dominant) { quality = Quality.Major; } if (quality == null) { quality = Quality.Major; } // var notes = new List <NoteName>(Chord.GetByTriad(rootNoteName, ChordQualities[quality.Value]).NotesNames); notes.AddRange(extensionNotes); extensionNumbers.InsertRange(0, new[] { 1, 3, 5 }); if (extensionIntervalNumber == 5) { notes.Clear(); notes.AddRange(new[] { rootNoteName, extensionNotes.First() }); extensionNumbers.Clear(); extensionNumbers.AddRange(new[] { 1, 5 }); } // var alteredToneGroup = match.Groups[AlteredToneGroupName]; if (alteredToneGroup.Success) { int alteredToneNumber; if (!ParsingUtilities.ParseInt(match, AlteredToneNumberGroupName, -1, out alteredToneNumber)) { return(ParsingResult.Error(AlteredToneNumberIsOutOfRange)); } var transposeBy = 0; var accidental = match.Groups[AlteredToneAccidentalGroupName].Value; switch (accidental) { case "#": case "+": transposeBy = 1; break; case "b": case "-": transposeBy = -1; break; } var maxExtensionNumber = extensionNumbers.Max(); if (maxExtensionNumber < alteredToneNumber) { var extensionNotesDictionary = GetExtensionNotes(quality, rootNoteName, alteredToneNumber, null) .Where(kv => kv.Value > maxExtensionNumber); notes.AddRange(extensionNotesDictionary.Select(kv => kv.Key)); extensionNumbers.AddRange(extensionNotesDictionary.Select(kv => kv.Value)); } var index = extensionNumbers.IndexOf(alteredToneNumber); if (index >= 0) { notes[index] = notes[index].Transpose(Interval.FromHalfSteps(transposeBy)); } } // var suspendedGroup = match.Groups[SuspendedGroupName]; if (suspendedGroup.Success) { int suspensionNumber; if (!ParsingUtilities.ParseInt(match, SuspendedNumberGroupName, -1, out suspensionNumber) || suspensionNumber != 2 && suspensionNumber != 4) { return(ParsingResult.Error(SuspensionNumberIsOutOfRange)); } var interval = suspensionNumber == 2 ? Interval.Get(IntervalQuality.Major, 2) : Interval.Get(IntervalQuality.Perfect, 4); notes[1] = rootNoteName.Transpose(interval); } // var addedToneGroup = match.Groups[AddedToneGroupName]; if (addedToneGroup.Success) { int addedToneNumber; if (!ParsingUtilities.ParseInt(match, AddedToneNumberGroupName, -1, out addedToneNumber)) { return(ParsingResult.Error(AddedToneNumberIsOutOfRange)); } var interval = Interval.IsPerfect(addedToneNumber) ? Interval.Get(IntervalQuality.Perfect, addedToneNumber) : Interval.Get(IntervalQuality.Major, addedToneNumber); notes.Add(rootNoteName.Transpose(interval)); } // var bassNoteNameGroup = match.Groups[BassNoteNameGroupName]; if (bassNoteNameGroup.Success) { NoteName bassNoteName; var bassNoteNameParsingResult = NoteNameParser.TryParse(bassNoteNameGroup.Value, out bassNoteName); if (bassNoteNameParsingResult.Status != ParsingStatus.Parsed) { return(bassNoteNameParsingResult); } notes.Insert(0, bassNoteName); } // chord = new Chord(notes); return(ParsingResult.Parsed); }