< Summary

Information
Class: MusicTheory.Theory.Scale.PcScaleLibrary
Assembly: MusicTheory
File(s): /home/runner/work/MusicTheory/MusicTheory/Theory/Scale/Scale.cs
Line coverage
100%
Covered lines: 37
Uncovered lines: 0
Coverable lines: 37
Total lines: 157
Line coverage: 100%
Branch coverage
N/A
Covered branches: 0
Total branches: 0
Branch coverage: N/A
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
get_Major()100%11100%
get_Minor()100%11100%
get_ChurchModes()100%11100%
get_Chromatic()100%11100%
get_WholeTone()100%11100%
get_PentatonicMajor()100%11100%
get_PentatonicMinor()100%11100%
get_Blues()100%11100%
.cctor()100%11100%
FindScalesContaining(...)100%11100%

File(s)

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

#LineLine coverage
 1using System;
 2using System.Linq;
 3using System.Collections.Generic;
 4using MusicTheory.Theory.Pitch; // PitchClass / PitchUtils / SpelledPitch
 5
 6namespace MusicTheory.Theory.Scale
 7{
 8    public interface IScale
 9    {
 10        string Name { get; }
 11        IReadOnlyList<int> GetSemitoneSet();
 12        bool ContainsSemitone(int semitone) => GetSemitoneSet().Contains(semitone % 12);
 13    }
 14
 15    public static class KeySignatureInference
 16    {
 17        // Major root (0=C) → sharps (負=flats)
 18        private static readonly Dictionary<int,int> MajorSharps = new(){ {0,0},{7,1},{2,2},{9,3},{4,4},{11,5},{6,6},{1,7
 19        private static readonly int[] Ionian = {0,2,4,5,7,9,11};
 20        private static readonly int[] Aeolian = {0,2,3,5,7,8,10};
 21        private static readonly (string name,int[] pattern,int relShiftToIonian)[] ModePatterns = new[]{
 22            ("Ionian", new[]{0,2,4,5,7,9,11}, 0),
 23            ("Dorian", new[]{0,2,3,5,7,9,10}, -2), // Dorian root +10 = Ionian root (2 半音下)
 24            ("Phrygian", new[]{0,1,3,5,7,8,10}, -4),
 25            ("Lydian", new[]{0,2,4,6,7,9,11}, +5),
 26            ("Mixolydian", new[]{0,2,4,5,7,9,10}, -7),
 27            ("Aeolian", new[]{0,2,3,5,7,8,10}, -9),
 28            ("Locrian", new[]{0,1,3,5,6,8,10}, -11)
 29        };
 30        public static int? InferKeySignature(IScale scale)
 31        {
 32            var set = scale.GetSemitoneSet().Select(x=>x%12).Distinct().OrderBy(x=>x).ToArray();
 33            if (set.Length!=7) return null; // 対象外
 34            for(int root=0; root<12; root++)
 35            {
 36                var rotated = set.Select(s=>(s-root+12)%12).OrderBy(x=>x).ToArray();
 37                foreach (var mode in ModePatterns)
 38                {
 39                    if (rotated.SequenceEqual(mode.pattern))
 40                    {
 41                        // Ionian (major) の root を求める (mode.relShiftToIonian は modeRoot から IonianRoot への半音差)
 42                        int majorRoot = (root + (12 + mode.relShiftToIonian)) % 12;
 43                        if (MajorSharps.TryGetValue(majorRoot, out var val)) return val;
 44                    }
 45                }
 46            }
 47            return null;
 48        }
 49    }
 50    /// <summary>
 51    /// 拍子(メトリック・シグネチャ)を表現するクラス
 52    /// </summary>
 53    public class TimeSignature
 54    {
 55        /// <summary>
 56        /// 分子(拍数、例:4/4拍子なら4)
 57        /// </summary>
 58        public int Numerator { get; set; }
 59
 60        /// <summary>
 61        /// 分母(1拍の音価、例:4/4拍子なら4は四分音符)
 62        /// </summary>
 63        public int Denominator { get; set; }
 64
 65        public TimeSignature(int numerator, int denominator)
 66        {
 67            Numerator = numerator;
 68            Denominator = denominator;
 69        }
 70    }
 71
 72    /// <summary>
 73    /// ピッチクラスのみで構成されるスケール(和声音集合)
 74    /// 他ファイルの ModalScale との衝突回避のため PcScale と命名
 75    /// </summary>
 76    public readonly record struct PcScale(string Name, PitchClass[] Degrees) : IScale
 77    {
 78        public bool Contains(PitchClass pc) => Degrees.Any(d => d.Pc == pc.Pc);
 79        public override string ToString() => $"{Name}: [{string.Join(", ", Degrees.Select(d => d.ToString()))}]";
 80
 81        public PcScale Transposed(int rootPc)
 82        {
 83            var transposed = Degrees.Select(d => new PitchClass((d.Pc + rootPc) % 12)).ToArray();
 84            return new PcScale($"{Name} (Root: {rootPc})", transposed);
 85        }
 86
 87        public string[] GetJapaneseDegreeNames()
 88        {
 89            return PcScaleLibrary.JapaneseDegreeNames;
 90        }
 91
 92        public IEnumerable<(SpelledPitch, string)> GetSpelledDegreesWithNames(SpelledPitch root)
 93        {
 94            var rootPc = PitchUtils.ToPc(root).Pc;
 95            var transposed = Degrees.Select(d => new PitchClass((d.Pc + rootPc) % 12)).ToArray();
 96            for (int i = 0; i < transposed.Length; i++)
 97            {
 98                var spelled = PitchUtils.GetEnharmonicSpellings(new PitchClass(transposed[i].Pc)).FirstOrDefault();
 99                var name = PcScaleLibrary.JapaneseDegreeNames[i];
 100                yield return (spelled, name);
 101            }
 102        }
 103
 104    public IReadOnlyList<int> GetSemitoneSet() => Degrees.Select(d => d.Pc).ToArray();
 105    }
 106
 107    public static class PcScaleLibrary
 108    {
 10109        public static PcScale Major => new("Major", new[] { new PitchClass(0), new PitchClass(2), new PitchClass(4), new
 3110        public static PcScale Minor => new("Minor", new[] { new PitchClass(9), new PitchClass(11), new PitchClass(0), ne
 111
 2112        public static PcScale[] ChurchModes => new[]
 2113        {
 2114            new PcScale("Ionian", new[] { new PitchClass(0), new PitchClass(2), new PitchClass(4), new PitchClass(5), ne
 2115            new PcScale("Dorian", new[] { new PitchClass(0), new PitchClass(2), new PitchClass(3), new PitchClass(5), ne
 2116            new PcScale("Phrygian", new[] { new PitchClass(0), new PitchClass(1), new PitchClass(3), new PitchClass(5), 
 2117            new PcScale("Lydian", new[] { new PitchClass(0), new PitchClass(2), new PitchClass(4), new PitchClass(6), ne
 2118            new PcScale("Mixolydian", new[] { new PitchClass(0), new PitchClass(2), new PitchClass(4), new PitchClass(5)
 2119            new PcScale("Aeolian", new[] { new PitchClass(0), new PitchClass(2), new PitchClass(3), new PitchClass(5), n
 2120            new PcScale("Locrian", new[] { new PitchClass(0), new PitchClass(1), new PitchClass(3), new PitchClass(5), n
 2121        };
 122
 26123        public static PcScale Chromatic => new("Chromatic", Enumerable.Range(0, 12).Select(i => new PitchClass(i)).ToArr
 2124        public static PcScale WholeTone => new("Whole Tone", new[] { new PitchClass(0), new PitchClass(2), new PitchClas
 2125        public static PcScale PentatonicMajor => new("Pentatonic Major", new[] { new PitchClass(0), new PitchClass(2), n
 2126        public static PcScale PentatonicMinor => new("Pentatonic Minor", new[] { new PitchClass(0), new PitchClass(3), n
 2127        public static PcScale Blues => new("Blues", new[] { new PitchClass(0), new PitchClass(3), new PitchClass(5), new
 128
 1129        public static readonly string[] JapaneseDegreeNames = new[]
 1130        {
 1131            "主音",
 1132            "上主音",
 1133            "中音",
 1134            "下属音",
 1135            "属音",
 1136            "下中音",
 1137            "導音"
 1138        };
 139
 140        public static IEnumerable<PcScale> FindScalesContaining(PitchClass pc)
 141        {
 1142            return new[]
 1143            {
 1144                Major,
 1145                Minor,
 1146                Chromatic,
 1147                WholeTone,
 1148                PentatonicMajor,
 1149                PentatonicMinor,
 1150                Blues
 15151            }.Concat(ChurchModes).Where(s => s.Contains(pc));
 152        }
 153    }
 154
 155    // ModalScale / ExtendedScales は別ファイルへ分離
 156
 157}