diff --git a/example/main.dart b/example/main.dart index 550156a5..9d548338 100644 --- a/example/main.dart +++ b/example/main.dart @@ -270,7 +270,7 @@ void main() { const Frequency(314).closestPitch(); // E♭4+16 const Frequency(440).closestPitch(temperature: const Celsius(24)); // A4-12 - Note.c.inOctave(1).harmonics(upToIndex: 15); + Note.c.inOctave(1).harmonics().take(16).toSet(); // {C1, C2, G2+2, C3, E3-14, G3+2, A♯3-31, C4, D4+4, // E4-14, F♯4-49, G4+2, A♭4+41, A♯4-31, B4-12, C5} diff --git a/lib/src/note/frequency.dart b/lib/src/note/frequency.dart index 09ed4920..90486efe 100644 --- a/lib/src/note/frequency.dart +++ b/lib/src/note/frequency.dart @@ -109,24 +109,27 @@ extension type const Frequency._(num hertz) implements num { index.isNegative ? hertz / (index.abs() + 1) : hertz * (index + 1), ); - /// The [Set] of [harmonics series](https://en.wikipedia.org/wiki/Harmonic_series_(music)) - /// [upToIndex] from this [Frequency]. + /// The set of [harmonic](https://en.wikipedia.org/wiki/Harmonic_series_(music)) + /// or [undertone](https://en.wikipedia.org/wiki/Undertone_series) series + /// from this [Frequency]. /// /// Example: /// ```dart - /// Note.a.inOctave(3).frequency().harmonics(upToIndex: 2) + /// const Frequency(220).harmonics().take(3).toSet() /// == const {Frequency(220), Frequency(440), Frequency(660)} /// - /// Note.a.inOctave(5).frequency().harmonics(upToIndex: -2) - /// == {const Frequency(880), const Frequency(440), const Frequency(293.33)} + /// Note.a.inOctave(5).frequency().harmonics(undertone: true).take(3).toSet() + /// == const {Frequency(880), Frequency(440), Frequency(293.33)} /// ``` - /// /// --- /// See also: /// * [Pitch.harmonics] for a [ClosestPitch] set of harmonic series. - Set harmonics({required int upToIndex}) => { - for (var i = 0; i <= upToIndex.abs(); i++) harmonic(i * upToIndex.sign), - }; + Iterable harmonics({bool undertone = false}) sync* { + var i = 0; + while (true) { + yield harmonic(i++ * (undertone ? -1 : 1)); + } + } /// This [Frequency] formatted as a string. /// diff --git a/lib/src/note/pitch.dart b/lib/src/note/pitch.dart index 28b48fee..33863fcf 100644 --- a/lib/src/note/pitch.dart +++ b/lib/src/note/pitch.dart @@ -465,17 +465,17 @@ final class Pitch extends Scalable /// ``` TuningFork at(Frequency frequency) => TuningFork(this, frequency); - /// The [ClosestPitch] set of harmonics series [upToIndex] from this [Pitch] - /// from [tuningSystem] and [temperature]. + /// The [ClosestPitch] set of harmonics series from this [Pitch] from + /// [tuningSystem], [temperature], and whether [undertone]. /// /// Example: /// ```dart - /// Note.c.inOctave(1).harmonics(upToIndex: 15).toString() + /// Note.c.inOctave(1).harmonics().take(16).toSet().toString() /// == '{C1, C2, G2+2, C3, E3-14, G3+2, A♯3-31, C4, D4+4, ' /// 'E4-14, F♯4-49, G4+2, A♭4+41, A♯4-31, B4-12, C5}' /// ``` - Set harmonics({ - required int upToIndex, + Iterable harmonics({ + bool undertone = false, TuningSystem tuningSystem = const EqualTemperament.edo12(), Celsius temperature = Celsius.reference, Celsius referenceTemperature = Celsius.reference, @@ -484,9 +484,7 @@ final class Pitch extends Scalable tuningSystem: tuningSystem, // we deliberately omit the temperature here, as the subsequent call to // `Frequency.closestPitch` will already take it into account. - ) - .harmonics(upToIndex: upToIndex) - .map( + ).harmonics(undertone: undertone).map( (frequency) => frequency .closestPitch( tuningSystem: tuningSystem, @@ -494,8 +492,7 @@ final class Pitch extends Scalable referenceTemperature: referenceTemperature, ) .respelledSimple, - ) - .toSet(); + ); /// The string representation of this [Pitch] based on [system]. /// diff --git a/test/src/note/frequency_test.dart b/test/src/note/frequency_test.dart index 46ed8ce9..608d0c0a 100644 --- a/test/src/note/frequency_test.dart +++ b/test/src/note/frequency_test.dart @@ -99,72 +99,69 @@ void main() { }); group('.harmonics()', () { - test( - 'returns a Set of the harmonic series up to index from this Frequency', - () { - expect( - const Frequency(512).harmonics(upToIndex: -15), - { - const Frequency(512), - const Frequency(256), - const Frequency(170.66666666666666), - const Frequency(128), - const Frequency(102.4), - const Frequency(85.33333333333333), - const Frequency(73.14285714285714), - const Frequency(64), - const Frequency(56.888888888888886), - const Frequency(51.2), - const Frequency(46.54545454545455), - const Frequency(42.666666666666664), - const Frequency(39.38461538461539), - const Frequency(36.57142857142857), - const Frequency(34.13333333333333), - const Frequency(32), - }, - ); - expect( - const Frequency(400).harmonics(upToIndex: -1), - const {Frequency(400), Frequency(200)}, - ); - expect( - const Frequency(220).harmonics(upToIndex: 0), - const {Frequency(220)}, - ); - expect( - const Frequency(110).harmonics(upToIndex: 1), - const {Frequency(110), Frequency(220)}, - ); - expect( - const Frequency(32).harmonics(upToIndex: 15), - const { - Frequency(32), - Frequency(64), - Frequency(96), - Frequency(128), - Frequency(160), - Frequency(192), - Frequency(224), - Frequency(256), - Frequency(288), - Frequency(320), - Frequency(352), - Frequency(384), - Frequency(416), - Frequency(448), - Frequency(480), - Frequency(512), - }, - ); - }, - ); + test('returns a Set of the harmonic series from this Frequency', () { + expect( + const Frequency(512).harmonics(undertone: true).take(16).toSet(), + { + const Frequency(512), + const Frequency(256), + const Frequency(170.66666666666666), + const Frequency(128), + const Frequency(102.4), + const Frequency(85.33333333333333), + const Frequency(73.14285714285714), + const Frequency(64), + const Frequency(56.888888888888886), + const Frequency(51.2), + const Frequency(46.54545454545455), + const Frequency(42.666666666666664), + const Frequency(39.38461538461539), + const Frequency(36.57142857142857), + const Frequency(34.13333333333333), + const Frequency(32), + }, + ); + expect( + const Frequency(400).harmonics(undertone: true).take(2), + const {Frequency(400), Frequency(200)}, + ); + expect( + const Frequency(220).harmonics().take(1), + const {Frequency(220)}, + ); + expect( + const Frequency(110).harmonics().take(2), + const {Frequency(110), Frequency(220)}, + ); + expect( + const Frequency(32).harmonics().take(16), + const { + Frequency(32), + Frequency(64), + Frequency(96), + Frequency(128), + Frequency(160), + Frequency(192), + Frequency(224), + Frequency(256), + Frequency(288), + Frequency(320), + Frequency(352), + Frequency(384), + Frequency(416), + Frequency(448), + Frequency(480), + Frequency(512), + }, + ); + }); }); group('.format()', () { test('returns this Frequency formatted as a string', () { expect(const Frequency(440).format(), '440 Hz'); expect(const Frequency(415.62).format(), '415.62 Hz'); - expect(const Frequency(2200.2968).format(), '2200.2968 Hz'); + expect(const Frequency(2200.296).format(), '2200.296 Hz'); }); }); }); diff --git a/test/src/note/pitch_test.dart b/test/src/note/pitch_test.dart index dcf2e1d9..ecb12ad6 100644 --- a/test/src/note/pitch_test.dart +++ b/test/src/note/pitch_test.dart @@ -1143,7 +1143,7 @@ void main() { group('.harmonics()', () { test('returns the ClosestPitch set of harmonic series', () { expect( - Note.c.inOctave(1).harmonics(upToIndex: 15).toString(), + Note.c.inOctave(1).harmonics().take(16).toSet().toString(), '{C1, C2, G2+2, C3, E3-14, G3+2, A♯3-31, C4, D4+4, ' 'E4-14, F♯4-49, G4+2, A♭4+41, A♯4-31, B4-12, C5}', ); @@ -1152,11 +1152,12 @@ void main() { Note.c .inOctave(1) .harmonics( - upToIndex: 15, tuningSystem: const EqualTemperament.edo12( fork: TuningFork(Pitch.reference, Frequency(438)), ), ) + .take(16) + .toSet() .toString(), '{C1, C2, G2+2, C3, E3-14, G3+2, A♯3-31, C4, D4+4, ' 'E4-14, F♯4-49, G4+2, A♭4+41, A♯4-31, B4-12, C5}', @@ -1166,10 +1167,11 @@ void main() { Note.c .inOctave(1) .harmonics( - upToIndex: 15, tuningSystem: const EqualTemperament.edo12(fork: TuningFork.c256), ) + .take(16) + .toSet() .toString(), '{C1, C2, G2+2, C3, E3-14, G3+2, A♯3-31, C4, D4+4, ' 'E4-14, F♯4-49, G4+2, A♭4+41, A♯4-31, B4-12, C5}', @@ -1178,7 +1180,9 @@ void main() { expect( Note.c .inOctave(1) - .harmonics(upToIndex: 15, temperature: const Celsius(18)) + .harmonics(temperature: const Celsius(18)) + .take(16) + .toSet() .toString(), '{C1+6, C2+6, G2+8, C3+6, E3-8, G3+8, A♯3-25, C4+6, D4+10, ' 'E4-8, F♯4-43, G4+8, A♭4+47, A♯4-25, B4-6, C5+6}',