Skip to content

Commit

Permalink
Merge 4c082ad into f3cb0a6
Browse files Browse the repository at this point in the history
  • Loading branch information
albertms10 authored Mar 27, 2024
2 parents f3cb0a6 + 4c082ad commit d85cc7f
Show file tree
Hide file tree
Showing 7 changed files with 78 additions and 13 deletions.
1 change: 1 addition & 0 deletions lib/music_notes.dart
Original file line number Diff line number Diff line change
Expand Up @@ -31,4 +31,5 @@ export 'src/tuning/cent.dart';
export 'src/tuning/equal_temperament.dart';
export 'src/tuning/just_intonation.dart';
export 'src/tuning/ratio.dart';
export 'src/tuning/temperature.dart';
export 'src/tuning/tuning_system.dart';
8 changes: 5 additions & 3 deletions lib/src/note/closest_pitch.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import 'package:music_notes/utils.dart';

import '../tuning/cent.dart';
import '../tuning/equal_temperament.dart';
import '../tuning/temperature.dart';
import '../tuning/tuning_system.dart';
import 'frequency.dart';
import 'pitch.dart';
Expand Down Expand Up @@ -58,8 +59,8 @@ class ClosestPitch {
return ClosestPitch(Pitch.parse(match[1]!), cents: cents);
}

/// The [Frequency] of this [ClosestPitch] from [referenceFrequency]
/// and [tuningSystem].
/// The [Frequency] of this [ClosestPitch] from [referenceFrequency],
/// [tuningSystem] and [temperature].
///
/// Example:
/// ```dart
Expand All @@ -68,14 +69,15 @@ class ClosestPitch {
Frequency frequency({
Frequency referenceFrequency = Frequency.reference,
TuningSystem tuningSystem = const EqualTemperament.edo12(),
Celsius temperature = Celsius.reference,
}) =>
Frequency(
pitch.frequency(
referenceFrequency: referenceFrequency,
tuningSystem: tuningSystem,
) *
cents.ratio,
);
).at(temperature);

/// The string representation of this [ClosestPitch] record.
///
Expand Down
32 changes: 27 additions & 5 deletions lib/src/note/frequency.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import 'package:meta/meta.dart' show immutable;

import '../tuning/equal_temperament.dart';
import '../tuning/ratio.dart';
import '../tuning/temperature.dart';
import '../tuning/tuning_system.dart';
import 'closest_pitch.dart';
import 'hearing_range.dart';
Expand Down Expand Up @@ -35,8 +36,8 @@ extension type const Frequency._(num hertz) implements num {
/// ```
bool get isHumanAudible => HearingRange.human.isAudible(this);

/// The [ClosestPitch] to this [Frequency] from [referenceFrequency] and
/// [tuningSystem].
/// The [ClosestPitch] to this [Frequency] from [referenceFrequency],
/// [tuningSystem] and [temperature].
///
/// Example:
/// ```dart
Expand All @@ -45,20 +46,24 @@ extension type const Frequency._(num hertz) implements num {
///
/// const Frequency(260).closestPitch()
/// == Note.c.inOctave(4) - const Cent(10.7903)
///
/// const Frequency(440).closestPitch(temperature: const Celsius(24))
/// == Note.a.inOctave(4) - const Cent(12.06)
/// ```
///
/// This method and [ClosestPitch.frequency] are inverses of each other for a
/// specific input `frequency`.
///
/// ```dart
/// const frequency = Frequency(415);
/// frequency.closestPitch().frequency() == frequency;
/// const reference = Frequency(415);
/// reference.closestPitch().frequency() == reference;
/// ```
ClosestPitch closestPitch({
Frequency referenceFrequency = Frequency.reference,
TuningSystem tuningSystem = const EqualTemperament.edo12(),
Celsius temperature = Celsius.reference,
}) {
final cents = Ratio(hertz / referenceFrequency).cents;
final cents = Ratio(at(temperature) / referenceFrequency).cents;
final semitones =
tuningSystem.referencePitch.semitones + (cents / 100).round();

Expand All @@ -69,6 +74,7 @@ extension type const Frequency._(num hertz) implements num {
final closestPitchFrequency = closestPitch.frequency(
referenceFrequency: referenceFrequency,
tuningSystem: tuningSystem,
temperature: temperature,
);
final hertzDelta = hertz - closestPitchFrequency;

Expand Down Expand Up @@ -142,3 +148,19 @@ extension FrequencyIterableExtension on Iterable<Frequency> {
Set<ClosestPitch> get closestPitches =>
map((frequency) => frequency.closestPitch()).toSet();
}

/// A Frequency extension based on temperature.
extension TemperatureFrequency on Frequency {
// Speed of sound at [Celsius.zero] in m/s.
static const _baseSpeedOfSound = 331.3;

/// See [Speed of sound](https://en.wikipedia.org/wiki/Speed_of_sound#Tables).
static num speedOfSoundAt(Celsius temperature) =>
_baseSpeedOfSound + 0.6 * temperature;

/// This [Frequency] at [temperature], based on [reference].
Frequency at(Celsius temperature, {Celsius reference = Celsius.reference}) =>
Frequency(
hertz * (speedOfSoundAt(temperature) / speedOfSoundAt(reference)),
);
}
19 changes: 14 additions & 5 deletions lib/src/note/pitch.dart
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import '../music.dart';
import '../scalable.dart';
import '../tuning/cent.dart';
import '../tuning/equal_temperament.dart';
import '../tuning/temperature.dart';
import '../tuning/tuning_system.dart';
import 'accidental.dart';
import 'base_note.dart';
Expand Down Expand Up @@ -377,8 +378,8 @@ final class Pitch extends Scalable<Pitch> implements Comparable<Pitch> {
);
}

/// The [Frequency] of this [Pitch] from [referenceFrequency] and
/// [tuningSystem].
/// The [Frequency] of this [Pitch] from [referenceFrequency],
/// [tuningSystem] and [frequency].
///
/// Example:
/// ```dart
Expand All @@ -396,18 +397,24 @@ final class Pitch extends Scalable<Pitch> implements Comparable<Pitch> {
/// ) == const Frequency(430.54)
/// ```
///
/// Note.a.inOctave(4).frequency(temperature: const Celsius(18))
/// == const Frequency(438.46)
/// Note.a.inOctave(4).frequency(temperature: const Celsius(24))
/// == const Frequency(443.08)
///
/// This method and [Frequency.closestPitch] are inverses of each other for a
/// specific `pitch`.
///
/// ```dart
/// final pitch = Note.a.inOctave(5);
/// pitch.frequency().closestPitch().pitch == pitch;
/// final reference = Note.a.inOctave(5);
/// reference.frequency().closestPitch().pitch == reference;
/// ```
Frequency frequency({
Frequency referenceFrequency = Frequency.reference,
TuningSystem tuningSystem = const EqualTemperament.edo12(),
Celsius temperature = Celsius.reference,
}) =>
Frequency(referenceFrequency * tuningSystem.ratio(this));
Frequency(referenceFrequency * tuningSystem.ratio(this)).at(temperature);

/// The [ClosestPitch] set of harmonics series [upToIndex] from this [Pitch].
///
Expand All @@ -421,10 +428,12 @@ final class Pitch extends Scalable<Pitch> implements Comparable<Pitch> {
required int upToIndex,
Frequency referenceFrequency = Frequency.reference,
TuningSystem tuningSystem = const EqualTemperament.edo12(),
Celsius temperature = Celsius.reference,
}) =>
frequency(
referenceFrequency: referenceFrequency,
tuningSystem: tuningSystem,
temperature: temperature,
).harmonics(upToIndex: upToIndex).closestPitches;

/// The string representation of this [Pitch] based on [system].
Expand Down
5 changes: 5 additions & 0 deletions lib/src/tuning/temperature.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
/// The representation of a Celsius temperature.
extension type const Celsius(num degrees) implements num {
/// The reference [Celsius] temperature.
static const reference = Celsius(20);
}
13 changes: 13 additions & 0 deletions test/src/note/frequency_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,19 @@ void main() {
),
Note.a.inOctave(4) + const Cent(37.63165622959145),
);

expect(
const Frequency(440).closestPitch(temperature: const Celsius(24)),
Note.a.inOctave(4) - const Cent(12.060895566170192),
);
expect(
const Frequency(440).closestPitch(temperature: const Celsius(18)),
Note.a.inOctave(4) + const Cent(6.062103827228064),
);
expect(
const Frequency(256).closestPitch(temperature: const Celsius(18)),
Note.c.inOctave(4) - const Cent(31.569552402363644),
);
});

test('returns the same Frequency after Pitch.frequency()', () {
Expand Down
13 changes: 13 additions & 0 deletions test/src/note/pitch_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -995,6 +995,19 @@ void main() {
Note.b.inOctave(4).frequency(),
const Frequency(493.8833012561241),
);

expect(
Note.a.inOctave(4).frequency(temperature: const Celsius(18)),
const Frequency(438.4619866006409),
);
expect(
Note.a.inOctave(4).frequency(temperature: const Celsius(24)),
const Frequency(443.07602679871826),
);
expect(
Note.c.inOctave(4).frequency(temperature: const Celsius(18)),
const Frequency(260.71105706185494),
);
});

test('returns the Frequency of this Pitch at 438 Hz', () {
Expand Down

0 comments on commit d85cc7f

Please sign in to comment.