Skip to content

Commit

Permalink
Merge c9c5c5d into 8db1fe6
Browse files Browse the repository at this point in the history
  • Loading branch information
albertms10 committed Mar 30, 2024
2 parents 8db1fe6 + c9c5c5d commit b4ca8cd
Show file tree
Hide file tree
Showing 3 changed files with 132 additions and 17 deletions.
137 changes: 123 additions & 14 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,17 +6,16 @@
[![License](https://img.shields.io/badge/License-BSD_3--Clause-blue.svg)](https://opensource.org/license/bsd-3-clause/)
[![style: very good analysis](https://img.shields.io/badge/style-very_good_analysis-B22C89.svg)](https://pub.dev/packages/very_good_analysis)

A simple Dart library that provides a comprehensive set of utilities
for working with music theory concepts through a beautifully crafted API.
A comprehensive Dart library for effortlessly working with music theory concepts,
offering an elegant and beautifully crafted API.

## Features

- Chords, harmonic functions, and circle of fifths
- Intervals and qualities
- Notes, frequencies, accidentals, and enharmonic operations
- Scales and scale degrees
- Notes, accidentals, and enharmonic operations
- Intervals, qualities, and circle of fifths
- Chords, scales, harmonic functions, inversions and retrogrades
- Keys, key signatures, and modes
- Tuning systems (_work in progress_)
- Frequencies and tuning systems (_work in progress_)

## Usage

Expand All @@ -32,15 +31,27 @@ For more detailed usage instructions and examples, please refer to the

### Notes

Define `Note`s from the musical scale:
Define a `Note` from a `BaseNote` and an `Accidental`, or using their
shorthand static constants:

```dart
const Note(BaseNote.e, Accidental.flat); // E♭
Note.c; // C
Note.d; // D
Note.f; // F
```

Alter them:
`BaseNote`s can be obtained from semitones or ordinal:

```dart
BaseNote.fromSemitones(2); // D
BaseNote.fromSemitones(9) // A
BaseNote.fromOrdinal(3); // E
BaseNote.fromOrdinal(7); // B
```

Alter a `Note` with `sharp` or `flat`:

```dart
Note.c.sharp; // C♯
Expand All @@ -64,6 +75,48 @@ Pitch.parse("g''"); // G5
Pitch.parse('Eb3'); // E♭3
```

Get their difference in semitones:

```dart
BaseNote.c.difference(BaseNote.e); // 4
BaseNote.a.difference(BaseNote.e); // -5
BaseNote.a.positiveDifference(BaseNote.e); // 7
Note.c.difference(Note.e.flat); // 3
Pitch.parse('C').difference(Pitch.parse("c''''")); // 60
```

Transpose them:

```dart
Note.g.flat.transposeBy(-Interval.m3); // E♭
Note.b.inOctave(3).transposeBy(Interval.P5); // F♯4
```

Respell them by any criteria:

```dart
Note.c.sharp.respellByBaseNote(BaseNote.d); // D♭
Note.e.flat.respellByAccidental(Accidental.sharp); // D♯
Note.g.flat.inOctave(3).respellByOrdinalDistance(-1); // F♯3
Note.g.sharp.respelledUpwards; // A♭
Note.a.flat.respelledDownwards; // G♯
Note.b.sharp.inOctave(4).respelledSimple; // C5
```

Represent them using any notation system:

```dart
Note.d.flat
..toString() // D♭
..toString(system: NoteNotation.romance) // Re♭
..toString(system: NoteNotation.german); // Des
Note.b.flat.inOctave(-1).toString(); // B♭-1
Note.c.inOctave(6).toString(system: PitchNotation.helmholtz); // c′′′
```

### Intervals

Create an `Interval`:
Expand All @@ -86,8 +139,10 @@ Calculate the `Interval` between two notes:

```dart
Note.c.interval(Note.g); // P5
Note.d.interval(Note.f.sharp).inverted; // m6
Note.g.flat.transposeBy(-Interval.m3); // E♭
Note.d.interval(Note.f.sharp).inversion; // m6
BaseNote.d.intervalSize(BaseNote.f); // 3
BaseNote.a.intervalSize(BaseNote.e); // 5
```

And even play with the circle of fifths or any circle of intervals
Expand All @@ -98,6 +153,12 @@ Interval.P5.circleFrom(Note.c, distance: 12).toList();
// [C, G, D, A, E, B, F♯, C♯, G♯, D♯, A♯, E♯, B♯]
Note.c.circleOfFifths();
// (flats: [F, B♭, E♭, A♭, D♭, G♭], sharps: [G, D, A, E, B, F♯])
Note.c.flatCircleOfFifths(distance: 3); // [E♭, B♭, F, C, G, D, A]
Note.d.circleOfFifthsDistance; // 2
Note.a.flat.circleOfFifthsDistance; // -4
Note.c.fifthsDistanceWith(Note.e.flat) // -3
Note.b.fifthsDistanceWith(Note.f.sharp) // 1
```

### Keys
Expand All @@ -116,11 +177,29 @@ Note.d.major.signature; // 2 (F♯ C♯)
Note.e.flat.minor.signature; // -6 (B♭ E♭ A♭ D♭ G♭ C♭)
```

And its relative `Key`:
Whether it is theoretical:

```dart
Note.e.major.isTheoretical; // false
Note.a.flat.minor.isTheoretical; // true
```

And its relative and parallel `Key`s:

```dart
Note.d.major.relative; // B minor
Note.c.minor.relative; // E♭ major
Note.f.minor.parallel; // F major
Note.c.sharp.major.parallel; // C♯ minor
```

Represent it using any notation system:

```dart
Note.d.flat.major.toString(); // D♭ major
Note.c.major.toString(system: NoteNotation.romance); // Do maggiore
Note.e.flat.minor.toString(system: NoteNotation.german); // es-moll
```

### Key signatures
Expand All @@ -133,6 +212,14 @@ KeySignature([Note.b.flat, Note.e.flat]); // -2 (B♭ E♭)
KeySignature([Note.g.sharp, Note.a.sharp]); // null (G♯ A♯)
```

Increment them by sharps or flats:

```dart
KeySignature.fromDistance(-4).incrementBy(-1); // -3 (B♭ E♭ A♭)
KeySignature([Note.f.sharp, Note.c.sharp]).incrementBy(3);
// 5 (F♯ C♯ G♯ D♯ A♯)
```

And know its `Key`s:

```dart
Expand Down Expand Up @@ -201,6 +288,23 @@ Note.c.major.scale.functionChord(
); // D maj. (D F♯ A)
```

Rearrange any set of `Note`s, `Pitch`es or `PitchClass`es
as `inversion` or `retrograde`:

```dart
{Note.b, Note.a.sharp, Note.d}.inversion.toSet(); // {B, C, G♯}
{PitchClass.dSharp, PitchClass.g, PitchClass.fSharp}.retrograde.toSet();
// {{F♯|G♭}, {G}, {D♯|E♭}}
```

Or play with its numeric representation:

```dart
{PitchClass.b, PitchClass.aSharp, PitchClass.d, PitchClass.e}
..numericRepresentation.toSet() // {0, 11, 3, 5}
..deltaNumericRepresentation.toList(); // [0, -1, 4, 2]
```

### Chords

Create a `Chord` from a series of `Note`s or a `ChordPattern`:
Expand Down Expand Up @@ -233,18 +337,23 @@ Get the `Frequency` of a `Pitch`:

```dart
Note.a.inOctave(4).frequency(); // 440
final tuningSystem = EqualTemperament.edo12(referencePitch: Note.c.inOctave(4));
Note.b.flat.inOctave(4).frequency(
referenceFrequency: const Frequency(256),
tuningSystem:
EqualTemperament.edo12(referencePitch: Note.c.inOctave(4)),
tuningSystem: tuningSystem,
); // 456.1401436878537
Note.a.inOctave(4).frequency(temperature: const Celsius(18));
// 438.4619866006409
```

Get the closest note from a given `Frequency`:

```dart
const Frequency(432).closestPitch(); // A4-32
const Frequency(314).closestPitch(); // E♭4+16
const Frequency(440).closestPitch(temperature: const Celsius(24)); // A4-12
```

And combining both methods, the harmonic series of a given `Pitch`:
Expand Down
4 changes: 2 additions & 2 deletions lib/src/note/base_note.dart
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@ enum BaseNote implements Comparable<BaseNote> {
/// ```dart
/// BaseNote.c.difference(BaseNote.c) == 0
/// BaseNote.c.difference(BaseNote.e) == 4
/// BaseNote.a.difference(BaseNote.d) == 5
/// BaseNote.a.difference(BaseNote.e) == -5
/// ```
int difference(BaseNote other) => Note(this).difference(Note(other));

Expand All @@ -127,7 +127,7 @@ enum BaseNote implements Comparable<BaseNote> {
/// ```dart
/// BaseNote.c.positiveDifference(BaseNote.c) == 0
/// BaseNote.c.positiveDifference(BaseNote.e) == 4
/// BaseNote.a.positiveDifference(BaseNote.d) == 5
/// BaseNote.a.positiveDifference(BaseNote.e) == 7
/// ```
int positiveDifference(BaseNote other) {
final diff = difference(other);
Expand Down
8 changes: 7 additions & 1 deletion pubspec.yaml
Original file line number Diff line number Diff line change
@@ -1,8 +1,14 @@
name: music_notes
description: Comprehensive set of utilities for working with music theory concepts.
description: A comprehensive Dart library for effortlessly working with music theory concepts.
version: 0.17.1

repository: https://github.com/albertms10/music_notes.git
issue_tracker: https://github.com/albertms10/music_notes/issues
topics:
- music
- music-theory
- education
- midi

environment:
sdk: ">=3.3.0 <4.0.0"
Expand Down

0 comments on commit b4ca8cd

Please sign in to comment.