ktsu.Semantics.Music
5.12.1
Prefix Reserved
dotnet add package ktsu.Semantics.Music --version 5.12.1
NuGet\Install-Package ktsu.Semantics.Music -Version 5.12.1
<PackageReference Include="ktsu.Semantics.Music" Version="5.12.1" />
<PackageVersion Include="ktsu.Semantics.Music" Version="5.12.1" />
<PackageReference Include="ktsu.Semantics.Music" />
paket add ktsu.Semantics.Music --version 5.12.1
#r "nuget: ktsu.Semantics.Music, 5.12.1"
#:package ktsu.Semantics.Music@5.12.1
#addin nuget:?package=ktsu.Semantics.Music&version=5.12.1
#tool nuget:?package=ktsu.Semantics.Music&version=5.12.1
ktsu.Semantics.Music
Immutable, validated musical value types plus a harmonic and structural analysis layer, all pure logic with no I/O.
ktsu.Semantics.Music is one package in the ktsu.Semantics family. Start at the root README for the family overview.
Introduction
ktsu.Semantics.Music models music theory as immutable value types. Pitches, intervals, scales, modes, chords, keys, rational durations, and time signatures are all first-class, validate on creation, and return new instances on every operation. The pitch convention is MIDI 60 = C4, and equal temperament is anchored at A4 = 440 Hz.
Above the single-event types sits an analysis layer that models harmony nested inside structure. A Progression is an ordered chord sequence with bar-based harmonic rhythm that computes roman numerals, functional roles, cadences, an inferred key, and chromatic classifications. Sections group progressions, an Arrangement orders them into a piece, and Form names the structural pattern.
Features
- Pitch and interval types:
PitchClass,Pitch(MIDI, with name and frequency conversion),Interval(signed semitones, cents, folding). - Scales and modes:
Modewith roughly 29 presets (diatonic, jazz, symmetric, pentatonic, blues),Scalerooting a mode at a pitch class, withContainsandDegreeOf. - Chord-symbol parsing:
Chord.Parsehandles triads, sixths, sevenths (includingm7b5, the half-diminishedø7,mmaj7, and a flat fifth over a major third such as7b5andmaj7b5), extensions and altered tensions (9/11/13,b9/#9/#11/b13), suspensions, power chords, omissions (no3/no5), the six-nine idiom (C6/9), and slash bass. - Chord realization:
ChordTones()andVoice(octave)/Voice(octave, inversion), plusTranspose. - Roman-numeral analysis both directions:
Key.RomanNumeralOf(chord)andKey.ChordFromRomanNumeral(numeral). - Rhythm and real time: rational
Duration,TimeSignature,Tempo, andNote/Rest/ChordEventevents that convert to seconds. - Analysis layer:
Progression(roman numerals, functions, cadences, key inference, chromatic chords),Section,Arrangement, andFormwith named-form recognition.
Installation
Package Manager Console
Install-Package ktsu.Semantics.Music
.NET CLI
dotnet add package ktsu.Semantics.Music
Package Reference
<PackageReference Include="ktsu.Semantics.Music" Version="x.y.z" />
Usage Examples
Pitches, intervals, scales
using ktsu.Semantics.Music;
Pitch middleC = Pitch.Parse("C4"); // MIDI 60
Pitch cSharp4 = Pitch.Create(NoteLetter.C, Accidental.Sharp, octave: 4); // type-safe, no parsing
Pitch g4 = middleC.Transpose(7); // a perfect fifth up
Interval fifth = Interval.Between(middleC, g4); // +7 semitones
double hz = Pitch.Parse("A4").FrequencyHz; // 440.0
Scale dDorian = Scale.Create(PitchClass.Create(2), Mode.Dorian);
bool hasF = dDorian.Contains(PitchClass.Create(5)); // true
Chord parsing, voicing, and rhythm
using ktsu.Semantics.Music;
Chord cmaj7 = Chord.Parse("Cmaj7");
IReadOnlyList<Pitch> voicing = cmaj7.Voice(octave: 4); // C4, E4, G4, B4
IReadOnlyList<int> tones = Chord.Parse("Cmaj7#11").ChordTones();
Chord up = cmaj7.Transpose(2); // Dmaj7
Tempo tempo = Tempo.Create(120.0); // quarter = 120 bpm
double halfNoteSeconds = tempo.Seconds(Duration.Half); // 1.0 s
Note a4 = Note.Create(Pitch.Parse("A4"), Duration.Quarter, Velocity.Forte);
double noteSeconds = a4.Seconds(tempo); // 0.5 s
Chord symbols use one grammar for parsing and canonical formatting:
symbol = root body [ "/" bass ] EOF
root, bass = letter { accidental }
accidental = "#" | "b" | "♯" | "♭"
body = [ quality ] [ extension ] [ suspension ] { modifier }
quality = minor [ "aug" | "+" ] | "dim" | "°" | "ø" | "aug" | "+"
| majorWord
minor = "m" | "min" | "-"
majorWord = "maj" | "Maj" | "M" | "Δ"
extension = [ majorWord ] number | number "M" | "Δ"
| "6" [ "/9" | "9" ] | "b6" | "♭6" | "5"
number = "7" | "9" | "11" | "13"
suspension = "sus2" | "sus4" | "sus"
modifier = alteration | add | omit | "b6" | "♭6"
| "(" modifier { [ "," ] modifier } ")"
alteration = ("b" | "♭" | "-") ( "5" | "9" | "13" )
| ("#" | "♯" | "+") ( "5" | "9" | "11" )
add = "add" ( "9" | "11" | "13" | "6" | "b6" )
omit = "no3" | "no5"
The grammar consumes the complete symbol and does not permit whitespace. A numbered extension
implies a seventh and its natural lower stack tones unless an alteration replaces that degree;
addN adds only that tone. M/maj before an extension or an M suffix on its number marks a
major seventh. A small compatibility reader also accepts previously printed forms
such as C79, Cmaj79, Csus47, and C6add9; a bare + after a numbered chord remains an
augmentation mark. ToString() emits the canonical form, for example C13b9, C7sus4, C6/9,
and C(#11).
Roman numerals, both directions
using ktsu.Semantics.Music;
Key cMajor = Key.Create(PitchClass.Create(0), Mode.Major);
string label = cMajor.RomanNumeralOf(Chord.Parse("Dm7")); // "ii7"
Chord five = cMajor.ChordFromRomanNumeral("V7"); // G7
Analysis: progressions, cadences, key inference, and form
using ktsu.Semantics.Music;
// Chart style: a leading time signature, bars separated by "|", and a beat slash "/"
// extends the preceding chord by one beat (so "Cmaj7 / / /" is a whole bar of 4/4).
Progression prog = Progression.Parse("4/4 Dm7 / / / | G7 / / / | Cmaj7 / / / | Cmaj7 / / /");
Key key = prog.InferKey()!; // C major (quality-weighted fit)
IReadOnlyList<string> roman = prog.RomanNumerals(key); // ii7, V7, Imaj7, Imaj7
IReadOnlyList<HarmonicFunction> fns = prog.Functions(key); // Predominant, Dominant, Tonic, Tonic
IReadOnlyList<CadenceInstance> cadences = prog.Cadences(key); // Authentic at the resolution
// Chromatic analysis: secondary dominants, borrowed chords, Neapolitan
IReadOnlyList<ChromaticAnalysis> chromatic =
Progression.Parse("4/4 C / / / | A7 / / / | Dm / / / | G7 / / /").ChromaticChords(key); // A7 -> secondary dominant
// Structure: sections -> arrangement -> form
Progression verse = Progression.Parse("4/4 C / / / | G / / / | Am / / / | F / / /");
Progression bridge = Progression.Parse("4/4 F / / / | C / / / | G / / / | C / / /");
Arrangement song = Arrangement.Create(key,
[
Section.Create(SectionType.Verse, verse, "Verse 1"),
Section.Create(SectionType.Verse, verse, "Verse 2"),
Section.Create(SectionType.Bridge, bridge, "Bridge"),
Section.Create(SectionType.Verse, verse, "Verse 3"),
]);
Form form = song.Form; // Pattern "AABA", Name NamedForm.ThirtyTwoBarAABA
Type-safe construction and round-trippable text
using ktsu.Semantics.Music;
// Compiler-enforced construction — no strings to mistype
Pitch p = Pitch.Create(NoteLetter.C, Accidental.Sharp, octave: 4); // C#4
// Every in-scope type has a canonical ToString() and a Parse/TryParse that inverts it
Chord c = Chord.Parse("Cmaj7");
Chord same = Chord.Parse(c.ToString()); // c == same
// Progressions read like a lead sheet and round-trip through their chart form
Progression prog = Progression.Parse("4/4 Dm7 / G7 / | Cmaj7 / / /");
Progression reparsed = Progression.Parse(prog.ToString()); // prog == reparsed
// TryParse never throws; Parse throws FormatException on malformed text
bool ok = Interval.TryParse("-5", out Interval? descendingFourth);
API Reference
Core value types
| Type | Description | Key factories / members |
|---|---|---|
PitchClass |
One of twelve pitch classes, folded to 0..11. | Create(int), Create(NoteLetter, Accidental), Parse/TryParse, Value, Name |
Pitch |
MIDI pitch 0..127 (60 = C4). | Create(int), Create(NoteLetter, Accidental, int), Parse/TryParse, FromFrequency(double), Transpose(int), FrequencyHz, Octave |
Interval |
Signed interval in semitones. | Create(int), Between(Pitch, Pitch), Parse/TryParse, Semitones, Cents, Folded |
Mode |
Scale shape by semitone offsets. | Parse/TryParse, presets (Major, Dorian, HarmonicMinor, WholeTone, MajorPentatonic, ...), Intervals |
Scale |
A mode rooted at a pitch class. | Create(PitchClass, Mode), Parse/TryParse, Contains, DegreeOf, Transpose |
Chord |
Chord parsed from a symbol. | Parse/TryParse, ChordTones(), Voice(octave), Voice(octave, inversion), Transpose |
Key |
Tonic in a mode, resolves function. | Create(PitchClass, Mode), Parse/TryParse, RomanNumeralOf(Chord), ChordFromRomanNumeral(string), FunctionOf, Scale |
Duration |
Exact rational fraction of a whole note. | Create(int, int), Parse/TryParse, presets (Whole..Sixteenth), Dotted(), Add, Multiply, AsWholeNotes |
TimeSignature |
Bar and beat lengths. | Create(int, int), Parse/TryParse, BarDuration, BeatDuration |
Note / Rest / ChordEvent |
Timed musical events (IMusicalEvent). |
Create(...), Parse/TryParse, Duration, Seconds(Tempo) |
Velocity |
MIDI velocity 0..127. | Create(int), Parse/TryParse, presets (Piano..Fortissimo) |
Tempo |
Beats per minute with a beat unit. | Create(double), Parse/TryParse, Seconds(Duration), SecondsPerBeat |
Analysis layer
| Type | Description | Key members |
|---|---|---|
Progression |
Chord sequence with bar-based harmonic rhythm. | Parse(string), Create(...), RomanNumerals(Key), Functions(Key), Cadences(Key), InferKey(), InferKeys(), ChromaticChords(Key) |
Section |
Labeled structural unit. | Create(SectionType, Progression, label?, key?), Parse/TryParse, IsSameStructure(Section) |
Arrangement |
Sections in performance order. | Create(Key, IEnumerable<Section>), Parse/TryParse, Form, TotalBars |
Form |
Structural pattern and its name. | Of(Arrangement), Parse/TryParse, Pattern, Name |
HarmonicFunction |
enum: Tonic, Predominant, Dominant, Chromatic. | |
CadenceInstance |
A cadence at a resolution index. | Index, Type (Cadence enum: Authentic, Plagal, Half, Deceptive) |
ChromaticAnalysis |
A non-diatonic chord classification. | Index, Kind (ChromaticKind), Detail |
NamedForm |
enum: ThirtyTwoBarAABA, TwelveBarBlues, Binary, Ternary, Rondo, Strophic, ... |
InferKey() returns Key? (null for all-chromatic input), while InferKeys() returns the full ranked IReadOnlyList<KeyMatch>. See the complete library guide for a wider tour.
Contributing
Contributions are welcome! Feel free to open issues or submit pull requests.
License
This project is licensed under the MIT License. See the LICENSE.md file for details.
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net5.0 was computed. net5.0-windows was computed. net6.0 was computed. net6.0-android was computed. net6.0-ios was computed. net6.0-maccatalyst was computed. net6.0-macos was computed. net6.0-tvos was computed. net6.0-windows was computed. net7.0 was computed. net7.0-android was computed. net7.0-ios was computed. net7.0-maccatalyst was computed. net7.0-macos was computed. net7.0-tvos was computed. net7.0-windows was computed. net8.0 is compatible. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. net9.0 is compatible. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. net10.0 is compatible. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
| .NET Core | netcoreapp2.0 was computed. netcoreapp2.1 was computed. netcoreapp2.2 was computed. netcoreapp3.0 was computed. netcoreapp3.1 was computed. |
| .NET Standard | netstandard2.0 is compatible. netstandard2.1 is compatible. |
| .NET Framework | net461 was computed. net462 was computed. net463 was computed. net47 was computed. net471 was computed. net472 was computed. net48 was computed. net481 was computed. |
| MonoAndroid | monoandroid was computed. |
| MonoMac | monomac was computed. |
| MonoTouch | monotouch was computed. |
| Tizen | tizen40 was computed. tizen60 was computed. |
| Xamarin.iOS | xamarinios was computed. |
| Xamarin.Mac | xamarinmac was computed. |
| Xamarin.TVOS | xamarintvos was computed. |
| Xamarin.WatchOS | xamarinwatchos was computed. |
-
.NETStandard 2.0
- System.Memory (>= 4.6.3)
- System.Threading.Tasks.Extensions (>= 4.6.3)
-
.NETStandard 2.1
- No dependencies.
-
net10.0
- No dependencies.
-
net8.0
- No dependencies.
-
net9.0
- No dependencies.
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated |
|---|---|---|
| 5.12.1 | 305 | 10/12/2026 |
| 5.12.0 | 125 | 10/11/2026 |
| 5.11.6 | 105 | 10/10/2026 |
| 5.11.5 | 107 | 10/9/2026 |
| 5.11.4 | 103 | 10/9/2026 |
| 5.11.3 | 168 | 10/8/2026 |
| 5.11.2 | 134 | 10/6/2026 |
| 5.11.1 | 107 | 10/6/2026 |
| 5.11.1-pre.1 | 71 | 10/5/2026 |
| 5.11.0 | 150 | 10/2/2026 |
| 5.10.0 | 132 | 9/30/2026 |
| 5.9.0 | 123 | 9/29/2026 |
| 5.8.5 | 131 | 9/29/2026 |
| 5.8.4 | 120 | 9/29/2026 |
| 5.8.3 | 132 | 9/28/2026 |
| 5.8.2 | 138 | 9/28/2026 |
| 5.8.1 | 129 | 9/28/2026 |
| 5.8.0 | 138 | 9/25/2026 |
| 5.7.0 | 133 | 9/24/2026 |
| 5.6.0 | 127 | 9/23/2026 |
## v5.12.1 (patch)
Changes since v5.12.0:
- Name secondary dominant targets by the mode's own triad [patch] ([@Claude](https://github.com/Claude))
- Escape section labels so ToString round-trips through Parse [patch] ([@Claude](https://github.com/Claude))
- Stop Duration arithmetic from wrapping silently on int overflow [patch] ([@Claude](https://github.com/Claude))