< Summary

Information
Class: MusicTheory.Theory.Harmony.FourPartVoicing
Assembly: MusicTheory
File(s): /home/runner/work/MusicTheory/MusicTheory/Theory/Harmony/Voicing.cs
Line coverage
100%
Covered lines: 6
Uncovered lines: 0
Coverable lines: 6
Total lines: 65
Line coverage: 100%
Branch coverage
100%
Covered branches: 4
Total branches: 4
Branch coverage: 100%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
get_S()100%11100%
Notes()100%11100%
IsOrderedTopDown()100%44100%

File(s)

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

#LineLine coverage
 1using System;
 2using System.Collections.Generic;
 3using System.Linq;
 4
 5namespace MusicTheory.Theory.Harmony;
 6
 80657public readonly record struct FourPartVoicing(int S, int A, int T, int B)
 8{
 4049    public IEnumerable<(Voice voice, int midi)> Notes() => new[]
 40410    {
 40411        (Voice.Soprano, S), (Voice.Alto, A), (Voice.Tenor, T), (Voice.Bass, B)
 40412    };
 13
 19814    public bool IsOrderedTopDown() => S >= A && A >= T && T >= B;
 15}
 16
 17public static class VoiceLeadingRules
 18{
 19    public static bool HasSpacingViolations(FourPartVoicing v)
 20    {
 21        // Spacing: S-A and A-T <= octave; T-B no hard limit (common rule: <= octave too, but looser)
 22        return (v.S - v.A) > 12 || (v.A - v.T) > 12;
 23    }
 24
 25    public static bool HasRangeViolation(FourPartVoicing v)
 26    {
 27        foreach (var (voice, midi) in v.Notes())
 28        {
 29            var r = VoiceRanges.ForVoice(voice);
 30            if (!r.InWarnRange(midi)) return true; // hard out of warn range
 31        }
 32        return false;
 33    }
 34
 35    public static bool HasOverlap(FourPartVoicing prev, FourPartVoicing next)
 36    {
 37        // No voice overlapping: each voice must stay within its neighbors' previous positions.
 38        return next.S < prev.A || next.A > prev.S || next.A < prev.T || next.T > prev.A || next.T < prev.B || next.B > p
 39    }
 40
 41    public static bool HasParallelPerfects(FourPartVoicing a, FourPartVoicing b)
 42    {
 43        // Check pairs for P5/P8 parallels by motion direction and interval equality.
 44        var pairs = new (int a1, int a2, int b1, int b2)[]
 45        {
 46            (a.S, a.A, b.S, b.A), (a.S, a.T, b.S, b.T), (a.S, a.B, b.S, b.B),
 47            (a.A, a.T, b.A, b.T), (a.A, a.B, b.A, b.B),
 48            (a.T, a.B, b.T, b.B)
 49        };
 50
 51        foreach (var (x1,y1,x2,y2) in pairs)
 52        {
 53            var intv1 = Math.Abs((x1 - y1) % 12);
 54            var intv2 = Math.Abs((x2 - y2) % 12);
 55            // Treat only P8 and P5 as perfect for parallel checks
 56            bool isPerfect1 = (intv1 % 12) == 0 || (intv1 % 12) == 7;
 57            bool isPerfect2 = (intv2 % 12) == 0 || (intv2 % 12) == 7;
 58            if (!isPerfect1 || !isPerfect2) continue;
 59            int dir1 = Math.Sign(x2 - x1);
 60            int dir2 = Math.Sign(y2 - y1);
 61            if (dir1 == dir2 && dir1 != 0) return true;
 62        }
 63        return false;
 64    }
 65}

Methods/Properties

get_S()
Notes()
IsOrderedTopDown()