Skip to content

Commit

Permalink
refactor: Extract Frequency
Browse files Browse the repository at this point in the history
This is to separate concerns: converting the positioned note to a frequency,
which could be done in different ways, e.g. equal tempered, pythagorean
(these methods belong to PositionedNote),
versus properties or functionality related to the frequency itself, such as
isHumanAudible or a hypothetical .play() method.

Closes #107
  • Loading branch information
plammens committed May 12, 2023
1 parent 9115542 commit 4409f23
Show file tree
Hide file tree
Showing 5 changed files with 75 additions and 62 deletions.
1 change: 1 addition & 0 deletions lib/music_notes.dart
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ part 'src/music.dart';
part 'src/music_item.dart';
part 'src/note/accidental.dart';
part 'src/note/enharmonic_note.dart';
part 'src/note/frequency.dart';
part 'src/note/note.dart';
part 'src/note/notes.dart';
part 'src/note/positioned_note.dart';
Expand Down
23 changes: 23 additions & 0 deletions lib/src/note/frequency.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
part of '../../music_notes.dart';

/// Represents an absolute pitch, a physical frequency.
class Frequency {
Frequency(this.hertz);

final double hertz;

/// Whether this [Frequency] is inside the human hearing range.
///
/// Example:
/// ```dart
/// Note.a.inOctave(4).isHumanAudibleAt() == true
/// Note.d.inOctave(0).isHumanAudibleAt() == false
/// Note.g.inOctave(12).isHumanAudibleAt(442) == false
/// ```
bool get isHumanAudible {
const minFrequency = 20;
const maxFrequency = 20000;

return hertz >= minFrequency && hertz <= maxFrequency;
}
}
21 changes: 3 additions & 18 deletions lib/src/note/positioned_note.dart
Original file line number Diff line number Diff line change
Expand Up @@ -82,24 +82,9 @@ final class PositionedNote extends Note {
/// Note.a.inOctave(4).equalTemperamentFrequency(338) == 338
/// Note.bFlat.inOctave(4).equalTemperamentFrequency(338) == 464.04
/// ```
double equalTemperamentFrequency([double a4Hertzs = 440]) =>
a4Hertzs * math.pow(sqrt12_2, Note.a.inOctave(4).difference(this));

/// Whether this [Note] is inside the human hearing range at [a4Hertzs].
///
/// Example:
/// ```dart
/// Note.a.inOctave(4).isHumanAudibleAt() == true
/// Note.d.inOctave(0).isHumanAudibleAt() == false
/// Note.g.inOctave(12).isHumanAudibleAt(442) == false
/// ```
bool isHumanAudibleAt([double a4Hertzs = 440]) {
final frequency = equalTemperamentFrequency(a4Hertzs);
const minFrequency = 20;
const maxFrequency = 20000;

return frequency >= minFrequency && frequency <= maxFrequency;
}
Frequency equalTemperamentFrequency([double a4Hertzs = 440]) => Frequency(
a4Hertzs * math.pow(sqrt12_2, Note.a.inOctave(4).difference(this)),
);

/// Returns the string representation of this [Note] following the
/// [scientific pitch notation](https://en.wikipedia.org/wiki/Scientific_pitch_notation).
Expand Down
14 changes: 14 additions & 0 deletions test/src/note/frequency_test.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import 'package:music_notes/music_notes.dart';
import 'package:test/test.dart';

void main() {
group('.isHumanAudible', () {
test('should return whether the frequency is audible by humans', () {
expect(Frequency(0).isHumanAudible, isFalse);
expect(Frequency(100).isHumanAudible, isTrue);
expect(Frequency(400).isHumanAudible, isTrue);
expect(Frequency(15000).isHumanAudible, isTrue);
expect(Frequency(100000).isHumanAudible, isFalse);
});
});
}
78 changes: 34 additions & 44 deletions test/src/note/positioned_note_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -313,151 +313,141 @@ void main() {
group('.equalTemperamentFrequency()', () {
test('should return the hertzs of this PositionedNote from 440 Hz', () {
expect(
Note.c.inOctave(4).equalTemperamentFrequency(),
Note.c.inOctave(4).equalTemperamentFrequency().hertz,
closeTo(261.63, 0.01),
);
expect(
Note.cSharp.inOctave(4).equalTemperamentFrequency(),
Note.cSharp.inOctave(4).equalTemperamentFrequency().hertz,
closeTo(277.18, 0.01),
);
expect(
Note.dFlat.inOctave(4).equalTemperamentFrequency(),
Note.dFlat.inOctave(4).equalTemperamentFrequency().hertz,
closeTo(277.18, 0.01),
);
expect(
Note.d.inOctave(4).equalTemperamentFrequency(),
Note.d.inOctave(4).equalTemperamentFrequency().hertz,
closeTo(293.66, 0.01),
);
expect(
Note.dSharp.inOctave(4).equalTemperamentFrequency(),
Note.dSharp.inOctave(4).equalTemperamentFrequency().hertz,
closeTo(311.13, 0.01),
);
expect(
Note.eFlat.inOctave(4).equalTemperamentFrequency(),
Note.eFlat.inOctave(4).equalTemperamentFrequency().hertz,
closeTo(311.13, 0.01),
);
expect(
Note.e.inOctave(4).equalTemperamentFrequency(),
Note.e.inOctave(4).equalTemperamentFrequency().hertz,
closeTo(329.63, 0.01),
);
expect(
Note.f.inOctave(4).equalTemperamentFrequency(),
Note.f.inOctave(4).equalTemperamentFrequency().hertz,
closeTo(349.23, 0.01),
);
expect(
Note.fSharp.inOctave(4).equalTemperamentFrequency(),
Note.fSharp.inOctave(4).equalTemperamentFrequency().hertz,
closeTo(369.99, 0.01),
);
expect(
Note.gFlat.inOctave(4).equalTemperamentFrequency(),
Note.gFlat.inOctave(4).equalTemperamentFrequency().hertz,
closeTo(369.99, 0.01),
);
expect(
Note.g.inOctave(4).equalTemperamentFrequency(),
Note.g.inOctave(4).equalTemperamentFrequency().hertz,
closeTo(392, 0.01),
);
expect(
Note.gSharp.inOctave(4).equalTemperamentFrequency(),
Note.gSharp.inOctave(4).equalTemperamentFrequency().hertz,
closeTo(415.3, 0.01),
);
expect(
Note.aFlat.inOctave(4).equalTemperamentFrequency(),
Note.aFlat.inOctave(4).equalTemperamentFrequency().hertz,
closeTo(415.3, 0.01),
);
expect(Note.a.inOctave(4).equalTemperamentFrequency(), 440);
expect(Note.a.inOctave(4).equalTemperamentFrequency().hertz, 440);
expect(
Note.aSharp.inOctave(4).equalTemperamentFrequency(),
Note.aSharp.inOctave(4).equalTemperamentFrequency().hertz,
closeTo(466.16, 0.01),
);
expect(
Note.bFlat.inOctave(4).equalTemperamentFrequency(),
Note.bFlat.inOctave(4).equalTemperamentFrequency().hertz,
closeTo(466.16, 0.01),
);
expect(
Note.b.inOctave(4).equalTemperamentFrequency(),
Note.b.inOctave(4).equalTemperamentFrequency().hertz,
closeTo(493.88, 0.01),
);
});

test('should return the hertzs of this PositionedNote from 438 Hz', () {
expect(
Note.c.inOctave(4).equalTemperamentFrequency(438),
Note.c.inOctave(4).equalTemperamentFrequency(438).hertz,
closeTo(260.44, 0.01),
);
expect(
Note.cSharp.inOctave(4).equalTemperamentFrequency(438),
Note.cSharp.inOctave(4).equalTemperamentFrequency(438).hertz,
closeTo(275.92, 0.01),
);
expect(
Note.dFlat.inOctave(4).equalTemperamentFrequency(438),
Note.dFlat.inOctave(4).equalTemperamentFrequency(438).hertz,
closeTo(275.92, 0.01),
);
expect(
Note.d.inOctave(4).equalTemperamentFrequency(438),
Note.d.inOctave(4).equalTemperamentFrequency(438).hertz,
closeTo(292.33, 0.01),
);
expect(
Note.dSharp.inOctave(4).equalTemperamentFrequency(438),
Note.dSharp.inOctave(4).equalTemperamentFrequency(438).hertz,
closeTo(309.71, 0.01),
);
expect(
Note.eFlat.inOctave(4).equalTemperamentFrequency(438),
Note.eFlat.inOctave(4).equalTemperamentFrequency(438).hertz,
closeTo(309.71, 0.01),
);
expect(
Note.e.inOctave(4).equalTemperamentFrequency(438),
Note.e.inOctave(4).equalTemperamentFrequency(438).hertz,
closeTo(328.13, 0.01),
);
expect(
Note.f.inOctave(4).equalTemperamentFrequency(438),
Note.f.inOctave(4).equalTemperamentFrequency(438).hertz,
closeTo(347.64, 0.01),
);
expect(
Note.fSharp.inOctave(4).equalTemperamentFrequency(438),
Note.fSharp.inOctave(4).equalTemperamentFrequency(438).hertz,
closeTo(368.31, 0.01),
);
expect(
Note.gFlat.inOctave(4).equalTemperamentFrequency(438),
Note.gFlat.inOctave(4).equalTemperamentFrequency(438).hertz,
closeTo(368.31, 0.01),
);
expect(
Note.g.inOctave(4).equalTemperamentFrequency(438),
Note.g.inOctave(4).equalTemperamentFrequency(438).hertz,
closeTo(390.21, 0.01),
);
expect(
Note.gSharp.inOctave(4).equalTemperamentFrequency(438),
Note.gSharp.inOctave(4).equalTemperamentFrequency(438).hertz,
closeTo(413.42, 0.01),
);
expect(
Note.aFlat.inOctave(4).equalTemperamentFrequency(438),
Note.aFlat.inOctave(4).equalTemperamentFrequency(438).hertz,
closeTo(413.42, 0.01),
);
expect(Note.a.inOctave(4).equalTemperamentFrequency(438), 438);
expect(Note.a.inOctave(4).equalTemperamentFrequency(438).hertz, 438);
expect(
Note.aSharp.inOctave(4).equalTemperamentFrequency(438),
Note.aSharp.inOctave(4).equalTemperamentFrequency(438).hertz,
closeTo(464.04, 0.01),
);
expect(
Note.bFlat.inOctave(4).equalTemperamentFrequency(438),
Note.bFlat.inOctave(4).equalTemperamentFrequency(438).hertz,
closeTo(464.04, 0.01),
);
expect(
Note.b.inOctave(4).equalTemperamentFrequency(438),
Note.b.inOctave(4).equalTemperamentFrequency(438).hertz,
closeTo(491.64, 0.01),
);
});
});

group('.isHumanAudibleAt()', () {
test('should return whether this PositionedNote is human-audible', () {
expect(Note.c.inOctave(4).isHumanAudibleAt(338), isTrue);
expect(Note.a.inOctave(2).isHumanAudibleAt(442), isTrue);
expect(Note.d.inOctave(0).isHumanAudibleAt(), isFalse);
expect(Note.d.inOctave(11).isHumanAudibleAt(220), isTrue);
expect(Note.d.inOctave(12).isHumanAudibleAt(), isFalse);
});
});

group('.scientificName', () {
test(
'should return the scientific pitch notation name for this '
Expand Down

0 comments on commit 4409f23

Please sign in to comment.