Example #1
0
def _read_tab_file_path(chords_from_tab_file_path: str, alphabet: ChordAlphabet) -> (int, np.array, np.array, np.array):
    """
    Load chord information from chords_from_tab_file_path

    :param chords_from_tab_file_path: File that contains chord information
    :return: (nr_of_chords_in_tab, chord_ids, is_first_in_line, is_last_in_line)
    """
    # Load .txt file consisting of: [line_nr, segment_nr, system_nr, chord_x, chord_str] (UntimedChordSequence)
    untimed_chord_sequence = read_untimed_chord_sequence(chords_from_tab_file_path)
    nr_of_chords_in_tab = len(untimed_chord_sequence.untimed_chord_sequence_item_items)
    # If we found less than 5 chords, we will not use this tab
    if nr_of_chords_in_tab < 5:
        return nr_of_chords_in_tab, [], [], []

    # Chord id's
    line_nrs = [ucs_item.line_nr for ucs_item in untimed_chord_sequence.untimed_chord_sequence_item_items]
    chord_ids = np.zeros(nr_of_chords_in_tab).astype(int)
    for i in range(nr_of_chords_in_tab):
        chord_ids[i] = alphabet.get_index_of_chord_in_alphabet(
            Chord.from_harte_chord_string(untimed_chord_sequence.untimed_chord_sequence_item_items[i].chord_str))

    # Array: is this chord first and/or last in its line?
    is_first_in_line = np.zeros(nr_of_chords_in_tab).astype(int)
    is_first_in_line[0] = 1
    for i in range(1, nr_of_chords_in_tab):
        if line_nrs[i] != line_nrs[i - 1]:
            is_first_in_line[i] = 1
    is_last_in_line = np.zeros(nr_of_chords_in_tab).astype(int)
    is_last_in_line[-1] = 1
    for i in range(nr_of_chords_in_tab - 1):
        if line_nrs[i] != line_nrs[i + 1]:
            is_last_in_line[i] = 1

    return nr_of_chords_in_tab, chord_ids, is_first_in_line, is_last_in_line
Example #2
0
    def _add_chord_from_str(self, chord_str: str, chord_x: int):
        """
        Find the Chord from chord_str and add it to self.chords

        :param chord_str: String of the Chord
        :param chord_x: Integer index of the Chord
        """
        chord = Chord.from_common_tab_notation_string(chord_str)
        if chord is not None:
            self.chords.append((chord_x, chord))
Example #3
0
def _chord_label_to_chord_str(chord_label: int, alphabet: ChordAlphabet) -> str:
    """
    Translate the integer chord label to a chord string

    :param chord_label: Chord index in the chord_vocabulary (integer)
    :return: Chord string (str)
    """
    if chord_label == 0:
        return 'N'
    return str(Chord.from_common_tab_notation_string(alphabet.alphabet_list[chord_label]))
Example #4
0
def _write_final_labels(final_labels, lab_path, alphabet):
    """
    Write the row of final labels (after data fusion) from the chord matrix to a .json file at lab_path
    :param final_labels: The row of final labels after data fusion
    :param lab_path: The path to write the final .json file to
    :param alphabet: The chord vocabulary (used to translate chord indices to chord strings)
    """
    # First, we fill the write_labels list with (start_time, end_time, chord_string) 3-tuples
    write_labels = []
    current_beat = 1
    final_list = []
    start_time = -1
    last_chord = ''
    last_added = True
    for i in range(len(final_labels)):
        if final_labels[i] == last_chord:
            last_added = False
        else:
            if not last_added:
                # Write previous chord
                write_labels.append((start_time, float(i) / 100, last_chord))
            # Set parameters for next
            start_time = float(i) / 100
            last_chord = final_labels[i]
    if not last_added:
        # The last chord has not been added yet, so we do it now
        write_labels.append(
            (start_time, float(len(final_labels) - 1) / 100, last_chord))

    # Now write the chords to the .json file in .json format
    with open(lab_path, 'w') as write_file:
        for write_label in write_labels:
            chord_string = str(
                Chord.from_common_tab_notation_string(
                    alphabet[write_label[2]]))
            if chord_string == 'None':
                chord_string = 'N'
            dictionary = {}
            dictionary['current_beat'] = current_beat
            dictionary['current_beat_time'] = write_label[0]
            dictionary['estimated_chord'] = chord_string
            final_list.append(dictionary)
            current_beat += 1
        json.dump(final_list, write_file, indent=4)
Example #5
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))
Example #6
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
Example #7
0
def _write_final_labels(final_labels, lab_path, alphabet):
    """
    Write the row of final labels (after data fusion) from the chord matrix to a .lab file at lab_path

    :param final_labels: The row of final labels after data fusion
    :param lab_path: The path to write the final lab file to
    :param alphabet: The chord vocabulary (used to translate chord indices to chord strings)
    """

    # First, we fill the write_labels list with (start_time, end_time, chord_string) 3-tuples
    write_labels = []
    start_time = -1
    last_chord = ''
    last_added = True
    for i in range(len(final_labels)):
        if final_labels[i] == last_chord:
            last_added = False
        else:
            if not last_added:
                # Write previous chord
                write_labels.append((start_time, float(i) / 100, last_chord))
            # Set parameters for next
            start_time = float(i) / 100
            last_chord = final_labels[i]
    if not last_added:
        # The last chord has not been added yet, so we do it now
        write_labels.append(
            (start_time, float(len(final_labels) - 1) / 100, last_chord))

    # Now write the chords to the .lab file in Harte format
    with open(lab_path, 'w') as write_file:
        for write_label in write_labels:
            chord_string = str(
                Chord.from_common_tab_notation_string(
                    alphabet[write_label[2]]))
            if chord_string == 'None':
                chord_string = 'N'
            write_file.write('{0} {1} {2}\n'.format(str(write_label[0]),
                                                    str(write_label[1]),
                                                    chord_string))
Example #8
0
    def _load_lab_file_into_chord_matrix(self, lab_path: str, i: int):
        """
        Load a chord file (in Harte's chord annotation format) into a chord matrix.

        :param lab_path: Path to the .lab file with chord annotation/estimation
        :param i: Index to the row in the chord_matrix to fill
        """
        with open(lab_path, 'r') as read_file:
            # Read chord annotation from file
            chord_annotation = read_file.readlines()
            chord_annotation = [x.rstrip().split() for x in chord_annotation]
            for y in chord_annotation:
                # Parse start and end time, retrieve index of chord
                start_time, end_time = float(y[0]), float(y[1])
                chord = Chord.from_harte_chord_string(y[2])
                chord_label_alphabet_index = self._chord_alphabet.get_index_of_chord_in_alphabet(
                    chord)

                # Add chord index to each entry in the chord_matrix that is between start and end time
                for s in range(
                        int(start_time * 100),
                        min(int(end_time * 100), self._nr_of_samples - 1)):
                    self.array[i, s] = chord_label_alphabet_index
Example #9
0
def train(chord_vocabulary: ChordVocabulary,
          train_songs: Dict[int, Song]) -> HMMParameters:
    """
    Train the HMM parameters on training_set for the given chords_list vocabulary

    :param chord_vocabulary: List of chords in our vocabulary
    :param train_songs: Set of songs for training
    :return: HMM Parameters
    """
    # Convert the vocabulary to a ChordAlphabet
    alphabet = ChordAlphabet(chord_vocabulary)
    alphabet_size = len(alphabet.alphabet_list)

    # Initialize chord_beat_matrix_per_chord: a list with |chord_vocabulary| x |beats| list for each chord
    chroma_beat_matrix_per_chord = [[] for _ in alphabet.alphabet_list]

    # Initialize transition_matrix and init_matrix
    trans = np.ones((alphabet_size, alphabet_size))
    init = np.ones(alphabet_size)

    # Iterate over the songs; fill chroma_beat_matrix_per_chord, init_matrix and transition_matrix
    for train_song_key, train_song in train_songs.items():
        train_song.audio_features_path = filehandler.get_full_audio_features_path(
            train_song_key)
        if train_song.audio_features_path != '':
            # We have audio features and labels for this song; load them (otherwise ignore the song)
            features = np.load(train_song.audio_features_path)
            chord_index_list = []
            # Iterate over the beats, fill chroma_beat_matrix_per_chord and chord_index_list
            for frame_index in range(features.shape[0]):
                chroma = features[frame_index, 1:13].astype(float)
                chord_index = alphabet.get_index_of_chord_in_alphabet(
                    Chord.from_harte_chord_string(features[frame_index, 13]))
                chord_index_list.append(chord_index)
                chroma_beat_matrix_per_chord[chord_index].append(chroma)
            # Add first chord to init_matrix
            init[chord_index_list[0]] += 1
            # Add each chord transition to transition_matrix
            for i in range(0, len(chord_index_list) - 1):
                trans[chord_index_list[i], chord_index_list[i + 1]] += 1

    # Normalize transition and init matrices
    init = init / sum(init)
    trans = np.array([trans[i] / sum(trans[i]) for i in range(alphabet_size)])

    # Calculate mean and covariance matrices
    obs_mu = np.zeros((alphabet_size, 12))
    obs_sigma = np.zeros((alphabet_size, 12, 12))
    for i in range(alphabet_size):
        chroma_beat_matrix_per_chord[i] = np.array(
            chroma_beat_matrix_per_chord[i]).T
        obs_mu[i] = np.mean(chroma_beat_matrix_per_chord[i], axis=1)
        obs_sigma[i] = np.cov(chroma_beat_matrix_per_chord[i], ddof=0)

    # Calculate additional values so we can calculate the emission probability more easily
    twelve_log_two_pi = 12 * np.log(2 * np.pi)
    log_det_sigma = np.zeros(alphabet_size)
    sigma_inverse = np.zeros(obs_sigma.shape)
    for i in range(alphabet_size):
        log_det_sigma[i] = np.log(np.linalg.det(obs_sigma[i]))
        sigma_inverse[i] = np.mat(np.linalg.pinv(obs_sigma[i]))

    return HMMParameters(alphabet=alphabet,
                         trans=trans,
                         init=init,
                         obs_mu=obs_mu,
                         obs_sigma=obs_sigma,
                         log_det_sigma=log_det_sigma,
                         sigma_inverse=sigma_inverse,
                         twelve_log_two_pi=twelve_log_two_pi,
                         trained_on_keys=list(train_songs.keys()))