Skip to content

Commit

Permalink
feat!(pitch): 💥 use double, triple, and quadruple prime symbols for H…
Browse files Browse the repository at this point in the history
…elmholtz notation (#502)
  • Loading branch information
albertms10 committed May 17, 2024
1 parent 859d805 commit 5bfe6ed
Show file tree
Hide file tree
Showing 4 changed files with 55 additions and 18 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,7 @@ Note.d.flat
..toString(system: NoteNotation.german); // Des
Note.b.flat.inOctave(-1).toString(); // B♭-1
Note.c.inOctave(6).toString(system: PitchNotation.helmholtz); // c′′′
Note.c.inOctave(6).toString(system: PitchNotation.helmholtz); // c
PitchClass.c.toString(); // {C}
PitchClass.dSharp.toString(); // {D♯|E♭}
Expand Down
2 changes: 1 addition & 1 deletion example/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ void main() {
..toString(system: NoteNotation.german); // Des

Note.b.flat.inOctave(-1).toString(); // B♭-1
Note.c.inOctave(6).toString(system: PitchNotation.helmholtz); // c′′′
Note.c.inOctave(6).toString(system: PitchNotation.helmholtz); // c

PitchClass.c.toString(); // {C}
PitchClass.dSharp.toString(); // {D♯|E♭}
Expand Down
44 changes: 36 additions & 8 deletions lib/src/note/pitch.dart
Original file line number Diff line number Diff line change
Expand Up @@ -46,10 +46,18 @@ final class Pitch extends Scalable<Pitch> implements Comparable<Pitch> {
static const referenceOctave = 4;

static const _superPrime = '′';
static const _superDoublePrime = '″';
static const _superTriplePrime = '‴';
static const _superQuadruplePrime = '⁗';
static const _superPrimeAlt = "'";
static const _subPrime = '͵';
static const _subPrimeAlt = ',';

static const _compoundPrimeSymbols = [
_superDoublePrime,
_superTriplePrime,
_superQuadruplePrime,
];
static const _primeSymbols = [
_superPrime,
_superPrimeAlt,
Expand All @@ -58,9 +66,15 @@ final class Pitch extends Scalable<Pitch> implements Comparable<Pitch> {
];

static final _scientificNotationRegExp = RegExp(r'^(.+?)([-]?\d+)$');
static final _helmholtzNotationRegExp =
RegExp('(^[A-Ga-g${Accidental.symbols.join()}]+)'
'(${[for (final symbol in _primeSymbols) '$symbol+'].join('|')})?\$');
static final _helmholtzNotationRegExp = RegExp(
'(^(?:${[
for (final baseNote in BaseNote.values) baseNote.name,
].join('|')})[${Accidental.symbols.join()}]*)(${[
..._compoundPrimeSymbols,
for (final symbol in _primeSymbols) '$symbol+',
].join('|')})?\$',
caseSensitive: false,
);

/// Parse [source] as a [Pitch] and return its value.
///
Expand Down Expand Up @@ -92,12 +106,17 @@ final class Pitch extends Scalable<Pitch> implements Comparable<Pitch> {
? switch (primes?.first) {
'' || null => middleOctave - 1,
_subPrime || _subPrimeAlt => middleOctave - primes!.length - 1,
_ => throw FormatException('Invalid Pitch', source),
_ =>
throw FormatException('Invalid Pitch', source, notePart.length),
}
: switch (primes?.first) {
'' || null => middleOctave,
_superPrime || _superPrimeAlt => middleOctave + primes!.length,
_ => throw FormatException('Invalid Pitch', source),
_superDoublePrime => middleOctave + primes!.length + 1,
_superTriplePrime => middleOctave + primes!.length + 2,
_superQuadruplePrime => middleOctave + primes!.length + 3,
_ =>
throw FormatException('Invalid Pitch', source, notePart.length),
};

return Pitch(Note.parse(notePart), octave: octave);
Expand Down Expand Up @@ -606,12 +625,21 @@ final class HelmholtzPitchNotation extends PitchNotation {
static const romance =
HelmholtzPitchNotation(noteSystem: NoteNotation.romance);

static String _symbols(int n) => switch (n) {
4 => Pitch._superQuadruplePrime,
3 => Pitch._superTriplePrime,
2 => Pitch._superDoublePrime,
< 0 => Pitch._subPrime * n.abs(),
_ => Pitch._superPrime * n,
};

@override
String pitch(Pitch pitch) {
final note = pitch.note.toString(system: noteSystem);

return pitch.octave >= 3
? '${note.toLowerCase()}${Pitch._superPrime * (pitch.octave - 3)}'
: '$note${Pitch._subPrime * (pitch.octave - 2).abs()}';
return switch (pitch.octave) {
>= 3 => '${note.toLowerCase()}${_symbols(pitch.octave - 3)}',
_ => '$note${_symbols(pitch.octave - 2)}',
};
}
}
25 changes: 17 additions & 8 deletions test/src/note/pitch_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,17 @@ void main() {
group('.parse()', () {
test('throws a FormatException when source is invalid', () {
expect(() => Pitch.parse('x'), throwsFormatException);
expect(() => Pitch.parse('aa'), throwsFormatException);
expect(() => Pitch.parse("A,'"), throwsFormatException);
expect(() => Pitch.parse('bb,'), throwsFormatException);
expect(() => Pitch.parse("F#'"), throwsFormatException);
expect(() => Pitch.parse("g''h"), throwsFormatException);
expect(() => Pitch.parse('C,d'), throwsFormatException);
expect(() => Pitch.parse("d′'"), throwsFormatException);
expect(() => Pitch.parse('f″″'), throwsFormatException);

expect(() => Pitch.parse('D5,'), throwsFormatException);
expect(() => Pitch.parse('ba'), throwsFormatException);
expect(() => Pitch.parse("d7'"), throwsFormatException);
expect(() => Pitch.parse("e'4"), throwsFormatException);
expect(() => Pitch.parse("'E3"), throwsFormatException);
Expand All @@ -37,7 +41,8 @@ void main() {
expect(Pitch.parse('f'), Note.f.inOctave(3));
expect(Pitch.parse("d#'"), Note.d.sharp.inOctave(4));
expect(Pitch.parse("ebb''"), Note.e.flat.flat.inOctave(5));
expect(Pitch.parse('gx′′′'), Note.g.sharp.sharp.inOctave(6));
expect(Pitch.parse('b#′′'), Note.b.sharp.inOctave(5));
expect(Pitch.parse('gx‴'), Note.g.sharp.sharp.inOctave(6));

var pitch = Note.b.flat.flat.inOctave(-2);
expect(Pitch.parse(pitch.toString()), pitch);
Expand Down Expand Up @@ -1229,11 +1234,15 @@ void main() {
);
expect(
Note.f.sharp.inOctave(5).toString(system: PitchNotation.helmholtz),
'f♯′′',
'f♯',
);
expect(
Note.e.inOctave(7).toString(system: PitchNotation.helmholtz),
'e′′′′',
'e⁗',
);
expect(
Note.b.flat.inOctave(8).toString(system: PitchNotation.helmholtz),
'b♭′′′′′',
);
});

Expand Down Expand Up @@ -1282,11 +1291,11 @@ void main() {
Note.a.flat
.inOctave(5)
.toString(system: HelmholtzPitchNotation.german),
'as′′',
'as',
);
expect(
Note.e.inOctave(7).toString(system: HelmholtzPitchNotation.german),
'e′′′′',
'e',
);
});

Expand Down Expand Up @@ -1335,11 +1344,11 @@ void main() {
Note.a.flat
.inOctave(5)
.toString(system: HelmholtzPitchNotation.romance),
'la♭′′',
'la♭',
);
expect(
Note.e.inOctave(7).toString(system: HelmholtzPitchNotation.romance),
'mi′′′′',
Note.e.inOctave(6).toString(system: HelmholtzPitchNotation.romance),
'mi',
);
});

Expand Down

0 comments on commit 5bfe6ed

Please sign in to comment.