< Summary

Information
Class: MusicTheory.Theory.Harmony.LibraryWarmUp
Assembly: MusicTheory
File(s): /home/runner/work/MusicTheory/MusicTheory/Theory/Harmony/LibraryWarmUp.cs
Line coverage
93%
Covered lines: 55
Uncovered lines: 4
Coverable lines: 59
Total lines: 130
Line coverage: 93.2%
Branch coverage
57%
Covered branches: 8
Total branches: 14
Branch coverage: 57.1%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
Initialize()50%4492.15%
Pc()100%11100%
SameSet()60%1010100%

File(s)

/home/runner/work/MusicTheory/MusicTheory/Theory/Harmony/LibraryWarmUp.cs

#LineLine coverage
 1#pragma warning disable CA2255 // 'ModuleInitializer' is intended for application code or advanced source generators
 2using System.Runtime.CompilerServices;
 3
 4namespace MusicTheory.Theory.Harmony;
 5
 6/// <summary>
 7/// Lightweight warm-up for hot parsing/analyzer paths to reduce first-run jitter in tests/CLI/CI.
 8/// Performed once when the assembly loads; side-effect free.
 9/// </summary>
 10internal static class LibraryWarmUp
 11{
 12    [ModuleInitializer]
 13    public static void Initialize()
 14    {
 15        try
 16        {
 117            var key = new Key(60, true);
 18            // Exercise RomanInputParser common tokens (mixture, Neapolitan, secondaries, Aug6)
 119            RomanInputParser.Parse("bII; bIII; bⅢ; bVI; bVII; N6; bII6; V/ii; vii0/V; viiø/V; viio7/V; It6; Fr43; Ger65"
 20            // Touch ChordRomanizer helpers indirectly via HarmonyAnalyzer small triad
 121            new Chord((key.TonicMidi + 7) % 12, ChordQuality.DominantSeventh).PitchClasses().ToArray();
 22            // Additional targeted warm-ups for previously flaky first-run cases
 123            RomanInputParser.Parse("bIII", key);
 124            RomanInputParser.Parse("vii0/V", key);
 125            RomanInputParser.Parse("vii°7/V; vii°65/V; vii°43/V; vii°42/V", key);
 26
 27            // Dominant ninth on V (C major): G B D F A — both with and without fifth
 128            var vRoot = (key.ScaleDegreeMidi(4) % 12 + 12) % 12; // V root pc
 129            var v9full = new[] { vRoot, (vRoot + 4) % 12, (vRoot + 7) % 12, (vRoot + 10) % 12, (vRoot + 14) % 12 };
 130            var v9omit5 = new[] { vRoot, (vRoot + 4) % 12, (vRoot + 10) % 12, (vRoot + 14) % 12 };
 131            _ = HarmonyAnalyzer.AnalyzeTriad(v9full, key, (FourPartVoicing?)null, null);
 132            _ = HarmonyAnalyzer.AnalyzeTriad(v9omit5, key, (FourPartVoicing?)null, null);
 33
 34            // Targeted harmony analyzer warm-ups (secondary triad inversions + maj7 inversion option)
 35            // Secondary dominant triad V/ii inversions: A C# E
 2036            int Pc(int m) => ((m % 12) + 12) % 12;
 137            var v_over_ii = new[] { Pc(9), Pc(1), Pc(4) };
 38            // Root (A)
 139            _ = HarmonyAnalyzer.AnalyzeTriad(v_over_ii, key, new FourPartVoicing(81, 69, 57, 69), null);
 40            // 1st inv (C#)
 141            _ = HarmonyAnalyzer.AnalyzeTriad(v_over_ii, key, new FourPartVoicing(85, 73, 69, 61), null);
 42            // 2nd inv (E)
 143            _ = HarmonyAnalyzer.AnalyzeTriad(v_over_ii, key, new FourPartVoicing(88, 76, 72, 64), null);
 44
 45            // Secondary leading-tone triad vii°/V inversions: F# A C
 146            var viio_over_V = new[] { Pc(6), Pc(9), Pc(0) };
 47            // Root (F#)
 148            _ = HarmonyAnalyzer.AnalyzeTriad(viio_over_V, key, new FourPartVoicing(78, 74, 66, 66), null);
 49            // 1st inv (A)
 150            _ = HarmonyAnalyzer.AnalyzeTriad(viio_over_V, key, new FourPartVoicing(81, 76, 69, 69), null);
 51            // 2nd inv (C)
 152            _ = HarmonyAnalyzer.AnalyzeTriad(viio_over_V, key, new FourPartVoicing(84, 72, 72, 60), null);
 53
 54            // Secondary leading-tone seventh vii°7/V inversions: F# A C Eb
 155            var vii7_over_V = new[] { Pc(6), Pc(9), Pc(0), Pc(3) };
 56            // Root (F#)
 157            _ = HarmonyAnalyzer.AnalyzeTriad(vii7_over_V, key, new FourPartVoicing(87, 81, 72, 66), null);
 58            // 1st inv (A)
 159            _ = HarmonyAnalyzer.AnalyzeTriad(vii7_over_V, key, new FourPartVoicing(89, 84, 76, 69), null);
 60            // 2nd inv (C)
 161            _ = HarmonyAnalyzer.AnalyzeTriad(vii7_over_V, key, new FourPartVoicing(89, 84, 76, 60), null);
 62            // 3rd inv (Eb)
 163            _ = HarmonyAnalyzer.AnalyzeTriad(vii7_over_V, key, new FourPartVoicing(89, 84, 77, 63), null);
 64
 65            // Secondary dominant triad V/vi inversions: E G# B
 166            var v_over_vi = new[] { Pc(4), Pc(8), Pc(11) };
 67            // Root (E)
 168            _ = HarmonyAnalyzer.AnalyzeTriad(v_over_vi, key, new FourPartVoicing(88, 83, 80, 64), null);
 69            // 1st inv (G#)
 170            _ = HarmonyAnalyzer.AnalyzeTriad(v_over_vi, key, new FourPartVoicing(92, 83, 76, 68), null);
 71            // 2nd inv (B)
 172            _ = HarmonyAnalyzer.AnalyzeTriad(v_over_vi, key, new FourPartVoicing(88, 80, 76, 71), null);
 73
 74            // Secondary dominant triad V/vii inversions: F# A# C#
 175            var v_over_vii = new[] { Pc(6), Pc(10), Pc(1) };
 76            // Root (F#)
 177            _ = HarmonyAnalyzer.AnalyzeTriad(v_over_vii, key, new FourPartVoicing(78, 66, 54, 66), null);
 78            // 1st inv (A#)
 179            _ = HarmonyAnalyzer.AnalyzeTriad(v_over_vii, key, new FourPartVoicing(82, 70, 58, 70), null);
 80            // 2nd inv (C#)
 181            _ = HarmonyAnalyzer.AnalyzeTriad(v_over_vii, key, new FourPartVoicing(85, 73, 61, 61), null);
 82
 83            // Minor IIImaj7 first inversion with IncludeMajInSeventhInversions option
 184            var keyMin = new Key(60, false);
 185            var pcsIIImaj7 = new[] { Pc(63), Pc(67), Pc(70), Pc(74) }; // Eb G Bb D
 186            var optsMajInv = new HarmonyOptions { IncludeMajInSeventhInversions = true };
 187            _ = HarmonyAnalyzer.AnalyzeTriad(pcsIIImaj7, keyMin, optsMajInv, new FourPartVoicing(91, 86, 79, 67), null);
 88
 89            // Best-effort self-checks: if canonical tokens don't match expected PC sets on cold start,
 90            // re-invoke parsing once to ensure JIT/warm state is complete.
 91            static bool SameSet(int[] a, int[] b)
 92            {
 293                if (a is null || b is null) return false;
 294                if (a.Length != b.Length) return false;
 295                var aa = (int[])a.Clone();
 296                var bb = (int[])b.Clone();
 497                Array.Sort(aa); Array.Sort(bb);
 2598                for (int i = 0; i < aa.Length; i++) if (aa[i] != bb[i]) return false;
 299                return true;
 100            }
 101
 1102            int tonic = ((key.TonicMidi % 12) + 12) % 12;
 103
 104            // bIII should be a major triad on lowered mediant
 1105            var gotBIII = RomanInputParser.Parse("bIII", key)[0].Pcs;
 1106            var expBIII = new Chord((tonic + 3) % 12, ChordQuality.Major).PitchClasses().ToArray();
 1107            if (!SameSet(gotBIII, expBIII))
 108            {
 0109                RomanInputParser.Parse("bIII", key);
 110            }
 111
 112            // viiø/V should be half-diminished seventh on leading tone to the dominant
 1113            int targetV = ((key.ScaleDegreeMidi(4) % 12) + 12) % 12; // V
 1114            int ltToV = (targetV + 11) % 12;
 1115            var expViiSec = new Chord(ltToV, ChordQuality.HalfDiminishedSeventh).PitchClasses().ToArray();
 1116            var gotViiSec = RomanInputParser.Parse("viiø/V", key)[0].Pcs;
 1117            if (!SameSet(gotViiSec, expViiSec))
 118            {
 0119                RomanInputParser.Parse("viiø/V", key);
 120            }
 1121        }
 0122        catch
 123        {
 124            // best-effort only
 0125        }
 1126    }
 127}
 128
 129#pragma warning restore CA2255
 130

Methods/Properties

Initialize()
Pc()
SameSet()