public static async Task <ChordFinderResultSet> FindChordsAsync(IChordFinderOptions chordFinderOptions, CancellationToken cancelToken) { if (null == chordFinderOptions) { throw new ArgumentNullException(nameof(chordFinderOptions)); } InternalNote root = NoteUtils.ToInternalNote(chordFinderOptions.RootNote); IChordQuality chordQuality = chordFinderOptions.ChordQuality; InternalNote[] notesInChord = NamedInterval.GetNotes(root, chordQuality.Intervals); ChordFinderResultSet results = new ChordFinderResultSet(chordFinderOptions); if (cancelToken.IsCancellationRequested) { return(results); } await FindAllChordsAsync(results, null, notesInChord, 0, chordFinderOptions, cancelToken); return(results); }
private static async Task FindAllChordsAsync(ChordFinderResultSet results, NoteNode noteNode, InternalNote[] targetNotes, int str, IChordFinderOptions chordFinderOptions, CancellationToken cancelToken) { if (cancelToken.IsCancellationRequested) { return; } IInstrument instrument = chordFinderOptions.Instrument; if (str == instrument.NumStrings) // Build a chord result { int[] marks = new int[instrument.NumStrings]; NoteNode nn = noteNode; str--; bool[] hasNotes = new bool[targetNotes.Length]; // Walk back up the tree to set the marks on the result and flag each target note while (null != nn) { marks[str] = nn.Fret; for (int i = 0; i < targetNotes.Length; i++) { if (nn.Note == targetNotes[i]) { hasNotes[i] = true; } } nn = nn.Parent; str--; } InternalNote rootNote = NoteUtils.ToInternalNote(chordFinderOptions.RootNote); // Add result if it had all the target notes bool valid = true; for (int i = 0; i < hasNotes.Length; i++) { // Validate: // 1. All notes when !AllowRootlessChords // 2. Non-root notes only when AllowRootlessChords if (!chordFinderOptions.AllowRootlessChords || (chordFinderOptions.AllowRootlessChords && targetNotes[i] != rootNote)) { valid = valid && hasNotes[i]; } } if (valid) { results.AddResult(marks); } } else // Keep building the tree { // Look at the muted string if (chordFinderOptions.AllowMutedStrings) { NoteNode muted = new NoteNode { Parent = noteNode }; await FindAllChordsAsync(results, muted, targetNotes, str + 1, chordFinderOptions, cancelToken); } // Look at all the notes on the string int startingFret = chordFinderOptions.AllowOpenStrings ? 0 : 1; for (int fret = startingFret; fret <= chordFinderOptions.MaxFret; fret++) { InternalNote note = NoteUtils.Shift(chordFinderOptions.Tuning.RootNotes[str].InternalNote, fret); // See if the note is a target note for (int i = 0; i < targetNotes.Length; i++) { // If it's a target note add it and search on the next string if (note == targetNotes[i]) { NoteNode child = new NoteNode(fret, note, noteNode); await FindAllChordsAsync(results, child, targetNotes, str + 1, chordFinderOptions, cancelToken); break; } } } } }
internal ChordFinderResult(ChordFinderResultSet parent, int[] marks) { Parent = parent ?? throw new ArgumentNullException(nameof(parent)); Marks = marks ?? throw new ArgumentNullException(nameof(marks)); }