def _get_interval_between_notes(cls, note1, note2): c_index = cls._SYSTEM_DIRECTORY["C"][1] self_index = cls._SYSTEM_DIRECTORY[note1.note_name.system][1] other_index = cls._SYSTEM_DIRECTORY[note2.note_name.system][1] l1 = (self_index - c_index) % 12 l2 = (other_index - c_index) % 12 d1 = Interval.get_interval_degree(l1) d2 = Interval.get_interval_degree(l2) degree = (d2 - d1) % 7 interval_st = Interval.get_interval_semitones(degree) ll = interval_st alt_count = ((l2 - l1) + (note2.alteration - note1.alteration) % 12) - ll if alt_count >= 6: alt_count -= 12 if alt_count <= -6: alt_count += 12 symbol = cls._INTERVAL._get_alteration_symbols(degree + 1, alt_count) return cls._INTERVAL(f"{degree + 1}{symbol}")
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): try: base_note = Note(base_note) except Exception: base_note = TheoryNote(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]
def load(cls, mappings): for mapping in mappings: interval_names, *names = mapping names_instance = ChordNameContainer(names) intervals = [] for interval_name in interval_names.split(","): intervals.append(Interval(interval_name)) for name in names: cls._DIRECTORY[name] = (names_instance, intervals) cls._REVERSE_DIRECTORY[interval_names] = names_instance
def load(cls, mappings): for mapping in mappings: interval_names, mode_name, *names = mapping names_instance = ScaleNameContainer(names) intervals = [ Interval(interval_name) for interval_name in interval_names.split(",") ] for name in names: cls._DIRECTORY[name] = (names_instance, mode_name, intervals) if mode_name: cls._MODES_DIRECTORY[mode_name].append(names[0])
def test_interval_initialization_with_a_major_and_minor_perfect_intervals( interval_name, error_msg): with raises(ValueError, match=error_msg): Interval(interval_name)
def test_interval_check_display(interval_name): assert str(Interval( interval_name)) == f"<Interval {interval_name.replace('M', '')}>"
def test_interval_initialization_with_a_non_existing_interval_name( interval_name): with raises(ValueError, match="Interval name does not exists"): Interval(interval_name)
def test_interval_with_multiple_alterations(interval_name): msg = "Interval alteration should be of a single type M, m, a or d" with raises(ValueError, match=msg): Interval(interval_name)
def test_interval_with_wrong_interval_name(interval_name): with raises(ValueError, match="Interval name does not exists"): Interval(interval_name)
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_interval_instanciation_through_to_dict(): interval = Interval("5") assert interval == Interval(**interval.to_dict())
def test_note_instanciation_without_attributes(): with raises(ValueError, match="Interval name must be set"): Interval()
def test_interval_check_fullname_display(interval_name, interval_display): IntervalNameContainer.set(1) assert str(Interval(interval_name)) == f"<Interval {interval_display}>" IntervalNameContainer.set(0)
if inversion := parsed_config.get("inversion"): pass # GET BASE NOTE OR INVERSION base_note = None base_degree = None if base := parsed_config.get("base"): try: base_note = Note(base) except (ValueError, AttributeError): # base_degree_interval = harmony.get_base_degree_interval(raw_data) base_degree = base if extensions := parsed_config.get("extensions"): extensions = {Interval(e) for e in extensions} if not (chord := Harmony(current_scale).get( parsed_config.get("chord"), inversion=inversion, base_note=base_note, base_degree=base_degree, extensions=extensions, strict=state.prompt_config.get("strict") )): chord = Chord.get_from_fullname( parsed_config.get("chord"), inversion=inversion, base_note=base_note, extensions=extensions )
def test_note_substract_interval_method(note_name, interval_name, result_note_name): assert Note(note_name) - Interval(interval_name) == Note(result_note_name)
def test_interval_is_hashable(): { Interval("1"): "unisson", Interval("8"): "octave", }
def test_interval_comparison(): assert Interval("1") <= Interval("1") assert Interval("1") < Interval("3")
def test_interval_with_minor_alteration_on_perfect_interval(interval_name): with raises(ValueError, match="Minor alteration on perfect interval is not possible"): Interval(interval_name)
def test_interval_with_implicit_major(interval_name): assert Interval(interval_name) == Interval(interval_name + "M")
def test_note_add_interval_alteration_normalization(): assert Note("A") + Interval("1aaaaaaaaaaa") == Note("Ab") assert Note("A") + Interval("1ddddddddddd") == Note("A#")