Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 6 additions & 2 deletions lib/model/alphabet.dart
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import 'package:collection/collection.dart';
import 'package:diacritic/diacritic.dart';
import 'package:flutter/material.dart';

Expand Down Expand Up @@ -154,8 +155,11 @@ class Alphabet {
bool operator ==(Object other) =>
other is Alphabet &&
name == other.name &&
_letterToNumber == other._letterToNumber;
const MapEquality<String, int>().equals(
_letterToNumber,
other._letterToNumber,
);

@override
int get hashCode => Object.hash(name, _letterToNumber);
int get hashCode => Object.hash(name, Object.hashAll(_letterToNumber.values));
}
26 changes: 22 additions & 4 deletions lib/model/cryptogram_scheme.dart
Original file line number Diff line number Diff line change
@@ -1,10 +1,20 @@
import 'dart:collection' show SplayTreeMap;

import 'package:collection/collection.dart' show MapEquality;
import 'package:music_notes/music_notes.dart';

import 'note_name.dart';

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

const CryptogramScheme({
required this.name,
required this.patterns,
this.notationSystem = const EnglishNoteNotation.symbol(),
});

/// 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
Expand All @@ -13,6 +23,7 @@ class CryptogramScheme {
/// See [Musical cryptogram](https://en.wikipedia.org/wiki/Musical_cryptogram#Syllables_to_solmization_names).
const CryptogramScheme.solmization()
: name = 'Solmization',
notationSystem = const RomanceNoteNotation.symbol(),
patterns = const {
'do|ut': .c,
're': .d,
Expand Down Expand Up @@ -40,6 +51,9 @@ class CryptogramScheme {
/// See [Musical cryptogram](https://en.wikipedia.org/wiki/Musical_cryptogram#French).
const CryptogramScheme.french()
: name = 'French',
notationSystem = const RomanceNoteNotation.symbol(
noteNameNotation: FrenchNoteNameNotation(),
),
patterns = const {
'A': .a,
'B': .b,
Expand Down Expand Up @@ -76,6 +90,9 @@ class CryptogramScheme {
/// (Alain).
const CryptogramScheme.frenchVariant()
: name = 'French variant',
notationSystem = const RomanceNoteNotation.symbol(
noteNameNotation: FrenchNoteNameNotation(),
),
patterns = const {
'A': .a,
'B': .b,
Expand Down Expand Up @@ -115,6 +132,7 @@ class CryptogramScheme {
/// See [Musical cryptogram](https://en.wikipedia.org/wiki/Musical_cryptogram#German).
const CryptogramScheme.german()
: name = 'German',
notationSystem = const GermanNoteNotation(),
patterns = const {
'As': Note(.a, .flat),
'Ais': Note(.a, .sharp),
Expand Down Expand Up @@ -170,10 +188,10 @@ class CryptogramScheme {
/// Example:
/// ```dart
/// const CryptogramScheme.german().cryptogramOf('Bach')
/// == [Note.b.flat, Note.a, Note.c, Note.b]
/// == <Note>[.b.flat, .a, .c, .b]
///
/// const CryptogramScheme.frenchVariant().cryptogramOf('Alain')
/// == [Note.a, Note.d, Note.a, Note.a, Note.f]
/// == <Note>[.a, .d, .a, .a, .f]
/// ```
List<Note> cryptogramOf(String name) {
final seenMatches = <Match>[];
Expand All @@ -199,8 +217,8 @@ class CryptogramScheme {
bool operator ==(Object other) =>
other is CryptogramScheme &&
name == other.name &&
patterns == other.patterns;
const MapEquality<String, Note>().equals(patterns, other.patterns);

@override
int get hashCode => Object.hash(name, patterns);
int get hashCode => Object.hash(name, Object.hashAll(patterns.values));
}
2 changes: 1 addition & 1 deletion lib/model/name.dart
Original file line number Diff line number Diff line change
Expand Up @@ -29,5 +29,5 @@ class Name {
other is Name && name == other.name && alphabet == other.alphabet;

@override
int get hashCode => Object.hash(name, alphabet.hashCode);
int get hashCode => Object.hash(name, alphabet);
}
37 changes: 37 additions & 0 deletions lib/model/note_name.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import 'package:music_notes/music_notes.dart';

/// The French notation system for [NoteName].
final class FrenchNoteNameNotation extends StringNotationSystem<NoteName> {
/// Creates a new [FrenchNoteNameNotation].
const FrenchNoteNameNotation();

static final _noteNames = <NoteName, String>{
.c: 'Do',
.d: 'Ré',
.e: 'Mi',
.f: 'Fa',
.g: 'Sol',
.a: 'La',
.b: 'Si',
};

static final _regExp = RegExp(
'(?<noteName>${_noteNames.values.join('|')})',
caseSensitive: false,
);

@override
RegExp get regExp => _regExp;

@override
NoteName parseMatch(RegExpMatch match) {
final name = match.namedGroup('noteName')!.toLowerCase();

return _noteNames.entries
.firstWhere((entry) => entry.value.toLowerCase() == name)
.key;
}

@override
String format(NoteName noteName) => _noteNames[noteName]!;
}
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,10 @@ class _CryptogramSchemeDropdownMenuItem extends StatelessWidget {
),
const SizedBox(width: 14),
Text(
scheme.cryptogramOf(name.name).join('–'),
scheme
.cryptogramOf(name.name)
.map((note) => note.format(scheme.notationSystem))
.join('–'),
style: TextStyle(
color: theme.colorScheme.primary.withValues(alpha: 0.6),
fontSize: 14,
Expand Down
2 changes: 1 addition & 1 deletion lib/widgets/musical_cryptogram/cryptogram_section.dart
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ class _CryptogramSectionState extends State<CryptogramSection> {
bottom: BorderSide(color: color.withValues(alpha: 0.4), width: 2),
),
),
clipBehavior: Clip.antiAlias,
clipBehavior: .antiAlias,
child: Stack(
children: [
Align(
Expand Down
7 changes: 3 additions & 4 deletions lib/widgets/musical_cryptogram/note_card.dart
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
import 'package:flutter/material.dart';
import 'package:music_notes/music_notes.dart';

class NoteCard extends StatelessWidget {
final Note note;
final String label;

const NoteCard({super.key, required this.note});
const NoteCard({super.key, required this.label});

@override
Widget build(BuildContext context) {
Expand All @@ -24,7 +23,7 @@ class NoteCard extends StatelessWidget {
mainAxisSize: .min,
children: [
Text(
'$note',
label,
style: theme.textTheme.displayLarge?.copyWith(
fontWeight: .w300,
color: onPrimary,
Expand Down
3 changes: 2 additions & 1 deletion lib/widgets/musical_cryptogram/note_cards.dart
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@ class NoteCards extends StatelessWidget {
runSpacing: 8,
crossAxisAlignment: .end,
children: [
for (final note in name.musicalCryptogram) NoteCard(note: note),
for (final note in name.musicalCryptogram)
NoteCard(label: note.format(name.scheme.notationSystem)),
],
),
);
Expand Down
2 changes: 1 addition & 1 deletion lib/widgets/name_section.dart
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ class _NameSectionState extends State<NameSection> {
),
),
),
clipBehavior: Clip.antiAlias,
clipBehavior: .antiAlias,
child: Stack(
children: [
Align(
Expand Down
2 changes: 1 addition & 1 deletion lib/widgets/name_section_value.dart
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ class NameSectionValue extends StatelessWidget {
final color = theme.colorScheme.primary;

return FittedBox(
fit: BoxFit.scaleDown,
fit: .scaleDown,
alignment: AlignmentDirectional.centerEnd,
child: Text(
'$value',
Expand Down
38 changes: 27 additions & 11 deletions pubspec.lock
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,10 @@ packages:
dependency: transitive
description:
name: async
sha256: "758e6d74e971c3e5aceb4110bfd6698efc7f501675bcfe0c775459a8140750eb"
sha256: e2eb0491ba5ddb6177742d2da23904574082139b07c1e33b8503b9f46f3e1a37
url: "https://pub.dev"
source: hosted
version: "2.13.0"
version: "2.13.1"
boolean_selector:
dependency: transitive
description:
Expand All @@ -34,7 +34,7 @@ packages:
source: hosted
version: "1.1.2"
collection:
dependency: transitive
dependency: "direct main"
description:
name: collection
sha256: "2f5709ae4d3d59dd8f7cd309b4e023046b57d8a6c82130785d2b0e5868084e76"
Expand Down Expand Up @@ -119,10 +119,10 @@ packages:
dependency: "direct main"
description:
name: music_notes
sha256: "6deb2bbc6bb6490d3323d8782825bb43f168b147ff26d1795101a6578edfe665"
sha256: "41d29c4900d7592e91c95c4dea0f2e75cc46d30f94719ba33f7c9abb0c125cbf"
url: "https://pub.dev"
source: hosted
version: "0.24.0"
version: "0.25.0"
path:
dependency: transitive
description:
Expand All @@ -131,6 +131,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.9.1"
petitparser:
dependency: transitive
description:
name: petitparser
sha256: "91bd59303e9f769f108f8df05e371341b15d59e995e6806aefab827b58336675"
url: "https://pub.dev"
source: hosted
version: "7.0.2"
sky_engine:
dependency: transitive
description: flutter
Expand All @@ -140,10 +148,10 @@ packages:
dependency: transitive
description:
name: source_span
sha256: "254ee5351d6cb365c859e20ee823c3bb479bf4a293c22d17a9f1bf144ce86f7c"
sha256: "56a02f1f4cd1a2d96303c0144c93bd6d909eea6bee6bf5a0e0b685edbd4c47ab"
url: "https://pub.dev"
source: hosted
version: "1.10.1"
version: "1.10.2"
stack_trace:
dependency: transitive
description:
Expand Down Expand Up @@ -196,18 +204,26 @@ packages:
dependency: "direct dev"
description:
name: very_good_analysis
sha256: "96245839dbcc45dfab1af5fa551603b5c7a282028a64746c19c547d21a7f1e3a"
sha256: "27927d1140ce1b140f998b6340f730a626faa5b95110b3e34a238ff254d731d0"
url: "https://pub.dev"
source: hosted
version: "10.0.0"
version: "10.1.0"
vm_service:
dependency: transitive
description:
name: vm_service
sha256: "45caa6c5917fa127b5dbcfbd1fa60b14e583afdc08bfc96dda38886ca252eb60"
sha256: "0016aef94fc66495ac78af5859181e3f3bf2026bd8eecc72b9565601e19ab360"
url: "https://pub.dev"
source: hosted
version: "15.2.0"
xml:
dependency: transitive
description:
name: xml
sha256: "971043b3a0d3da28727e40ed3e0b5d18b742fa5a68665cca88e74b7876d5e025"
url: "https://pub.dev"
source: hosted
version: "15.0.2"
version: "6.6.1"
sdks:
dart: ">=3.10.0 <4.0.0"
flutter: ">=3.38.0 <4.0.0"
3 changes: 2 additions & 1 deletion pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,11 @@ environment:
flutter: ^3.38.0

dependencies:
collection: ^1.19.1
diacritic: ^0.1.6
flutter:
sdk: flutter
music_notes: ^0.24.0
music_notes: ^0.25.0

dev_dependencies:
flutter_test:
Expand Down
6 changes: 5 additions & 1 deletion test/model/alphabet_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -171,11 +171,15 @@ void main() {
// ignore: prefer_const_constructors for testing
.latinNaturalOrder(),
// ignore: prefer_const_constructors for testing
Alphabet(name: 'A', letterToNumber: {'a': 1, 'b': 2}),
// ignore: prefer_const_constructors for testing
.latinNaturalOrder1(),
// ignore: prefer_const_constructors for testing
Alphabet(name: 'A', letterToNumber: {'a': 1, 'b': 2}),
// ignore: prefer_const_constructors for testing
.latinNaturalOrder(),
};
expect(collection.length, 2);
expect(collection.length, 3);
});
});
});
Expand Down
8 changes: 7 additions & 1 deletion test/model/cryptogram_scheme_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ void main() {
final dmitriSchostakowitsch = const CryptogramScheme.german()
.cryptogramOf('D. SCH');
expect(dmitriSchostakowitsch, <Note>[.d, .e.flat, .c, .b]);

final edvardGrieg = const CryptogramScheme.french().cryptogramOf('EBG');
expect(edvardGrieg, <Note>[.e, .b, .g]);

Expand All @@ -62,6 +63,7 @@ void main() {
final johannSebastianBach = const CryptogramScheme.german()
.cryptogramOf('BACH');
expect(johannSebastianBach, <Note>[.b.flat, .a, .c, .b]);

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

Expand Down Expand Up @@ -100,11 +102,15 @@ void main() {
// ignore: prefer_const_constructors for testing
.french(),
// ignore: prefer_const_constructors for testing
CryptogramScheme(name: 'A', patterns: {'a': .c}),
// ignore: prefer_const_constructors for testing
.frenchVariant(),
// ignore: prefer_const_constructors for testing
.french(),
// ignore: prefer_const_constructors for testing
CryptogramScheme(name: 'A', patterns: {'a': .c}),
};
expect(collection.length, 2);
expect(collection.length, 3);
});
});
});
Expand Down
2 changes: 2 additions & 0 deletions test/model/main.dart
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
import 'alphabet_test.dart' as alphabet_test;
import 'cryptogram_scheme_test.dart' as cryptogram_scheme_test;
import 'name_test.dart' as name_test;
import 'note_name_test.dart' as note_name_test;

void main() {
alphabet_test.main();
cryptogram_scheme_test.main();
name_test.main();
note_name_test.main();
}
Loading