Skip to content

Commit

Permalink
Merge 80ddfbe into 6ed2abe
Browse files Browse the repository at this point in the history
  • Loading branch information
albertms10 committed Jun 3, 2023
2 parents 6ed2abe + 80ddfbe commit d01fb61
Show file tree
Hide file tree
Showing 2 changed files with 148 additions and 132 deletions.
234 changes: 122 additions & 112 deletions lib/model/cryptogram_scheme.dart
Original file line number Diff line number Diff line change
Expand Up @@ -4,27 +4,27 @@ import 'package:music_notes/music_notes.dart';

class CryptogramScheme {
final String name;
final Map<Pattern, Note> patterns;
final Map<String, Note> patterns;

/// Under this scheme the vowel sounds in the text are matched to the vowel
/// sounds of the solmization syllables of Guido of Arezzo (where 'ut' is the
/// root, which we now call 'do', [Note.c]).
///
/// See [Musical cryptogram](https://en.wikipedia.org/wiki/Musical_cryptogram#Syllables_to_solmization_names).
CryptogramScheme.solmization()
const CryptogramScheme.solmization()
: name = 'Solmization',
patterns = {
RegExp('do|ut', caseSensitive: false): Note.c,
RegExp('re', caseSensitive: false): Note.d,
RegExp('mi', caseSensitive: false): Note.e,
RegExp('fa', caseSensitive: false): Note.f,
RegExp('sol?', caseSensitive: false): Note.g,
RegExp('la', caseSensitive: false): Note.a,
RegExp('(t|s)i', caseSensitive: false): Note.b,
RegExp('a', caseSensitive: false): Note.f,
RegExp('e', caseSensitive: false): Note.d,
RegExp('i', caseSensitive: false): Note.e,
RegExp('u', caseSensitive: false): Note.c,
patterns = const {
'do|ut': Note.c,
're': Note.d,
'mi': Note.e,
'fa': Note.f,
'sol?': Note.g,
'la': Note.a,
'(t|s)i': Note.b,
'a': Note.f,
'e': Note.d,
'i': Note.e,
'u': Note.c,
};

/// Arisen late in the 19th century, it was more akin to normal encipherment.
Expand All @@ -38,71 +38,71 @@ class CryptogramScheme {
/// | V | W | X | Y | Z | | |
///
/// See [Musical cryptogram](https://en.wikipedia.org/wiki/Musical_cryptogram#French).
CryptogramScheme.french()
const CryptogramScheme.french()
: name = 'French',
patterns = {
RegExp('A', caseSensitive: false): Note.a,
RegExp('B', caseSensitive: false): Note.b,
RegExp('C', caseSensitive: false): Note.c,
RegExp('D', caseSensitive: false): Note.d,
RegExp('E', caseSensitive: false): Note.e,
RegExp('F', caseSensitive: false): Note.f,
RegExp('G', caseSensitive: false): Note.g,
RegExp('H', caseSensitive: false): Note.a,
RegExp('I', caseSensitive: false): Note.b,
RegExp('J', caseSensitive: false): Note.c,
RegExp('K', caseSensitive: false): Note.d,
RegExp('L', caseSensitive: false): Note.e,
RegExp('M', caseSensitive: false): Note.f,
RegExp('N', caseSensitive: false): Note.g,
RegExp('O', caseSensitive: false): Note.a,
RegExp('P', caseSensitive: false): Note.b,
RegExp('Q', caseSensitive: false): Note.c,
RegExp('R', caseSensitive: false): Note.d,
RegExp('S', caseSensitive: false): Note.e,
RegExp('T', caseSensitive: false): Note.f,
RegExp('U', caseSensitive: false): Note.g,
RegExp('V', caseSensitive: false): Note.a,
RegExp('W', caseSensitive: false): Note.b,
RegExp('X', caseSensitive: false): Note.c,
RegExp('Y', caseSensitive: false): Note.d,
RegExp('Z', caseSensitive: false): Note.e,
patterns = const {
'A': Note.a,
'B': Note.b,
'C': Note.c,
'D': Note.d,
'E': Note.e,
'F': Note.f,
'G': Note.g,
'H': Note.a,
'I': Note.b,
'J': Note.c,
'K': Note.d,
'L': Note.e,
'M': Note.f,
'N': Note.g,
'O': Note.a,
'P': Note.b,
'Q': Note.c,
'R': Note.d,
'S': Note.e,
'T': Note.f,
'U': Note.g,
'V': Note.a,
'W': Note.b,
'X': Note.c,
'Y': Note.d,
'Z': Note.e,
};

/// Derived on the [CryptogramScheme.french] but leaving H = [Note.b] and
/// starting the second line with 'I'.
///
/// See [Musical cryptogram](https://en.wikipedia.org/wiki/Musical_cryptogram#Summary_of_signature_motifs)
/// (Alain).
CryptogramScheme.frenchVariant()
const CryptogramScheme.frenchVariant()
: name = 'French variant',
patterns = {
RegExp('A', caseSensitive: false): Note.a,
RegExp('B', caseSensitive: false): Note.b,
RegExp('C', caseSensitive: false): Note.c,
RegExp('D', caseSensitive: false): Note.d,
RegExp('E', caseSensitive: false): Note.e,
RegExp('F', caseSensitive: false): Note.f,
RegExp('G', caseSensitive: false): Note.g,
RegExp('H', caseSensitive: false): Note.b,
RegExp('I', caseSensitive: false): Note.a,
RegExp('J', caseSensitive: false): Note.b,
RegExp('K', caseSensitive: false): Note.c,
RegExp('L', caseSensitive: false): Note.d,
RegExp('M', caseSensitive: false): Note.e,
RegExp('N', caseSensitive: false): Note.f,
RegExp('O', caseSensitive: false): Note.g,
RegExp('P', caseSensitive: false): Note.a,
RegExp('Q', caseSensitive: false): Note.b,
RegExp('R', caseSensitive: false): Note.c,
RegExp('S', caseSensitive: false): Note.d,
RegExp('T', caseSensitive: false): Note.e,
RegExp('U', caseSensitive: false): Note.f,
RegExp('V', caseSensitive: false): Note.g,
RegExp('W', caseSensitive: false): Note.a,
RegExp('X', caseSensitive: false): Note.b,
RegExp('Y', caseSensitive: false): Note.c,
RegExp('Z', caseSensitive: false): Note.d,
patterns = const {
'A': Note.a,
'B': Note.b,
'C': Note.c,
'D': Note.d,
'E': Note.e,
'F': Note.f,
'G': Note.g,
'H': Note.b,
'I': Note.a,
'J': Note.b,
'K': Note.c,
'L': Note.d,
'M': Note.e,
'N': Note.f,
'O': Note.g,
'P': Note.a,
'Q': Note.b,
'R': Note.c,
'S': Note.d,
'T': Note.e,
'U': Note.f,
'V': Note.g,
'W': Note.a,
'X': Note.b,
'Y': Note.c,
'Z': Note.d,
};

/// Because the development of note names took place within the framework of
Expand All @@ -113,48 +113,55 @@ class CryptogramScheme {
/// 'Es' in German, could represent 'S' and [Note.a.flat] the digraph 'As'.
///
/// See [Musical cryptogram](https://en.wikipedia.org/wiki/Musical_cryptogram#German).
CryptogramScheme.german()
const CryptogramScheme.german()
: name = 'German',
patterns = {
RegExp('As', caseSensitive: false): Note.a.flat,
RegExp('Ais', caseSensitive: false): Note.a.sharp,
RegExp('A', caseSensitive: false): Note.a,
RegExp('B', caseSensitive: false): Note.b.flat,
RegExp('Ces', caseSensitive: false): Note.c.flat,
RegExp('Cis', caseSensitive: false): Note.c.sharp,
RegExp('C', caseSensitive: false): Note.c,
RegExp('Des', caseSensitive: false): Note.d.flat,
RegExp('Dis', caseSensitive: false): Note.d.sharp,
RegExp('D', caseSensitive: false): Note.d,
RegExp('Es', caseSensitive: false): Note.e.flat,
RegExp('Eis', caseSensitive: false): Note.e.sharp,
RegExp('Fes', caseSensitive: false): Note.f.flat,
RegExp('Fis', caseSensitive: false): Note.f.sharp,
RegExp('F', caseSensitive: false): Note.f,
RegExp('Ges', caseSensitive: false): Note.g.flat,
RegExp('Gis', caseSensitive: false): Note.g.sharp,
RegExp('G', caseSensitive: false): Note.g,
RegExp('E', caseSensitive: false): Note.e,
RegExp('H', caseSensitive: false): Note.b,
RegExp('I', caseSensitive: false): Note.b,
RegExp('J', caseSensitive: false): Note.c,
RegExp('K', caseSensitive: false): Note.d,
RegExp('L', caseSensitive: false): Note.e,
RegExp('M', caseSensitive: false): Note.f,
RegExp('N', caseSensitive: false): Note.g,
RegExp('O', caseSensitive: false): Note.a,
RegExp('P', caseSensitive: false): Note.b,
RegExp('Q', caseSensitive: false): Note.c,
RegExp('R', caseSensitive: false): Note.d,
RegExp('S', caseSensitive: false): Note.e.flat,
RegExp('T', caseSensitive: false): Note.f,
RegExp('U', caseSensitive: false): Note.g,
RegExp('V', caseSensitive: false): Note.a,
RegExp('W', caseSensitive: false): Note.b,
RegExp('X', caseSensitive: false): Note.c,
RegExp('Y', caseSensitive: false): Note.d,
RegExp('Z', caseSensitive: false): Note.e,
patterns = const {
'As': Note(BaseNote.a, Accidental.flat),
'Ais': Note(BaseNote.a, Accidental.sharp),
'A': Note.a,
'B': Note(BaseNote.b, Accidental.flat),
'Ces': Note(BaseNote.c, Accidental.flat),
'Cis': Note(BaseNote.c, Accidental.sharp),
'C': Note.c,
'Des': Note(BaseNote.d, Accidental.flat),
'Dis': Note(BaseNote.d, Accidental.sharp),
'D': Note.d,
'Es': Note(BaseNote.e, Accidental.flat),
'Eis': Note(BaseNote.e, Accidental.sharp),
'Fes': Note(BaseNote.f, Accidental.flat),
'Fis': Note(BaseNote.f, Accidental.sharp),
'F': Note.f,
'Ges': Note(BaseNote.g, Accidental.flat),
'Gis': Note(BaseNote.g, Accidental.sharp),
'G': Note.g,
'E': Note.e,
'H': Note.b,
'I': Note.b,
'J': Note.c,
'K': Note.d,
'L': Note.e,
'M': Note.f,
'N': Note.g,
'O': Note.a,
'P': Note.b,
'Q': Note.c,
'R': Note.d,
'S': Note(BaseNote.e, Accidental.flat),
'T': Note.f,
'U': Note.g,
'V': Note.a,
'W': Note.b,
'X': Note.c,
'Y': Note.d,
'Z': Note.e,
};

static const List<CryptogramScheme> schemes = [
CryptogramScheme.solmization(),
CryptogramScheme.french(),
CryptogramScheme.frenchVariant(),
CryptogramScheme.german(),
];
}

extension CryptogramStringExtension on String {
Expand All @@ -170,12 +177,15 @@ extension CryptogramStringExtension on String {
/// 'Alain'.toCryptogram(CryptogramScheme.frenchVariant())
/// == [Note.a, Note.d, Note.a, Note.a, Note.f]
/// ```
List<Note> toCryptogram(CryptogramScheme scheme) {
List<Note> toCryptogram([
CryptogramScheme scheme = const CryptogramScheme.solmization(),
]) {
final seenMatches = <Match>[];
final notes = SplayTreeMap<int, Note>();

for (final pattern in scheme.patterns.keys) {
for (final match in pattern.allMatches(this)) {
final matches = RegExp(pattern, caseSensitive: false).allMatches(this);
for (final match in matches) {
final isMatchSeen = seenMatches.any(
(element) => match.start >= element.start && match.end <= element.end,
);
Expand Down
46 changes: 26 additions & 20 deletions test/model/cryptogram_string_extension_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -6,79 +6,85 @@ void main() {
group('CryptogramStringExtension', () {
group('.toCryptogram()', () {
test('should return the musical cryptogram of this String', () {
final herculesDuxFerrarie = 'Hercules Dux Ferrarie'
.toCryptogram(CryptogramScheme.solmization());
final herculesDuxFerrarie = 'Hercules Dux Ferrarie'.toCryptogram();
expect(
herculesDuxFerrarie,
[Note.d, Note.c, Note.d, Note.c, Note.d, Note.f, Note.e, Note.d],
);

final arnoldSchonberg =
'A. SCHBEG'.toCryptogram(CryptogramScheme.german());
'A. SCHBEG'.toCryptogram(const CryptogramScheme.german());
expect(
arnoldSchonberg,
[Note.a, Note.e.flat, Note.c, Note.b, Note.b.flat, Note.e, Note.g],
);

final belaBartok = 'BE BA'.toCryptogram(CryptogramScheme.french());
final belaBartok =
'BE BA'.toCryptogram(const CryptogramScheme.french());
expect(belaBartok, [Note.b, Note.e, Note.b, Note.a]);

final carlPhilipEmmanuelBach =
'CFE BACH'.toCryptogram(CryptogramScheme.german());
'CFE BACH'.toCryptogram(const CryptogramScheme.german());
expect(
carlPhilipEmmanuelBach,
[Note.c, Note.f, Note.e, Note.b.flat, Note.a, Note.c, Note.b],
);

final dimitriSchostakowitsch =
'D. SCH'.toCryptogram(CryptogramScheme.german());
'D. SCH'.toCryptogram(const CryptogramScheme.german());
expect(dimitriSchostakowitsch, [Note.d, Note.e.flat, Note.c, Note.b]);

final edvardGried = 'EBG'.toCryptogram(CryptogramScheme.french());
final edvardGried = 'EBG'.toCryptogram(const CryptogramScheme.french());
expect(edvardGried, [Note.e, Note.b, Note.g]);

final franzSchubert = 'F. SCH'.toCryptogram(CryptogramScheme.german());
final franzSchubert =
'F. SCH'.toCryptogram(const CryptogramScheme.german());
expect(franzSchubert, [Note.f, Note.e.flat, Note.c, Note.b]);

final gustavHolst = 'GSAH'.toCryptogram(CryptogramScheme.german());
final gustavHolst =
'GSAH'.toCryptogram(const CryptogramScheme.german());
expect(gustavHolst, [Note.g, Note.e.flat, Note.a, Note.b]);

final jehanAlain =
'ALAIN'.toCryptogram(CryptogramScheme.frenchVariant());
'ALAIN'.toCryptogram(const CryptogramScheme.frenchVariant());
expect(jehanAlain, [Note.a, Note.d, Note.a, Note.a, Note.f]);

final johannesBrahms = 'BAHS'.toCryptogram(CryptogramScheme.german());
final johannesBrahms =
'BAHS'.toCryptogram(const CryptogramScheme.german());
expect(johannesBrahms, [Note.b.flat, Note.a, Note.b, Note.e.flat]);

final johannSebastianBach =
'BACH'.toCryptogram(CryptogramScheme.german());
'BACH'.toCryptogram(const CryptogramScheme.german());
expect(johannSebastianBach, [Note.b.flat, Note.a, Note.c, Note.b]);

final johnCage = 'CAGE'.toCryptogram(CryptogramScheme.german());
final johnCage = 'CAGE'.toCryptogram(const CryptogramScheme.german());
expect(johnCage, [Note.c, Note.a, Note.g, Note.e]);

final josephHaydn = 'HAYDN'.toCryptogram(CryptogramScheme.german());
final josephHaydn =
'HAYDN'.toCryptogram(const CryptogramScheme.german());
expect(josephHaydn, [Note.b, Note.a, Note.d, Note.d, Note.g]);

final metaAbegg = 'ABEGG'.toCryptogram(CryptogramScheme.german());
final metaAbegg = 'ABEGG'.toCryptogram(const CryptogramScheme.german());
expect(metaAbegg, [Note.a, Note.b.flat, Note.e, Note.g, Note.g]);

final nielsGade = 'GADE'.toCryptogram(CryptogramScheme.german());
final nielsGade = 'GADE'.toCryptogram(const CryptogramScheme.german());
expect(nielsGade, [Note.g, Note.a, Note.d, Note.e]);

final paulSacher = 'SACHER'.toCryptogram(CryptogramScheme.german());
final paulSacher =
'SACHER'.toCryptogram(const CryptogramScheme.german());
expect(
paulSacher,
[Note.e.flat, Note.a, Note.c, Note.b, Note.e, Note.d],
);

final robertSchumann = 'SCHA'.toCryptogram(CryptogramScheme.german());
final robertSchumann =
'SCHA'.toCryptogram(const CryptogramScheme.german());
expect(robertSchumann, [Note.e.flat, Note.c, Note.b, Note.a]);

final asch1 = 'ASCH'.toCryptogram(CryptogramScheme.german());
final asch1 = 'ASCH'.toCryptogram(const CryptogramScheme.german());
expect(asch1, [Note.a.flat, Note.c, Note.b]);

final asch2 = 'A SCH'.toCryptogram(CryptogramScheme.german());
final asch2 = 'A SCH'.toCryptogram(const CryptogramScheme.german());
expect(asch2, [Note.a, Note.e.flat, Note.c, Note.b]);
});
});
Expand Down

0 comments on commit d01fb61

Please sign in to comment.