Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
286 changes: 78 additions & 208 deletions README.md

Large diffs are not rendered by default.

160 changes: 160 additions & 0 deletions Semantics.Color/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,160 @@
# ktsu.Semantics.Color

> A physically-grounded color type with linear-RGB math, perceptual Oklab operations, and built-in WCAG accessibility checks.

[![License](https://img.shields.io/github/license/ktsu-dev/Semantics.svg?label=License&logo=nuget)](../LICENSE.md)
[![NuGet Version](https://img.shields.io/nuget/v/ktsu.Semantics.Color?label=Stable&logo=nuget)](https://nuget.org/packages/ktsu.Semantics.Color)
[![NuGet Version](https://img.shields.io/nuget/vpre/ktsu.Semantics.Color?label=Latest&logo=nuget)](https://nuget.org/packages/ktsu.Semantics.Color)
[![NuGet Downloads](https://img.shields.io/nuget/dt/ktsu.Semantics.Color?label=Downloads&logo=nuget)](https://nuget.org/packages/ktsu.Semantics.Color)
[![GitHub commit activity](https://img.shields.io/github/commit-activity/m/ktsu-dev/Semantics?label=Commits&logo=github)](https://github.com/ktsu-dev/Semantics/commits/main)
[![GitHub contributors](https://img.shields.io/github/contributors/ktsu-dev/Semantics?label=Contributors&logo=github)](https://github.com/ktsu-dev/Semantics/graphs/contributors)
[![GitHub Actions Workflow Status](https://img.shields.io/github/actions/workflow/status/ktsu-dev/Semantics/dotnet.yml?label=Build&logo=github)](https://github.com/ktsu-dev/Semantics/actions)

`ktsu.Semantics.Color` is one package in the [ktsu.Semantics](../README.md) family. Start at the [root README](../README.md) for the family overview.

## Introduction

`ktsu.Semantics.Color` treats color the way rendering and color science do: the canonical `Color` type stores linear (non gamma-encoded) RGB plus straight alpha, each channel in 0..1, and all math happens in linear space. Gamma-encoded sRGB is a separate type (`Srgb`) that you cross into only when you convert. That distinction is the difference between correct blending, mixing, and luminance and the subtly-wrong results you get from doing arithmetic on gamma-encoded values.

On top of that foundation the package adds perceptual operations in the Oklab color space, hex and byte interop, `System.Numerics` vector output, and WCAG contrast tooling that can rate a color pair and automatically nudge a color until it meets a target conformance level.

## Features

- **Linear RGB as the hub**: `Color` stores linear RGBA in 0..1, and all mixing, interpolation, and luminance math happens there.
- **Color spaces**: sRGB (`Srgb`), HSL (`Hsl`), HSV (`Hsv`), Oklab (`Oklab`), and Oklch (`Oklch`), each with `From*` / `To*` conversions on `Color`.
- **Interop**: hex parse and format (`#RGB`, `#RRGGBB`, `#RRGGBBAA`), 8-bit byte tuples, and linear or sRGB `Vector3` / `Vector4` output (the sRGB vectors are what ImGui expects).
- **WCAG accessibility**: relative luminance, contrast ratio (1..21), conformance rating against a background, and `AdjustForContrast` which binary-searches Oklab lightness to hit a target while preserving hue and chroma.
- **Perceptual operations**: Oklab distance (`DistanceTo`), perceptually uniform mixing (`MixOklab`), and Oklab gradients (`Gradient`), alongside plain linear `Lerp`.
- **Named colors**: a CSS/X11 subset with case-insensitive lookup.

## Installation

### Package Manager Console

```powershell
Install-Package ktsu.Semantics.Color
```

### .NET CLI

```bash
dotnet add package ktsu.Semantics.Color
```

### Package Reference

```xml
<PackageReference Include="ktsu.Semantics.Color" Version="x.y.z" />
```

## Usage Examples

### Basic Example: accessible text color

```csharp
using ktsu.Semantics.Color;

Color text = Color.FromHex("#777777");
Color background = NamedColors.White;

double ratio = text.ContrastRatio(background); // ~4.48
AccessibilityLevel level = text.AccessibilityLevelAgainst(background); // Fail for AA body text

if (level < AccessibilityLevel.AA)
{
// darkens or lightens in Oklab, preserving hue and chroma, until AA is met
text = text.AdjustForContrast(background, AccessibilityLevel.AA);
}

Console.WriteLine(text.ToHex());
```

### Perceptually uniform gradients

```csharp
using ktsu.Semantics.Color;

Color start = Color.FromSrgb(0.9, 0.1, 0.2); // sRGB input, stored as linear
Color end = NamedColors.Blue;

// Oklab interpolation, inclusive of both endpoints; steps must be >= 2
IReadOnlyList<Color> ramp = start.Gradient(end, 5);

foreach (Color c in ramp)
{
(byte r, byte g, byte b, byte a) = c.ToBytes();
Console.WriteLine($"{c.ToHex()} rgba({r},{g},{b},{a})");
}
```

### Space conversions and vector interop

```csharp
using System.Numerics;
using ktsu.Semantics.Color;

Color c = NamedColors.Orange;

Hsl hsl = c.ToHsl(); // H in degrees, S/L in 0..1
Color complementary = Color.FromHsl(hsl with { H = (hsl.H + 180) % 360 });

Oklch lch = c.ToOklch(); // perceptual lightness/chroma/hue
Color brighter = Color.FromOklch(lch with { L = lch.L + 0.1 });

Vector4 imguiColor = complementary.ToSrgbVector4(); // gamma-encoded RGBA for ImGui
```

## API Reference

### `Color`

The canonical color: linear RGBA, each channel `double` in 0..1. A `readonly record struct` with positional properties `R`, `G`, `B`, `A`.

#### Construction and conversion

| Name | Return Type | Description |
|------|-------------|-------------|
| `FromLinear(r, g, b, a = 1)` | `Color` | From linear RGBA. |
| `FromSrgb(r, g, b, a = 1)` / `FromSrgb(Srgb, a = 1)` | `Color` | From gamma-encoded sRGB. |
| `FromBytes(r, g, b, a = 255)` | `Color` | From 8-bit channels. |
| `FromHex(string)` | `Color` | Parse `#RGB`, `#RRGGBB`, or `#RRGGBBAA` (the `#` is optional). |
| `FromOklab` / `FromOklch` / `FromHsl` / `FromHsv` | `Color` | From the named space. |
| `ToSrgb()` / `ToOklab()` / `ToOklch()` / `ToHsl()` / `ToHsv()` | space type | Convert out. |
| `ToHex()` | `string` | `#RRGGBB`, or `#RRGGBBAA` when alpha is not full. |
| `ToBytes()` | `(byte, byte, byte, byte)` | Rounded 8-bit RGBA. |
| `ToLinearVector3/4()` / `ToSrgbVector3/4()` | `Vector3` / `Vector4` | `System.Numerics` interop. |
| `WithAlpha(a)` / `Clamp()` | `Color` | Copy helpers. |

#### Operations

| Name | Return Type | Description |
|------|-------------|-------------|
| `RelativeLuminance` | `double` | WCAG luminance. |
| `ContrastRatio(other)` | `double` | WCAG ratio, 1..21. |
| `AccessibilityLevelAgainst(background, largeText = false)` | `AccessibilityLevel` | Conformance rating. |
| `AdjustForContrast(background, target, largeText = false)` | `Color` | Nudge lightness to meet a target level. |
| `DistanceTo(other)` | `double` | Perceptual Oklab distance. |
| `MixOklab(other, t)` | `Color` | Perceptually uniform mix (`t = 0` returns this, `t = 1` returns other). |
| `Lerp(other, t)` | `Color` | Linear-RGB interpolation. |
| `Gradient(to, steps)` | `IReadOnlyList<Color>` | Oklab gradient, inclusive of endpoints (`steps >= 2`). |

### Supporting types

| Type | Description |
|------|-------------|
| `Srgb` | Gamma-encoded sRGB, the only gamma-boundary crossing. `ToLinear()`, `FromLinear(Color)`. |
| `Hsl` / `Hsv` | Hue in degrees, saturation and lightness/value in 0..1, defined over sRGB. `FromSrgb` / `ToSrgb`. |
| `Oklab` | Perceptual color space (Ottosson 2020). `FromColor` / `ToColor` / `ToOklch`. |
| `Oklch` | Polar form of Oklab. `ToOklab`. |
| `AccessibilityLevel` | enum: `Fail = 0`, `AA = 1`, `AAA = 2`. |
| `NamedColors` | Common colors (`Black`, `White`, `Red`, `Orange`, `Transparent`, ...), plus `All` and `TryGet(name, out color)` with case-insensitive keys. |

`FromHex` throws `ArgumentException` for lengths other than 3, 6, or 8, and `Gradient` throws `ArgumentException` for `steps < 2`.

## 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](../LICENSE.md) file for details.
164 changes: 164 additions & 0 deletions Semantics.Music/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,164 @@
# ktsu.Semantics.Music

> Immutable, validated musical value types plus a harmonic and structural analysis layer, all pure logic with no I/O.

[![License](https://img.shields.io/github/license/ktsu-dev/Semantics.svg?label=License&logo=nuget)](../LICENSE.md)
[![NuGet Version](https://img.shields.io/nuget/v/ktsu.Semantics.Music?label=Stable&logo=nuget)](https://nuget.org/packages/ktsu.Semantics.Music)
[![NuGet Version](https://img.shields.io/nuget/vpre/ktsu.Semantics.Music?label=Latest&logo=nuget)](https://nuget.org/packages/ktsu.Semantics.Music)
[![NuGet Downloads](https://img.shields.io/nuget/dt/ktsu.Semantics.Music?label=Downloads&logo=nuget)](https://nuget.org/packages/ktsu.Semantics.Music)
[![GitHub commit activity](https://img.shields.io/github/commit-activity/m/ktsu-dev/Semantics?label=Commits&logo=github)](https://github.com/ktsu-dev/Semantics/commits/main)
[![GitHub contributors](https://img.shields.io/github/contributors/ktsu-dev/Semantics?label=Contributors&logo=github)](https://github.com/ktsu-dev/Semantics/graphs/contributors)
[![GitHub Actions Workflow Status](https://img.shields.io/github/actions/workflow/status/ktsu-dev/Semantics/dotnet.yml?label=Build&logo=github)](https://github.com/ktsu-dev/Semantics/actions)

`ktsu.Semantics.Music` is one package in the [ktsu.Semantics](../README.md) family. Start at the [root README](../README.md) 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. `Section`s 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**: `Mode` with roughly 29 presets (diatonic, jazz, symmetric, pentatonic, blues), `Scale` rooting a mode at a pitch class, with `Contains` and `DegreeOf`.
- **Chord-symbol parsing**: `Chord.Parse` handles triads, sixths, sevenths (including `m7b5` and `mmaj7`), extensions and altered tensions (`9`/`11`/`13`, `b9`/`#9`/`#11`/`b13`), suspensions, power chords, omissions (`no3`/`no5`), and slash bass.
- **Chord realization**: `ChordTones()` and `Voice(octave)` / `Voice(octave, inversion)`, plus `Transpose`.
- **Roman-numeral analysis both directions**: `Key.RomanNumeralOf(chord)` and `Key.ChordFromRomanNumeral(numeral)`.
- **Rhythm and real time**: rational `Duration`, `TimeSignature`, `Tempo`, and `Note` / `Rest` / `ChordEvent` events that convert to seconds.
- **Analysis layer**: `Progression` (roman numerals, functions, cadences, key inference, chromatic chords), `Section`, `Arrangement`, and `Form` with named-form recognition.

## Installation

### Package Manager Console

```powershell
Install-Package ktsu.Semantics.Music
```

### .NET CLI

```bash
dotnet add package ktsu.Semantics.Music
```

### Package Reference

```xml
<PackageReference Include="ktsu.Semantics.Music" Version="x.y.z" />
```

## Usage Examples

### Pitches, intervals, scales

```csharp
using ktsu.Semantics.Music;

Pitch middleC = Pitch.FromName("C4"); // MIDI 60
Pitch g4 = middleC.Transpose(7); // a perfect fifth up
Interval fifth = Interval.Between(middleC, g4); // +7 semitones
double hz = Pitch.FromName("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

```csharp
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.FromName("A4"), Duration.Quarter, Velocity.Forte);
double noteSeconds = a4.Seconds(tempo); // 0.5 s
```

### Roman numerals, both directions

```csharp
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

```csharp
using ktsu.Semantics.Music;

// "|" is a barline; spaces separate chords within a bar
Progression prog = Progression.Parse("| 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("| C | A7 | Dm | G7 |").ChromaticChords(key); // A7 -> secondary dominant

// Structure: sections -> arrangement -> form
Progression verse = Progression.Parse("| C | G | Am | F |");
Progression bridge = Progression.Parse("| 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
```

## API Reference

### Core value types

| Type | Description | Key factories / members |
|------|-------------|-------------------------|
| `PitchClass` | One of twelve pitch classes, folded to 0..11. | `Create(int)`, `Value`, `Name` |
| `Pitch` | MIDI pitch 0..127 (60 = C4). | `Create(int)`, `FromName(string)`, `FromFrequency(double)`, `Transpose(int)`, `FrequencyHz`, `Octave` |
| `Interval` | Signed interval in semitones. | `Create(int)`, `Between(Pitch, Pitch)`, `Semitones`, `Cents`, `Folded` |
| `Mode` | Scale shape by semitone offsets. | `FromName(string)`, presets (`Major`, `Dorian`, `HarmonicMinor`, `WholeTone`, `MajorPentatonic`, ...), `Intervals` |
| `Scale` | A mode rooted at a pitch class. | `Create(PitchClass, Mode)`, `Contains`, `DegreeOf`, `Transpose` |
| `Chord` | Chord parsed from a symbol. | `Parse(string)`, `ChordTones()`, `Voice(octave)`, `Voice(octave, inversion)`, `Transpose` |
| `Key` | Tonic in a mode, resolves function. | `Create(PitchClass, Mode)`, `RomanNumeralOf(Chord)`, `ChordFromRomanNumeral(string)`, `FunctionOf`, `Scale` |
| `Duration` | Exact rational fraction of a whole note. | `Create(int, int)`, presets (`Whole`..`Sixteenth`), `Dotted()`, `Add`, `Multiply`, `AsWholeNotes` |
| `TimeSignature` | Bar and beat lengths. | `Create(int, int)`, `BarDuration`, `BeatDuration` |
| `Note` / `Rest` / `ChordEvent` | Timed musical events (`IMusicalEvent`). | `Create(...)`, `Duration`, `Seconds(Tempo)` |
| `Velocity` | MIDI velocity 0..127. | `Create(int)`, presets (`Piano`..`Fortissimo`) |
| `Tempo` | Beats per minute with a beat unit. | `Create(double)`, `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?)`, `IsSameStructure(Section)` |
| `Arrangement` | Sections in performance order. | `Create(Key, IEnumerable<Section>)`, `Form`, `TotalBars` |
| `Form` | Structural pattern and its name. | `Of(Arrangement)`, `FromPattern(string)`, `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](../docs/complete-library-guide.md) 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](../LICENSE.md) file for details.
Loading