Skip to content

Commit

Permalink
feat(scale): add descendingIntervalSteps (#104)
Browse files Browse the repository at this point in the history
* feat(scale): add `descendingIntervalSteps`

* test(scale): remove unnecessary `toList` call on `reversed`

* test(scale): add test case for descending `PositionedNote` scale
  • Loading branch information
albertms10 committed May 9, 2023
1 parent 429f904 commit e61b124
Show file tree
Hide file tree
Showing 2 changed files with 95 additions and 18 deletions.
69 changes: 51 additions & 18 deletions lib/src/scale/scale.dart
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,15 @@ part of '../../music_notes.dart';
///
/// See [Scale (music)](https://en.wikipedia.org/wiki/Scale_(music)).
class Scale {
/// The interval steps that define the scale.
/// The interval steps that define this [Scale].
final List<Interval> intervalSteps;

/// Creates a new [Scale] from [intervalSteps].
const Scale(this.intervalSteps);
/// The descending interval steps that define this [Scale] (if different).
final List<Interval>? _descendingIntervalSteps;

/// Creates a new [Scale] from [intervalSteps] and optional
/// [_descendingIntervalSteps].
const Scale(this.intervalSteps, [this._descendingIntervalSteps]);

/// ![C Ionian scale](https://upload.wikimedia.org/score/p/2/p2fun2296uif26uyy61yxjli7ocfq9d/p2fun229.png).
static const ionian = Scale([
Expand Down Expand Up @@ -105,15 +109,26 @@ class Scale {
]);

/// ![C Melodic minor scale](https://upload.wikimedia.org/score/9/2/92i6sjg41ji8y1ab881a1pcq1u3hr0p/92i6sjg4.png).
static const melodicMinor = Scale([
Interval.majorSecond,
Interval.minorSecond,
Interval.majorSecond,
Interval.majorSecond,
Interval.majorSecond,
Interval.majorSecond,
Interval.minorSecond,
]);
static const melodicMinor = Scale(
[
Interval.majorSecond,
Interval.minorSecond,
Interval.majorSecond,
Interval.majorSecond,
Interval.majorSecond,
Interval.majorSecond,
Interval.minorSecond,
],
[
Interval.majorSecond,
Interval.majorSecond,
Interval.minorSecond,
Interval.majorSecond,
Interval.majorSecond,
Interval.minorSecond,
Interval.majorSecond,
],
);

/// See [Chromatic scale](https://en.wikipedia.org/wiki/Chromatic_scale).
///
Expand Down Expand Up @@ -183,6 +198,10 @@ class Scale {
Interval.minorSecond,
]);

/// The descending interval steps that define this [Scale].
List<Interval> get descendingIntervalSteps =>
_descendingIntervalSteps ?? intervalSteps.reversed.toList();

/// Returns the scale of notes starting from [transposable].
///
/// Example:
Expand All @@ -198,13 +217,27 @@ class Scale {
/// Scale.melodicMinor.fromNote(Note.c)
/// == const [Note.c, Note.d, Note.eFlat, Note.f, Note.g, Note.a, Note.b,
/// Note.c]
///
/// Scale.melodicMinor.fromNote(Note.c, isDescending: true)
/// == const [Note.c, Note.bFlat, Note.aFlat, Note.g, Note.f, Note.eFlat,
/// Note.d, Note.c]
/// ```
List<Transposable<T>> fromNote<T>(Transposable<T> transposable) =>
intervalSteps.fold(
[transposable],
(scaleNotes, interval) =>
[...scaleNotes, scaleNotes.last.transposeBy(interval)],
);
List<Transposable<T>> fromNote<T>(
Transposable<T> transposable, {
bool isDescending = false,
}) {
final steps = isDescending ? descendingIntervalSteps : intervalSteps;

return steps.fold(
[transposable],
(scaleNotes, interval) => [
...scaleNotes,
scaleNotes.last.transposeBy(
interval.descending(isDescending: isDescending),
),
],
);
}

/// Returns the mirrored scale version of this [Scale].
///
Expand Down
44 changes: 44 additions & 0 deletions test/src/scale/scale_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,19 @@ void main() {
Note.d.inOctave(3),
],
);
expect(
Scale.major.fromNote(Note.cSharp, isDescending: true),
const [
Note.cSharp,
Note(Notes.b, Accidental.sharp),
Note.aSharp,
Note.gSharp,
Note.fSharp,
Note(Notes.e, Accidental.sharp),
Note.dSharp,
Note.cSharp,
],
);
});

test(
Expand Down Expand Up @@ -88,6 +101,19 @@ void main() {
Note.d,
],
);
expect(
Scale.naturalMinor.fromNote(Note.aFlat, isDescending: true),
const [
Note.aFlat,
Note.gFlat,
Note(Notes.f, Accidental.flat),
Note.eFlat,
Note.dFlat,
Note(Notes.c, Accidental.flat),
Note.bFlat,
Note.aFlat,
],
);
},
);

Expand Down Expand Up @@ -120,6 +146,20 @@ void main() {
Note.d,
],
);
expect(
Scale.harmonicMinor
.fromNote(Note.e.inOctave(3), isDescending: true),
[
Note.e.inOctave(3),
Note.dSharp.inOctave(3),
Note.c.inOctave(3),
Note.b.inOctave(2),
Note.a.inOctave(2),
Note.g.inOctave(2),
Note.fSharp.inOctave(2),
Note.e.inOctave(2),
],
);
},
);

Expand Down Expand Up @@ -152,6 +192,10 @@ void main() {
Note.gSharp.inOctave(2),
],
);
expect(
Scale.melodicMinor.fromNote(Note.f, isDescending: true),
Scale.naturalMinor.fromNote(Note.f).reversed,
);
},
);

Expand Down

0 comments on commit e61b124

Please sign in to comment.