< Summary

Information
Class: MusicTheory.Theory.Chord.ChordFormula
Assembly: MusicTheory
File(s): /home/runner/work/MusicTheory/MusicTheory/Theory/Chord/Chord.cs
Line coverage
64%
Covered lines: 11
Uncovered lines: 6
Coverable lines: 17
Total lines: 128
Line coverage: 64.7%
Branch coverage
25%
Covered branches: 1
Total branches: 4
Branch coverage: 25%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
get_Name()100%210%
get_Symbol()100%11100%
get_CoreIntervals()100%11100%
get_Tensions()100%11100%
get_Aliases()100%11100%
.ctor(...)50%22100%
GetDisplayName()0%620%
.cctor()100%210%

File(s)

/home/runner/work/MusicTheory/MusicTheory/Theory/Chord/Chord.cs

#LineLine coverage
 1using System;
 2using System.Linq;
 3using System.Collections.Generic;
 4using MusicTheory.Theory.Pitch;
 5using MTInterval = MusicTheory.Theory.Interval.FunctionalInterval;
 6using MusicTheory.Theory.Interval;
 7
 8namespace MusicTheory.Theory.Chord
 9{
 10    // Interval 型は Interval namespace へ移動
 11
 12    public class ChordFormula
 13    {
 014        public string Name { get; }
 20715        public string Symbol { get; }
 21416    public MTInterval[] CoreIntervals { get; }
 23017    public MTInterval[] Tensions { get; }
 3918        public string[] Aliases { get; }
 19
 1320    public ChordFormula(string name, string symbol, MTInterval[] coreIntervals, MTInterval[] tensions, params string[] a
 21        {
 1322            Name = name;
 1323            Symbol = symbol;
 1324            CoreIntervals = coreIntervals;
 1325            Tensions = tensions;
 1326            Aliases = aliases ?? Array.Empty<string>();
 1327        }
 28
 29    public string GetDisplayName()
 30        {
 031            var tensionPart = Tensions.Any() ? "(" + string.Join(",", Tensions.Select(t => t.DisplayName)) + ")" : strin
 032            return Symbol + tensionPart;
 33        }
 34
 35        // 代表的コード(最低限)
 036    public static readonly ChordFormula Maj7 = new("Maj7", "maj7", new[] { new MTInterval(IntervalType.MajorThird), new 
 037    public static readonly ChordFormula Min7 = new("Min7", "m7", new[] { new MTInterval(IntervalType.MinorThird), new MT
 038    public static readonly ChordFormula Dom7 = new("Dom7", "7", new[] { new MTInterval(IntervalType.MajorThird), new MTI
 39    }
 40    public class ChordName
 41    {
 42        public string Root { get; }
 43        public ChordFormula Formula { get; }
 44
 45        public ChordName(string root, ChordFormula formula)
 46        {
 47            Root = root;
 48            Formula = formula;
 49        }
 50
 51    public override string ToString() => $"{Root}{Formula.Symbol}";
 52    }
 53
 54    public static class ChordFormulas
 55    {
 56        private static readonly ChordFormula[] formulas = new[]
 57        {
 58            new ChordFormula("Major Triad", "", new[] { I(IntervalType.MajorThird), I(IntervalType.PerfectFifth) }, Arra
 59            new ChordFormula("Minor Triad", "m", new[] { I(IntervalType.MinorThird), I(IntervalType.PerfectFifth) }, Arr
 60            new ChordFormula("Diminished Triad", "dim", new[] { I(IntervalType.MinorThird), I(IntervalType.Tritone) }, A
 61            new ChordFormula("Augmented Triad", "aug", new[] { I(IntervalType.MajorThird), I(IntervalType.MinorSixth) },
 62            new ChordFormula("Major 7", "maj7", new[] { I(IntervalType.MajorThird), I(IntervalType.PerfectFifth), I(Inte
 63            new ChordFormula("Dominant 7", "7", new[] { I(IntervalType.MajorThird), I(IntervalType.PerfectFifth), I(Inte
 64            new ChordFormula("Minor 7", "m7", new[] { I(IntervalType.MinorThird), I(IntervalType.PerfectFifth), I(Interv
 65            new ChordFormula("Half Diminished", "m7b5", new[] { I(IntervalType.MinorThird), I(IntervalType.Tritone), I(I
 66            new ChordFormula("Diminished 7", "dim7", new[] { I(IntervalType.MinorThird), I(IntervalType.Tritone), I(Inte
 67            new ChordFormula("Dominant 9", "9", new[] { I(IntervalType.MajorThird), I(IntervalType.PerfectFifth), I(Inte
 68            new ChordFormula("Dominant 13", "13", new[] { I(IntervalType.MajorThird), I(IntervalType.PerfectFifth), I(In
 69            new ChordFormula("Altered Dom7 (b9 #9 #11 b13)", "7alt", new[] { I(IntervalType.MajorThird), I(IntervalType.
 70            new ChordFormula("Major 13", "maj13", new[] { I(IntervalType.MajorThird), I(IntervalType.PerfectFifth), I(In
 71        };
 72
 73    private static MTInterval I(IntervalType t) => new MTInterval(t);
 74
 75        public static IEnumerable<ChordFormula> All => formulas;
 76
 77        public static IEnumerable<ChordFormula> MatchByNotes(IEnumerable<int> inputNotes)
 78        {
 79            foreach (var formula in formulas)
 80            {
 81                var semitoneSet = formula.CoreIntervals.Concat(formula.Tensions).Select(i => i.Semitones).ToHashSet();
 82                if (inputNotes.All(note => semitoneSet.Contains(note)))
 83                    yield return formula;
 84            }
 85        }
 86
 87        public static IEnumerable<ChordName> GenerateChordNames(string root)
 88        {
 89            foreach (var formula in formulas)
 90            {
 91                yield return new ChordName(root, formula);
 92            }
 93        }
 94
 95        public static ChordName? ParseChordName(string chordText)
 96        {
 97            if (string.IsNullOrWhiteSpace(chordText)) return null;
 98            // 最長一致(シンボルが空の場合は triad などの判定として fallback)
 99            ChordName? best = null;
 100            int bestLen = -1;
 101            // シンボル長で降順ソートして衝突回避 (maj7 vs m7 など)
 102            var ordered = formulas.Select(f => new { Formula = f, Symbols = new[] { f.Symbol }.Concat(f.Aliases).Where(s
 103            foreach (var entry in ordered)
 104            {
 105                foreach (var symbol in entry.Symbols)
 106                {
 107                    if (chordText.EndsWith(symbol, StringComparison.Ordinal))
 108                    {
 109                        var root = chordText[..^symbol.Length];
 110                        if (root.Length > 0 && symbol.Length > bestLen)
 111                        {
 112                            best = new ChordName(root, entry.Formula);
 113                            bestLen = symbol.Length;
 114                            break; // 長いシンボル優先で決定
 115                        }
 116                    }
 117                }
 118            }
 119            // triad 等シンボル空の処理: 既に best があれば返す。なければ root 全体を root とみなし、空シンボルを持つ最初の formula を当てる。
 120            if (best != null) return best;
 121            var emptySymbolFormula = formulas.FirstOrDefault(f => string.IsNullOrEmpty(f.Symbol));
 122            if (emptySymbolFormula != null)
 123                return new ChordName(chordText, emptySymbolFormula);
 124            return null;
 125        }
 126    }
 127
 128}