def get_chord(self, start_degree, degrees, alteration=0, inversion=None, base_note=None, extensions=None): degrees = degrees.split(",") notes = [] for degree in degrees: note = self.notes[(start_degree + int(degree) - 1) % 7] for _ in range(abs(alteration)): if alteration < 0: note += DIMINISHED elif alteration > 0: note += AUGMENTED notes.append(note) first_note = notes[0] intervals = [] for note in notes: intervals.append(first_note // note) chord_name = Chord.get_chord_name_from_intervals(intervals) return Chord(first_note, chord_name.short, inversion=inversion, base_note=base_note, extensions=extensions)
def test_chord_notation_on_initialization(short, extended, symbol): root_note = "A4" notation1 = Chord(root_note, short) notation2 = Chord(root_note, extended) notation3 = Chord(root_note, symbol) assert notation1 == notation2 assert notation2 == notation3 assert str(notation1) == str(notation2)
def adapt_chord_to_sequencer(chord, range): low_range, high_range = range chord_data = chord.to_dict() root_note = chord_data.pop("root_note") if isinstance(chord, TheoryChord): root_note = Note.cast_from_theory(root_note) if root_note < low_range: while root_note < low_range: root_note += OCTAVE chord = Chord(root_note=root_note, **chord_data) return [note for note in chord.notes if note <= high_range]
def test_harmony_parse_degree_non_strictly(degree_name, expected_chord): harmony = Harmony(Scale("A4", "major")) assert harmony.get(degree_name, strict=False) == Chord.get_from_fullname(expected_chord)
from pytest import mark from beethoven.sequencer.chord import Chord from beethoven.sequencer.note import Note from beethoven.sequencer.utils import adapt_chord_to_sequencer, get_all_notes from beethoven.theory.scale import Scale @mark.parametrize("chord,note_range,expected", [ (Chord("C1", "maj"), "C3,C6", "C3,E3,G3"), (Chord("C2", "maj"), "C3,C6", "C3,E3,G3"), (Chord("C3", "maj"), "C4,C6", "C4,E4,G4"), (Chord("C4", "maj"), "C4,C6", "C4,E4,G4"), (Chord("C5", "maj"), "C4,C6", "C5,E5,G5"), (Chord("C6", "maj"), "C5,F6", "C6,E6"), (Chord("A4", "maj7"), "C4,C6", "A4,C#5,E5,G#5"), (Chord("A4", "maj7"), "C4,E5", "A4,C#5,E5"), ]) def test_adapt_chord_to_sequencer(chord, note_range, expected): assert (adapt_chord_to_sequencer( chord, Note.to_list(note_range)) == Note.to_list(expected)) @mark.parametrize("scale,note_range,expected", [ (Scale("C3", "major"), "C3,C4", "C3,D3,E3,F3,G3,A3,B3,C4"), (Scale("C3", "major"), "E3,E4", "E3,F3,G3,A3,B3,C4,D4,E4"), (Scale("C3", "major"), "C3,C5", "C3,D3,E3,F3,G3,A3,B3,C4,D4,E4,F4,G4,A4,B4,C5"), ]) def test_get_all_notes(scale, note_range, expected): assert (get_all_notes(scale,
def test_chord_instanciation_with_inversion_out_of_range(inversion): with raises(ValueError, match="Chord inversion out of range"): Chord("A4", "power", inversion=inversion)
def test_chord_instanciation_with_inversion(): normal = Chord("A4", "power", inversion=0) inverted = Chord("A4", "power", inversion=1) assert normal.notes == Note.to_list("A4,E5") assert inverted.notes == Note.to_list("E5,A5")
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_chord_instanciation_without_attrubutes(): with raises(ValueError, match="Chord name and root note must be set"): Chord()
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}>"
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_chord_get_from_fullname_classmethod_no_match(chord_name): assert Chord.get_from_fullname(chord_name) is None
def test_chord_get_from_fullname_classmethod(chord_name): Chord.get_from_fullname(chord_name)
def test_chord_with_wrong_chord_name(chord_name): with raises(ValueError, match="Chord name does not exists"): Chord("A4", chord_name)
('ionian', 'major'), ('aeolian', 'minor'), ('melodic minor', 'ascending melodic minor'), ]) def test_scale_notation_on_initialization(name, alt_name): root_note = "A4" notation1 = Scale(root_note, name) notation2 = Scale(root_note, alt_name) assert notation1 == notation2 assert str(notation1) == str(notation2) @mark.parametrize("start_degree_name,chord_basic,chord_seventh", [ (1, Chord("B4", "min"), Chord("B4", "min7")), (2, Chord("C#5", "min"), Chord("C#5", "min7")), (3, Chord("D5", "maj"), Chord("D5", "maj7")), (4, Chord("E5", "maj"), Chord("E5", "7")), (6, Chord("G#5", "dim"), Chord("G#5", "min7b5")), ]) def test_scale_get_chords(start_degree_name, chord_basic, chord_seventh): scale = Scale("A4", "major") assert scale.get_chord(start_degree_name, "1,3,5") == chord_basic assert scale.get_chord(start_degree_name, "1,3,5,7") == chord_seventh @mark.parametrize("start_degree_name,alteration,chord_basic,chord_seventh", [ (1, 0, Chord("B4", "min"), Chord("B4", "min7")), (1, 1, Chord("B#4", "min"), Chord("B#4", "min7")),