def test_github_issue_8_sloppy_midi(): prog = ChordProgression.from_midi_file( os.path.join(os.path.dirname(__file__), "test_data", "issue_8.mid")) assert prog == ChordProgression([ Chord( name="A#min", root=Note("A#", 3), intervals=Intervals(name="min", semitones=[0, 3, 7]), ), Chord( name="D#", root=Note("D#", 4), intervals=Intervals(name="", semitones=[0, 4, 7]), ), Chord( name="D#min", root=Note("D#", 4), intervals=Intervals(name="min", semitones=[0, 3, 7]), ), Chord( name="A#min/G#", root=Note("G#", 3), intervals=Intervals(name="min/b7", semitones=[0, 5, 9, 14]), ), ])
def test_explicit_octave(): assert ChordProgression.from_string("4C -- +3Fm -2G7").progression == [ Chord.from_name("+4C"), Chord.from_name("4C"), Chord.from_name("3Fm"), Chord.from_name("-2G7"), ]
def test_repetition(): assert ChordProgression.from_string("C -- Fm G7").progression == [ Chord.from_name("C"), Chord.from_name("C"), Chord.from_name("Fm"), Chord.from_name("G7"), ]
def test_longer_progression(): assert ChordProgression.from_string("C Fm C G7").progression == [ Chord.from_name("C"), Chord.from_name("Fm"), Chord.from_name("C"), Chord.from_name("G7"), ]
def test_github_issue_61_slash_chord_with_octave(): """https://github.com/jonathangjertsen/jchord/issues/61#issuecomment-777625321""" chord = Chord.from_name("5C/E") assert chord == Chord( name="C/E", root=Note("C", 5), intervals=Intervals(name="major", semitones=[-8, 0, 4, 7]), ) assert chord.bass == Note("E", 4)
def test_multiline(): assert ChordProgression.from_string("""C Fm C G7 C E7 Am G""").progression == [ Chord.from_name("C"), Chord.from_name("Fm"), Chord.from_name("C"), Chord.from_name("G7"), Chord.from_name("C"), Chord.from_name("E7"), Chord.from_name("Am"), Chord.from_name("G"), ]
def make_midi_file(chord_names, beats_per_chord, out_file): chords = [] for name in chord_names: try: chord = Chord.from_name(name) except InvalidChord: chord = ChordProgression.DUMMY_CHORD chords.append(chord) progression = ChordProgression(chords) progression.to_midi( MidiConversionSettings(filename=out_file, instrument=11, beats_per_chord=beats_per_chord))
def test_github_issue_61_progression(): """https://github.com/jonathangjertsen/jchord/issues/61#issuecomment-777575298""" prog = ChordProgression.from_string("4F -- 3Am -- 4Dm7 -- 4F --") assert prog == ChordProgression([ Chord( name="F", root=Note("F", 4), intervals=Intervals(name="major", semitones=[0, 4, 7]), ), Chord( name="F", root=Note("F", 4), intervals=Intervals(name="major", semitones=[0, 4, 7]), ), Chord( name="Am", root=Note("A", 3), intervals=Intervals(name="m", semitones=[0, 3, 7]), ), Chord( name="Am", root=Note("A", 3), intervals=Intervals(name="m", semitones=[0, 3, 7]), ), Chord( name="Dm7", root=Note("D", 4), intervals=Intervals(name="m7", semitones=[0, 3, 7, 10]), ), Chord( name="Dm7", root=Note("D", 4), intervals=Intervals(name="m7", semitones=[0, 3, 7, 10]), ), Chord( name="F", root=Note("F", 4), intervals=Intervals(name="major", semitones=[0, 4, 7]), ), Chord( name="F", root=Note("F", 4), intervals=Intervals(name="major", semitones=[0, 4, 7]), ), ])
def _string_to_progression(string: str) -> List[Chord]: string = string.strip() if string == "": return [] progression = [] for name in string.split(): name = name.strip() if name == REPETITION_SYMBOL: if not progression: raise InvalidProgression( "Can't repeat before at least one chord has been added") progression.append(progression[-1]) else: progression.append(Chord.from_name(name)) return progression
def test_chord_from_degrees(name_in, semi_out): assert Chord.from_name(name_in).semitones == semi_out
def test_chord_add_root(name_in, octave): name_then_root = Intervals.from_name(name_in).with_root(Note("A#", octave)) name_and_root = Chord.from_name(f"{octave}A#{name_in}") assert name_then_root == name_and_root
def test_semitones_to_chord_options(semitones, options): assert semitones_to_chord_name_options(semitones) == options if options: assert Chord.from_semitones(None, semitones).name == options[0] else: assert Chord.from_semitones(None, semitones).name == Chord.UNNAMED
def test_chord_from_degrees(deg_in, semi_out): assert Chord.from_degrees("unnamed", deg_in).semitones == semi_out
def from_midi_file(cls, filename: str) -> "ChordProgression": notes = read_midi_file(filename) progression = [] for chord in group_notes_to_chords(notes): progression.append(Chord.from_midi([note.note for note in chord])) return cls(progression)
def test_single_chords(): assert ChordProgression.from_string("Cm").chords() == { Chord.from_name("Cm") }
def test_chord_from_root_and_semitone(root, semitones, name): assert Chord.from_root_and_semitones(Note(root, 4), semitones).name == name
def test_chord_transpose(name_in, name_out, shift): assert Chord.from_name(name_in).transpose(shift) == Chord.from_name(name_out)
def test_chord_with_root_invalid_name(name_in): with pytest.raises(InvalidChord): assert Chord.from_name(name_in)
def test_chord_with_root(name_in, root_out, semi_out): assert Chord.from_name(name_in).semitones == semi_out assert Chord.from_name(name_in).root.name == root_out
def test_longer_chords(): assert ChordProgression.from_string("C Fm C G7").chords() == { Chord.from_name("C"), Chord.from_name("Fm"), Chord.from_name("G7"), }
def test_chord_with_root_intervals(name_in, int_out): assert Chord.from_name(name_in).interval_sequence() == int_out
def test_chord_intervals(name_in, int_out): assert Chord.from_name(name_in).intervals() == int_out
def test_chord_note_to_midi(name_in, midi): assert Chord.from_name(name_in).midi() == midi
def test_chord_repr(name_in, repr_out): assert repr(Chord.from_name(name_in)) == repr_out assert Chord.from_name(name_in) == eval(repr_out)
def test_chord_with_root_repr(name_in): assert Chord.from_name(name_in) == eval(repr(Chord.from_name(name_in)))
def test_chord_add_root(name_in, octave): name_then_root = Chord.from_name(name_in).with_root(Note("A#", octave)) name_and_root = ChordWithRoot.from_name("A#" + name_in, octave=octave) print(name_and_root._keys()) assert name_then_root == name_and_root
def test_chord_from_midi(midi, name): assert Chord.from_midi(midi).name == name
def test_chord_from_invalid_name(name_in): with pytest.raises(InvalidChord): Chord.from_name(name_in)
def test_chord_from_semitones(semi_in, semi_out): assert Chord.from_semitones("unnamed", semi_in).semitones == semi_out
def test_single_progression(): assert ChordProgression.from_string("Cm").progression == [ Chord.from_name("Cm") ]