def _load_attributes(self, tonic, scale_name): if not isinstance(tonic, Note): tonic = Note(tonic) self.tonic = tonic if not (data := self._DIRECTORY.get(scale_name)): raise ValueError("Scale name does not exists")
def test_note_check_display_note_name_change(anglosaxon, solfege): note = Note(anglosaxon) assert str(note) == f"<Note {anglosaxon}>" NoteNameContainer.set(1) assert str(note) == f"<Note {solfege}>" NoteNameContainer.set(0)
def __call__(cls, root_note=None, chord_name=None, inversion=None, base_note=None, extensions=None): if root_note is None and chord_name is None: raise ValueError("Chord name and root note must be set") elif base_note and isinstance(base_note, str): base_note = Note(base_note) extensions = frozenset([ extension if isinstance(extension, Interval) else Interval(extension) for extension in extensions or [] ]) args = frozenset([root_note, chord_name, inversion, base_note, extensions]) if args not in cls._INSTANCES: instance = super().__call__(root_note, chord_name, inversion, base_note, extensions) cls._INSTANCES[args] = instance return cls._INSTANCES[args]
from pytest import mark from beethoven.common.tuning import Tuning from beethoven.theory.note import Note @mark.parametrize("tuning_args,expected_repr", [ ((Note("A"), ), "<Tuning 1 strings>"), (Note.to_list("A,B,C"), "<Tuning 3 strings>"), (Note.to_list("E,A,D,G,B,E"), "<Tuning 6 strings>"), ]) def test_tuning_instanciation(tuning_args, expected_repr): tuning = Tuning(*tuning_args) assert repr(tuning) == expected_repr
def test_chord_instanciation_with_inversion(): normal = Chord("A", "power", inversion=0) inverted = Chord("A", "power", inversion=1) assert normal.notes == Note.to_list("A,E") assert inverted.notes == Note.to_list("E,A")
def test_chord_instanciation(root_note, chord_name, result_notes): chord = Chord(root_note, chord_name) assert chord.notes == Note.to_list(result_notes)
def test_note_add_interval_with_non_interval_string(): with raises(ValueError, match="Interval name does not exists"): Note("A").add_interval("test")
def test_chord_instanciation_with_base_note(root_note, chord_name, base_note, expected_str): chord = Chord(root_note, chord_name, base_note=base_note) assert chord.base_note == Note(base_note) assert repr(chord) == f"<Chord {expected_str}>"
from beethoven.theory.note import Note class Tuning: def __init__(self, *strings): self.strings = list(strings) def __str__(self): return f"<Tuning {len(self.strings)} strings>" def __repr__(self): return str(self) @classmethod def from_notes_str(cls, string): return cls(*Note.to_list(string)) E_STANDARD = Tuning(*Note.to_list("E,A,D,G,B,E")) E_STANDARD_BASS = Tuning(*Note.to_list("E,A,D,G")) B_STANDARD_7 = Tuning(*Note.to_list("B,E,A,D,G,B,E"))
def test_note_instanciation_through_to_dict(): note = Note("A") assert note == Note(**note.to_dict())
def test_note_semitones_property(note_name, expected_semitones): assert Note(note_name).semitones == expected_semitones
def test_note_comparison(): assert Note("A") == Note("A") assert Note("A") < Note("B") assert Note("B") > Note("C")
def test_note_is_the_same_between_notation_systems_with_sharps( anglosaxon, solfege): note_a, note_s = Note(anglosaxon), Note(solfege) assert note_a == note_s assert note_a is not None
def test_note_with_sharps_and_flats_raise_exception(note_name): with raises(ValueError, match="Note name shouldn't contain sharps AND flats"): Note(note_name)
def test_note_with_wrong_note_name(note_name): with raises(ValueError, match="Note name does not exists"): Note(note_name)
def test_note_with_empty_note_name(): with raises(ValueError, match="Note name does not exists"): Note("")
def test_note_instanciation_without_attributes(): with raises(ValueError, match="Note name must be set"): Note()
from pytest import mark from beethoven.common.tuning import (B_STANDARD_7, E_STANDARD, E_STANDARD_BASS, Tuning) from beethoven.display.fretboard import Fretboard from beethoven.theory.chord import Chord from beethoven.theory.note import Note from beethoven.theory.scale import Scale @mark.parametrize( "tuning,fretboard_kwargs,call_kwargs,expected_ascii", [ # Test without any scale/chord (Tuning(Note("A")), {}, {}, (" ║ | | | | | | | | | | | |\n" "\n" " 0 ║ | | 3 | | 5 | | 7 | | 9 | | | 12 |" )), # Test with a scale (Tuning(Note("A")), {}, { "scale": Scale("A", "major") }, (" A ║ | B | | C# | D | | E | | F# | | G# | A |\n" "\n" " 0 ║ | | 3 | | 5 | | 7 | | 9 | | | 12 |" )), # Test with a chord (Tuning(Note("A")), {}, { "chord": Chord("A", "maj7") },
def validate_note_or_degree(string, loc, expr): try: Note(expr[0]) except Exception: Harmony(Scale("A", "major")).get(expr[0])
def process_config(parsed_config, current_scale=None): parsed = {} scale_updated = False scale_name = None tonic_note = None if current_scale: scale_name = str(current_scale.name) tonic_note = current_scale.tonic if scale_name_value := parsed_config.get("scale"): scale_name = scale_name_value scale_updated = True if tonic_name := parsed_config.get("note"): tonic_note = Note(tonic_name) scale_updated = True if scale_updated and scale_name and tonic_note: parsed["scale"] = Scale(tonic_note, scale_name) elif not current_scale: return parsed if progression := parsed_config.get("progression"): parsed["progression"] = progression if time_signature := parsed_config.get("time_signature"): parsed["time_signature"] = TimeSignature(*time_signature) if tempo := parsed_config.get("tempo"): parsed["tempo"] = Tempo(tempo)
def _load_attributes(self, root_note, chord_name, inversion, base_note, extensions): if not isinstance(root_note, Note): root_note = Note(root_note) if not (data := self._DIRECTORY.get(chord_name)): raise ValueError("Chord name does not exists")
def from_notes_str(cls, string): return cls(*Note.to_list(string))
def test_note_add_interval_alteration_normalization(): assert Note("A") + Interval("1aaaaaaaaaaa") == Note("Ab") assert Note("A") + Interval("1ddddddddddd") == Note("A#")
def validate_note(string, loc, expr): Note(expr[0])
def test_note_add_interval_method(note_name, interval_name, result_note_name): assert Note(note_name) + Interval(interval_name) == Note(result_note_name)
def test_scale(root_note, scale_name, result_notes): scale = Scale(root_note, scale_name) assert scale.notes == Note.to_list(result_notes)
def test_note_substract_interval_method(note_name, interval_name, result_note_name): assert Note(note_name) - Interval(interval_name) == Note(result_note_name)
assert chord.base_note == Note(base_note) assert repr(chord) == f"<Chord {expected_str}>" def test_chord_instanciation_with_inversion(): normal = Chord("A", "power", inversion=0) inverted = Chord("A", "power", inversion=1) assert normal.notes == Note.to_list("A,E") assert inverted.notes == Note.to_list("E,A") @mark.parametrize( "root_note,chord_name,extensions,expected_str,expected_notes", [("A", "power", ("5", ), "Apower", Note.to_list("A,E")), ("A", "power", ("4", ), "Apower add4", Note.to_list("A,E,D")), ("B", "min", ("9a", ), "Bmin add9a", Note.to_list("B,D,F#,C##")), ("D", "maj7", ("11", "13d"), "Dmaj7 add11add13d", Note.to_list("D,F#,A,C#,G,Bbb")), ("F", "min maj7", ("14dd", ), "Fmin maj7 add14dd", Note.to_list("F,Ab,C,E,Ebbb"))]) def test_chord_instanciation_with_extensions(root_note, chord_name, extensions, expected_str, expected_notes): chord = Chord(root_note, chord_name, extensions=extensions) assert repr(chord) == f"<Chord {expected_str}>" assert chord.notes == expected_notes
def test_note_add_interval_with_non_interval_instance(): assert Note("A").add_interval("3") == Note("C#") assert Note("G").add_interval("5", reverse=True) == Note("C")