Skip to content

Commit

Permalink
feat(note): ✨ add NoteNotation.useAscii param
Browse files Browse the repository at this point in the history
Signed-off-by: Albert Mañosa <26429103+albertms10@users.noreply.github.com>
  • Loading branch information
albertms10 committed May 12, 2024
1 parent 1d0a073 commit 5311009
Show file tree
Hide file tree
Showing 4 changed files with 79 additions and 22 deletions.
2 changes: 2 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
{
"cSpell.ignoreWords": ["music"],
"cSpell.words": [
"ascii",
"Ascii",
"barline",
"chordable",
"Chordable",
Expand Down
67 changes: 53 additions & 14 deletions lib/src/note/accidental.dart
Original file line number Diff line number Diff line change
Expand Up @@ -42,31 +42,48 @@ final class Accidental implements Comparable<Accidental> {
static const tripleFlat = Accidental(-3);

static const _doubleSharpSymbol = '𝄪';
static const _doubleSharpSymbolAlt = 'x';
static const _doubleSharpSymbolAscii = 'x';
static const _sharpSymbol = '♯';
static const _sharpSymbolAlt = '#';
static const _sharpSymbolAscii = '#';
static const _naturalSymbol = '♮';
static const _naturalSymbolAscii = '';
static const _flatSymbol = '♭';
static const _flatSymbolAlt = 'b';
static const _flatSymbolAscii = 'b';
static const _doubleFlatSymbol = '𝄫';

/// The list of valid symbols for an [Accidental].
static const _symbols = (
doubleSharp: _doubleSharpSymbol,
sharp: _sharpSymbol,
natural: _naturalSymbol,
flat: _flatSymbol,
doubleFlat: _doubleFlatSymbol,
);

static const _asciiSymbols = (
doubleSharp: _doubleSharpSymbolAscii,
sharp: _sharpSymbolAscii,
natural: _naturalSymbolAscii,
flat: _flatSymbolAscii,
doubleFlat: '$_flatSymbolAscii$_flatSymbolAscii',
);

/// The list of all valid symbols for an [Accidental].
static const symbols = [
_doubleSharpSymbol,
_doubleSharpSymbolAlt,
_doubleSharpSymbolAscii,
_sharpSymbol,
_sharpSymbolAlt,
_sharpSymbolAscii,
_naturalSymbol,
_flatSymbol,
_flatSymbolAlt,
_flatSymbolAscii,
_doubleFlatSymbol,
];

static int? _semitonesFromSymbol(String symbol) => switch (symbol) {
_doubleSharpSymbol || _doubleSharpSymbolAlt => 2,
_sharpSymbol || _sharpSymbolAlt => 1,
_naturalSymbol || '' => 0,
_flatSymbol || _flatSymbolAlt => -1,
_doubleSharpSymbol || _doubleSharpSymbolAscii => 2,
_sharpSymbol || _sharpSymbolAscii => 1,
_naturalSymbol || _naturalSymbolAscii => 0,
_flatSymbol || _flatSymbolAscii => -1,
_doubleFlatSymbol => -2,
_ => null,
};
Expand Down Expand Up @@ -164,12 +181,34 @@ final class Accidental implements Comparable<Accidental> {
/// Accidental.doubleFlat.symbol == '𝄫'
/// Accidental.tripleSharp.symbol == '♯𝄪'
/// ```
String get symbol {
String get symbol => _symbol();

/// The ASCII symbol of this [Accidental].
///
/// If the [Accidental] represents a natural note (0 semitones), returns an
/// empty string.
///
/// For other accidentals, returns a combination of sharp (#), flat (b), or
/// double sharp or flat symbols (x, bb) depending on the number of semitones
/// above or below the natural note.
///
/// Example:
/// ```dart
/// Accidental.flat.asciiSymbol == 'b'
/// Accidental.natural.asciiSymbol == ''
/// Accidental.doubleFlat.asciiSymbol == 'bb'
/// Accidental.tripleSharp.asciiSymbol == '#x'
/// ```
String get asciiSymbol => _symbol(useAscii: true);

String _symbol({bool useAscii = false}) {
final symbols = useAscii ? Accidental._asciiSymbols : Accidental._symbols;
if (semitones == 0) return _naturalSymbol;

final accidentalSymbol = semitones.isNegative ? _flatSymbol : _sharpSymbol;
final accidentalSymbol =
semitones.isNegative ? symbols.flat : symbols.sharp;
final doubleAccidentalSymbol =
semitones.isNegative ? _doubleFlatSymbol : _doubleSharpSymbol;
semitones.isNegative ? symbols.doubleFlat : symbols.doubleSharp;

final absSemitones = semitones.abs();
final singleAccidentals = accidentalSymbol * (absSemitones % 2);
Expand Down
20 changes: 18 additions & 2 deletions lib/src/note/note.dart
Original file line number Diff line number Diff line change
Expand Up @@ -437,8 +437,11 @@ final class Note extends Scalable<Note> implements Comparable<Note> {
/// The abstraction for [Note] notation systems.
@immutable
abstract class NoteNotation {
/// Whether to use ASCII characters.
final bool useAscii;

/// Creates a new [NoteNotation].
const NoteNotation();
const NoteNotation({this.useAscii = false});

/// The English alphabetic [NoteNotation] system.
static const english = EnglishNoteNotation();
Expand Down Expand Up @@ -481,6 +484,10 @@ final class EnglishNoteNotation extends NoteNotation {
/// Creates a new [EnglishNoteNotation].
const EnglishNoteNotation({this.showNatural = false});

/// Creates a new [EnglishNoteNotation] with [useAscii] enabled.
const EnglishNoteNotation.ascii({this.showNatural = false})
: super(useAscii: true);

@override
String accidental(Accidental accidental) =>
!showNatural && accidental.isNatural ? '' : accidental.symbol;
Expand All @@ -499,6 +506,9 @@ final class GermanNoteNotation extends NoteNotation {
/// Creates a new [GermanNoteNotation].
const GermanNoteNotation();

/// Creates a new [GermanNoteNotation] with [useAscii] enabled.
const GermanNoteNotation.ascii() : super(useAscii: true);

@override
String note(Note note) => switch (note) {
Note(baseNote: BaseNote.b, accidental: Accidental.flat) => 'B',
Expand Down Expand Up @@ -550,6 +560,10 @@ final class RomanceNoteNotation extends NoteNotation {
/// Creates a new [RomanceNoteNotation].
const RomanceNoteNotation({this.showNatural = false});

/// Creates a new [RomanceNoteNotation] with [useAscii] enabled.
const RomanceNoteNotation.ascii({this.showNatural = false})
: super(useAscii: true);

@override
String baseNote(BaseNote baseNote) => switch (baseNote) {
BaseNote.c => 'Do',
Expand All @@ -563,7 +577,9 @@ final class RomanceNoteNotation extends NoteNotation {

@override
String accidental(Accidental accidental) =>
!showNatural && accidental.isNatural ? '' : accidental.symbol;
!showNatural && accidental.isNatural
? ''
: (useAscii ? accidental.asciiSymbol : accidental.symbol);

@override
String tonalMode(TonalMode tonalMode) => switch (tonalMode) {
Expand Down
12 changes: 6 additions & 6 deletions lib/src/note/pitch.dart
Original file line number Diff line number Diff line change
Expand Up @@ -46,15 +46,15 @@ final class Pitch extends Scalable<Pitch> implements Comparable<Pitch> {
static const referenceOctave = 4;

static const _superPrime = '′';
static const _superPrimeAlt = "'";
static const _superPrimeAscii = "'";
static const _subPrime = '͵';
static const _subPrimeAlt = ',';
static const _subPrimeAscii = ',';

static const _primeSymbols = [
_superPrime,
_superPrimeAlt,
_superPrimeAscii,
_subPrime,
_subPrimeAlt,
_subPrimeAscii,
];

static final _scientificNotationRegExp = RegExp(r'^(.+?)([-]?\d+)$');
Expand Down Expand Up @@ -91,12 +91,12 @@ final class Pitch extends Scalable<Pitch> implements Comparable<Pitch> {
final octave = notePart[0].isUpperCase
? switch (primes?.first) {
'' || null => middleOctave - 1,
_subPrime || _subPrimeAlt => middleOctave - primes!.length - 1,
_subPrime || _subPrimeAscii => middleOctave - primes!.length - 1,
_ => throw FormatException('Invalid Pitch', source),
}
: switch (primes?.first) {
'' || null => middleOctave,
_superPrime || _superPrimeAlt => middleOctave + primes!.length,
_superPrime || _superPrimeAscii => middleOctave + primes!.length,
_ => throw FormatException('Invalid Pitch', source),
};

Expand Down

0 comments on commit 5311009

Please sign in to comment.