Beispiel #1
0
    def from_harte_chord_string(cls, chord_string):
        """
        Create a chord from a chord string in the syntax proposed in the 2005 paper 'SYMBOLIC REPRESENTATION OF
        MUSICAL CHORDS: A PROPOSED SYNTAX FOR TEXT ANNOTATIONS' by Harte et al.

        :param chord_string: Chord string in syntax proposed by Harte et al.
        :return: Chord type as specified by the chord string

        >>> Chord.from_harte_chord_string('C') == Chord.from_harte_chord_string('C:(3,5)')
        True
        >>> Chord.from_harte_chord_string('C:maj(4)') == Chord.from_harte_chord_string('C:(3,4,5)')
        True
        """
        # First find non-chord strings
        if chord_string == 'N':
            return None

        # Extract root, shorthand, degree_list and bass_degree from the chord string
        match_chord_string = re.search(
            r'(?P<root>[ABCDEFG][b#]{0,2}):?'
            r'(?P<shorthand>minmaj7|hdim7|maj7|min7|dim7|maj6|min6|maj9|min9|sus9|7|9|maj|min|dim|aug)?'
            r'(\((?P<degree_list>\*?[#b]{0,2}[0-9]{1,2}(, ?\*?[#b]{0,2}[0-9]{1,2})*)\))?'
            r'(/(?P<bass_degree>[#b]{0,2}[0-9]{1,2}))?',
            chord_string
        )

        # Any chord string needs to have a root note
        if match_chord_string.group('root') is None:
            return None
        root_note_pitch_class = PitchClass.from_harte_pitch_class(match_chord_string.group('root'))

        # Find the shorthand
        if match_chord_string.group('shorthand') is None:
            if match_chord_string.group('degree_list') is None:
                # This is a major chord
                shorthand_str = 'maj'
            else:
                # The chord type is specified by the degree list
                shorthand_str = ''
        else:
            # We use a predefined shorthand
            shorthand_str = match_chord_string.group('shorthand')

        # Find the degrees
        if match_chord_string.group('degree_list') is None:
            degree_str_list = []
        else:
            degree_str_list = match_chord_string.group('degree_list').split(',')

        # Find the bass degree
        if match_chord_string.group('bass_degree') is None:
            bass_degree = Interval(0)
        else:
            bass_degree = Interval.from_harte_interval(match_chord_string.group('bass_degree'))

        # Return the chord
        return cls.from_shorthand_degree_bass(root_note_pitch_class, shorthand_str, degree_str_list, bass_degree)
Beispiel #2
0
    def _midi_pitch_to_harte_class(self):
        """
        Get the harte class from the midi pitch of our Pitch object

        :return: Harte class from the midi pitch of our Pitch object

        >>> Pitch(69)._midi_pitch_to_harte_class()
        ['G##', 'A', 'Bbb']
        """
        return PitchClass(self.midi_pitch % 12).harte_pitch_class
Beispiel #3
0
    def _midi_pitch_to_pitch_class(self):
        """
        Get the pitch class from the midi pitch of our Pitch object

        :return: Pitch class from the midi pitch of our Pitch object

        >>> str(Pitch(69)._midi_pitch_to_pitch_class())
        'A'
        """
        return PitchClass(self.midi_pitch % 12)
Beispiel #4
0
    def add_tab_block(self, tab_block_str: [Line]):
        """
        Process the content of a tab block (6 subsequent lines of LineType Tablature), add chords to self.chords

        :param tab_block_str: Six subsequent Lines of the LineType Tablature
        """
        smallest_line_length = min(
            [len(block_line_str) for block_line_str in tab_block_str])
        for chord_x in range(0, smallest_line_length):
            # Read all six strings together per x-coordinate
            finger_list = []
            for tab_block_line_str in reversed(tab_block_str):
                finger_list.append(tab_block_line_str[chord_x])
            fingers = ''.join(finger_list)

            if re.match(r'[x0-9]{6}', fingers) and fingers != 'xxxxxx':
                # A chord sounds at this x position!
                fingering = Fingering('1', fingers[0], fingers[1], fingers[2],
                                      fingers[3], fingers[4], fingers[5])
                fingering_chroma = fingering.get_extended_chroma_vector()

                # Find nearest chord from vocabulary
                smallest_distance = 2
                best_matching_chord_str = 'X'
                for chord_template in ALL_CHORDS_LIST.chord_templates:
                    cosine_distance = ssd.cosine(fingering_chroma[:12],
                                                 chord_template.chroma_list)
                    if cosine_distance < smallest_distance:
                        smallest_distance = cosine_distance
                        best_matching_chord_str = str(
                            PitchClass(
                                chord_template.key))[0] + chord_template.mode
                chord = Chord.from_common_tab_notation_string(
                    best_matching_chord_str)

                # Fix bass note
                bass_note = PitchClass(fingering_chroma[12])
                bass_interval = Interval.from_pitch_class_distances(
                    chord.root_note, bass_note)
                chord.bass_degree = bass_interval

                # Add to self.chords
                self.chords.append((chord_x, chord))
Beispiel #5
0
    def from_common_tab_notation_string(cls, chord_string):
        """
        Create a chord from a string as it is typically found in a tab file

        :param chord_string: Chord string as you typically find it in a tab file
        :return: Chord as specified by the chord string

        >>> str(Chord.from_common_tab_notation_string('C:min'))
        'None'
        >>> str(Chord.from_common_tab_notation_string('Cmaj'))
        'C'
        >>> str(Chord.from_common_tab_notation_string('Db11'))
        'C#:(2,3,4,5,b7)'
        """
        chord_parts = re.search(
            r'(?P<root_str>[ABCDEFG][b#]?)'
            r'(?P<chord_type_str>7sus4|7sus|sus2|sus4|sus|m6|6/9|6|7-5|7\+5|5|m9|maj9|add9|9|11|13|maj7|M7|maj|min7|m7'
            r'|m|dim|aug|\+|7)?'
            r'(/(?P<bass_note_str>[ABCDEFG][b#])?)?$',
            chord_string)

        # This string was not recognized as a chord
        if chord_parts is None:
            return None

        # Root note
        root_note_pitch_class = PitchClass.from_harte_pitch_class(chord_parts.group('root_str'))

        # Chord type
        chord_type_string = chord_parts.group('chord_type_str')
        if chord_type_string is None:
            chord_type_string = 'maj'

        # Bass note
        bass_note_string = chord_parts.group('bass_note_str')
        if bass_note_string is None:
            bass_interval = Interval(0)
        else:
            bass_note_pitch_class = PitchClass.from_harte_pitch_class(bass_note_string)
            bass_interval = Interval.from_pitch_class_distances(root_note_pitch_class, bass_note_pitch_class)

        return Chord.from_shorthand_degree_bass(root_note_pitch_class, chord_type_string, [], bass_interval, 'tab')
Beispiel #6
0
    def add_note(self, note: pretty_midi.Note):
        """
        Add a Note to the Event

        :param note: The Note we add to the Event
        """
        self.notes.append(note)
        self.pitches.add(Pitch(note.pitch))
        pitch_class_nr = note.pitch % 12
        self.pitch_classes.add(PitchClass(pitch_class_nr))
        self.chroma[pitch_class_nr] += (self._note_duration_ratio_in_event(note) * note.velocity)
Beispiel #7
0
    def from_pitch_name(cls, pitch_name):
        """
        Create a Pitch instance based on its pitch name (a str consisting of pitch class and octave number (e.g. 'A4')
        Note: works only with single sharp/flats; fails with double sharps/flats

        :param pitch_name: Name, consisting of pitch class and octave number
        :return: Pitch, as specified by its pitch name

        >>> Pitch.from_pitch_name('A4').midi_pitch
        69
        >>> Pitch.from_pitch_name('Bb4').midi_pitch # We can handle single sharps or flats
        70
        >>> Pitch.from_pitch_name('Bbb4')           # We cannot handle double sharps or flats
        Traceback (most recent call last):
        ...
        ValueError: invalid literal for int() with base 10: 'b4'
        """
        if 'b' in pitch_name or '#' in pitch_name:
            return cls.from_pitch_class_and_octave_number(
                PitchClass.from_harte_pitch_class(pitch_name[0:2]),
                int(pitch_name[2:]))
        return cls.from_pitch_class_and_octave_number(
            PitchClass.from_harte_pitch_class(pitch_name[0]),
            int(pitch_name[1:]))
Beispiel #8
0
    def find_most_likely_chord(self, chord_vocabulary: ChordVocabulary) -> Tuple[ChordAnnotationItem, float]:
        """
        Find the chord to which the chroma of this event matches best.

        :param chord_vocabulary: List of all chords for classification
        """
        best_matching_chord_score = -2.99
        best_matching_chord_str = 'X'
        best_key_note_weight = 0
        for chord_template in chord_vocabulary.chord_templates:
            chord_score = self._score_compared_to_template(chord_template)
            if chord_score > best_matching_chord_score or (chord_score == best_matching_chord_score and
                                                           self.chroma[chord_template.key] > best_key_note_weight):
                best_matching_chord_score = chord_score
                best_matching_chord_str = str(PitchClass(chord_template.key)) + chord_template.mode
                best_key_note_weight = self.chroma[chord_template.key]
        if best_matching_chord_str == 'X':
            most_likely_chord = None
        else:
            most_likely_chord = Chord.from_common_tab_notation_string(best_matching_chord_str)
        return ChordAnnotationItem(self.start_time, self.end_time, most_likely_chord), best_matching_chord_score
 def __init__(self, chord_vocabulary: ChordVocabulary):
     self.alphabet_list = ['N'] + [
         str(PitchClass(chord_template.key)) + chord_template.mode
         for chord_template in chord_vocabulary.chord_templates
     ]
     self.chord_vocabulary_name = chord_vocabulary.name