Skip to content

Commit

Permalink
feat(mode)!: use brightness as the Dorian Brightness Quotient (#83)
Browse files Browse the repository at this point in the history
* feat(mode)!: use `brightness` as the Dorian Brightness Quotient

* feat(mode): add `ModalMode.mirrored` getter
  • Loading branch information
albertms10 committed May 1, 2023
1 parent 97af515 commit 50e2943
Show file tree
Hide file tree
Showing 3 changed files with 53 additions and 17 deletions.
1 change: 1 addition & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
"cSpell.words": [
"dorian",
"helmholtz",
"heptatonic",
"ionian",
"locrian",
"lydian",
Expand Down
39 changes: 30 additions & 9 deletions lib/src/tonality/mode.dart
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,13 @@ part of '../../music_notes.dart';
/// See [Mode (music)](https://en.wikipedia.org/wiki/Mode_(music)).
abstract class Mode implements Enum, Comparable<Mode> {
Scale get scale;

/// The [Dorian Brightness Quotient](https://mynewmicrophone.com/dorian-brightness-quotient)
/// is a number we assign to a heptatonic scale that tells us how bright or
/// dark the scale is relative to [ModalMode.dorian].
///
/// The lower the number, the darker the scale. The higher the number,
/// the brighter the scale.
int get brightness;

/// [Comparator] for [Mode]s.
Expand All @@ -17,10 +24,10 @@ abstract class Mode implements Enum, Comparable<Mode> {

enum TonalMode implements Mode {
/// See [Major mode](https://en.wikipedia.org/wiki/Major_mode).
major(Scale.major, brightness: 0),
major(Scale.major, brightness: 2),

/// See [Minor mode](https://en.wikipedia.org/wiki/Minor_mode).
minor(Scale.naturalMinor, brightness: -3);
minor(Scale.naturalMinor, brightness: -1);

@override
final Scale scale;
Expand All @@ -46,25 +53,25 @@ enum TonalMode implements Mode {

enum ModalMode implements Mode {
/// See [Lydian mode](https://en.wikipedia.org/wiki/Lydian_mode).
lydian(Scale.lydian, brightness: 1),
lydian(Scale.lydian, brightness: 3),

/// See [Ionian mode](https://en.wikipedia.org/wiki/Ionian_mode).
ionian(Scale.ionian, brightness: 0),
ionian(Scale.ionian, brightness: 2),

/// See [Mixolydian mode](https://en.wikipedia.org/wiki/Mixolydian_mode).
mixolydian(Scale.mixolydian, brightness: -1),
mixolydian(Scale.mixolydian, brightness: 1),

/// See [Dorian mode](https://en.wikipedia.org/wiki/Dorian_mode).
dorian(Scale.dorian, brightness: -2),
dorian(Scale.dorian, brightness: 0),

/// See [Aeolian mode](https://en.wikipedia.org/wiki/Aeolian_mode).
aeolian(Scale.aeolian, brightness: -3),
aeolian(Scale.aeolian, brightness: -1),

/// See [Phrygian mode](https://en.wikipedia.org/wiki/Phrygian_mode).
phrygian(Scale.phrygian, brightness: -4),
phrygian(Scale.phrygian, brightness: -2),

/// See [Locrian mode](https://en.wikipedia.org/wiki/Locrian_mode).
locrian(Scale.locrian, brightness: -5);
locrian(Scale.locrian, brightness: -3);

@override
final Scale scale;
Expand All @@ -74,6 +81,20 @@ enum ModalMode implements Mode {

const ModalMode(this.scale, {required this.brightness});

/// Returns the mirrored version of this [ModalMode].
///
/// Follows the DBQ property where the mirrored mode has the opposite
/// [brightness] value.
///
/// Example:
/// ```dart
/// ModalMode.dorian.mirrored == ModalMode.dorian
/// ModalMode.ionian.mirrored == ModalMode.phrygian
/// ModalMode.aeolian.mirrored == ModalMode.mixolydian
/// ```
ModalMode get mirrored =>
values.firstWhere((mode) => mode.brightness == -brightness);

@override
int compareTo(Mode other) => Mode.compareModes(this, other);
}
30 changes: 22 additions & 8 deletions test/src/tonality/mode_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,7 @@ import 'package:music_notes/music_notes.dart';
import 'package:test/test.dart';

void main() {
group('TonalMode', () {
group('.opposite', () {
test('should return the correct opposite mode', () {
expect(TonalMode.major.opposite, TonalMode.minor);
expect(TonalMode.minor.opposite, TonalMode.major);
});
});

group('Mode', () {
group('.compareTo', () {
test('should correctly sort Mode items in a collection', () {
final orderedSet = SplayTreeSet<Mode>.of(const [
Expand All @@ -33,4 +26,25 @@ void main() {
});
});
});

group('TonalMode', () {
group('.opposite', () {
test('should return the correct opposite TonalMode', () {
expect(TonalMode.major.opposite, TonalMode.minor);
expect(TonalMode.minor.opposite, TonalMode.major);
});
});
});

group('ModalMode', () {
group('.mirrored', () {
test('should return the mirrored version of this ModalMode', () {
expect(ModalMode.dorian.mirrored, ModalMode.dorian);
expect(ModalMode.mixolydian.mirrored, ModalMode.aeolian);
expect(ModalMode.ionian.mirrored, ModalMode.phrygian);
expect(ModalMode.locrian.mirrored, ModalMode.lydian);
expect(ModalMode.lydian.mirrored, ModalMode.locrian);
});
});
});
}

0 comments on commit 50e2943

Please sign in to comment.