예제 #1
0
 def __init__(self, nr_of_representations: int, nr_of_samples: int,
              chord_vocabulary: ChordVocabulary):
     self.array = np.zeros((nr_of_representations, nr_of_samples),
                           dtype=int)
     self._first_empty_row = 0
     self._nr_of_representations = nr_of_representations
     self._nr_of_samples = nr_of_samples
     self._chord_alphabet = ChordAlphabet(chord_vocabulary)
예제 #2
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
예제 #3
0
def data_fuse_song_with_actual_best_midi_and_tab(
        song: Song, chord_vocabulary: ChordVocabulary):
    """
    Data fuse a song using all combinations of selection and combination methods, write the final labels to .lab files

    :param song: The song on which we want to apply data fusion
    :param chord_vocabulary: The chord vocabulary
    """
    # Check if data fusion has already been calculated  TODO: make this check more robust
    if path.isfile(
            filehandler.get_data_fusion_path(song.key, 'df', 'actual-best',
                                             'CHF_2017')):
        return

    # Get list of audio lab files
    audio_labs = song.full_mirex_chord_lab_paths
    audio_labs['CHF_2017'] = song.full_chordify_chord_labs_path

    # Sample every 10ms, so 100 samples per second
    song_duration = song.duration
    nr_of_samples = int(ceil(song_duration * 100))

    # Turn the chords list (a list of (key, mode-str, chroma-list) tuples) into an chord_vocabulary (a list of strings)
    alphabet = ChordAlphabet(chord_vocabulary)

    selection_name = 'actual-best'
    lab_list = [get_actual_best_tab_lab(song)]
    # lab_list = [get_actual_best_midi_lab(song), get_actual_best_tab_lab(song)]

    # Fill a numpy array with chord labels for each of the lab files
    chord_matrix = np.zeros((len(lab_list) + 1, nr_of_samples), dtype=int)
    for lab_nr in range(len(lab_list)):
        load_lab_file_into_chord_matrix(lab_list[lab_nr], lab_nr, chord_matrix,
                                        alphabet, nr_of_samples)

    # Iterate over the audio types:
    for audio_name, audio_lab in audio_labs.items():
        if filehandler.file_exists(audio_lab):
            # Add the lab file to our chord matrix
            load_lab_file_into_chord_matrix(audio_lab, len(lab_list),
                                            chord_matrix, alphabet,
                                            nr_of_samples)
            final_labels_data_fusion = _data_fusion_chord_label_combination(
                chord_matrix, nr_of_samples, alphabet)
            _write_final_labels(
                final_labels_data_fusion,
                filehandler.get_data_fusion_path(song.key, 'df',
                                                 selection_name, audio_name),
                alphabet)
예제 #4
0
class ChordMatrix:
    def __init__(self, nr_of_representations: int, nr_of_samples: int,
                 chord_vocabulary: ChordVocabulary):
        self.array = np.zeros((nr_of_representations, nr_of_samples),
                              dtype=int)
        self._first_empty_row = 0
        self._nr_of_representations = nr_of_representations
        self._nr_of_samples = nr_of_samples
        self._chord_alphabet = ChordAlphabet(chord_vocabulary)

    def append_lab_file(self, lab_path: str):
        if self._first_empty_row < self._nr_of_representations:
            self._load_lab_file_into_chord_matrix(lab_path,
                                                  self._first_empty_row)
            self._first_empty_row += 1
        else:
            raise IndexError(
                'Index out of bounds; the chord matrix seems to be full already.'
            )

    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
예제 #5
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()))
def export_result_image(song: Song,
                        chords_vocabulary: ChordVocabulary,
                        midi: bool = True,
                        tab: bool = True,
                        audio: str = 'CHF_2017',
                        df: bool = True):
    """
    Export visualisation to a png file.

    :param song: Song for which we want to export the visualisation
    :param chords_vocabulary: Chord vocabulary
    :param midi: Show MIDI files?
    :param tab: Show Tab files?
    :param audio: Audio ACE method
    :param df: Show all DF results?
    """
    if filehandler.file_exists(
            filehandler.get_lab_visualisation_path(song, audio)):
        return song.title + " was already visualised for the ACE method " + audio + "."

    nr_of_samples = int(ceil(song.duration * 100))
    alphabet = ChordAlphabet(chords_vocabulary)

    # Select labs based on parameter setting
    label_data = [{
        'name': 'Ground truth',
        'index': 0,
        'lab_path': song.full_ground_truth_chord_labs_path,
        'csr': 1.0,
        'ovs': 1.0,
        'uns': 1.0,
        'seg': 1.0
    }]
    i = 1
    best_indices = []  # For expected best MIDI and tab
    if midi:
        duplicate_midis = filehandler.find_duplicate_midis(song)
        best_midi_name, best_segmentation = data_fusion.get_expected_best_midi(
            song)
        full_midi_paths = song.full_midi_paths
        full_midi_paths.sort()
        for full_midi_path in full_midi_paths:
            midi_name = filehandler.get_file_name_from_full_path(
                full_midi_path)
            for segmentation_method in ['bar', 'beat']:
                full_midi_chords_path = filehandler.get_full_midi_chord_labs_path(
                    midi_name, segmentation_method)
                if filehandler.file_exists(full_midi_chords_path) \
                        and midi_name not in duplicate_midis:
                    # Evaluate song
                    csr, ovs, uns, seg = evaluate(
                        song.full_ground_truth_chord_labs_path,
                        full_midi_chords_path)
                    # Save evaluation values to label_data
                    label_data.append({
                        'name':
                        'MIDI ' + midi_name + ' | ' + segmentation_method,
                        'index': i,
                        'lab_path': full_midi_chords_path,
                        'csr': csr,
                        'ovs': ovs,
                        'uns': uns,
                        'seg': seg
                    })
                    # Check if this is the expected best MIDI & segmentation method for this song
                    if midi_name == best_midi_name and segmentation_method == best_segmentation:
                        best_indices.append(i)
                    i += 1

    if tab:
        best_tab = data_fusion.get_expected_best_tab_lab(song)
        for tab_counter, full_tab_path in enumerate(song.full_tab_paths, 1):
            tab_chord_labs_path = filehandler.get_full_tab_chord_labs_path(
                full_tab_path)
            if filehandler.file_exists(tab_chord_labs_path):
                # Evaluate song
                csr, ovs, uns, seg = evaluate(
                    song.full_ground_truth_chord_labs_path,
                    tab_chord_labs_path)
                # Save evaluation values to label_data
                label_data.append({
                    'name': 'Tab ' + str(tab_counter),
                    'index': i,
                    'lab_path': tab_chord_labs_path,
                    'csr': csr,
                    'ovs': ovs,
                    'uns': uns,
                    'seg': seg
                })
                if tab_chord_labs_path == best_tab:
                    best_indices.append(i)
                i += 1
    if df:
        csr, ovs, uns, seg = evaluate(
            song.full_ground_truth_chord_labs_path,
            filehandler.get_full_mirex_chord_labs_path(song, audio))
        label_data.append({
            'name':
            audio,
            'index':
            i,
            'lab_path':
            filehandler.get_full_mirex_chord_labs_path(song, audio),
            'csr':
            csr,
            'ovs':
            ovs,
            'uns':
            uns,
            'seg':
            seg
        })

        for selection_name in 'all', 'best':
            for combination_name in 'rnd', 'mv', 'df':
                df_lab_path = filehandler.get_data_fusion_path(
                    song.key, combination_name, selection_name, audio)
                csr, ovs, uns, seg = evaluate(
                    song.full_ground_truth_chord_labs_path, df_lab_path)
                label_data.append({
                    'name':
                    audio + '-' + combination_name.upper() + '-' +
                    selection_name.upper(),
                    'index':
                    i,
                    'lab_path':
                    df_lab_path,
                    'csr':
                    csr,
                    'ovs':
                    ovs,
                    'uns':
                    uns,
                    'seg':
                    seg
                })

    # Fill a numpy array with chord labels for each of the lab files
    chord_matrix = np.zeros((len(label_data), nr_of_samples), dtype=int)
    for lab_nr in range(len(label_data)):
        data_fusion.load_lab_file_into_chord_matrix(
            label_data[lab_nr]['lab_path'], lab_nr, chord_matrix, alphabet,
            nr_of_samples)

    all_chords = [chord_matrix[x] for x in range(len(label_data))]

    # Find names
    names = [label_dict['name'] for label_dict in label_data]

    # Find results
    results = ['CSR  OvS  UnS  Seg']
    for label_dict in label_data[1:]:
        results.append(' '.join([
            str(round(label_dict[measure], 2)).ljust(4, '0')
            for measure in ['csr', 'ovs', 'uns', 'seg']
        ]))

    # Show result
    plt1 = _show_chord_sequences(song, all_chords, best_indices, names,
                                 results, alphabet)

    plt1.savefig(filehandler.get_lab_visualisation_path(song, audio),
                 bbox_inches="tight",
                 pad_inches=0)

    return song.title + " was visualised for the ACE method " + audio + "."
예제 #7
0
def data_fuse_song(song: Song, chord_vocabulary: ChordVocabulary):
    """
    Data fuse a song using all combinations of selection and combination methods, write the final labels to .lab files

    :param song: The song on which we want to apply data fusion
    :param chord_vocabulary: The chord vocabulary
    """
    # Check if data fusion has already been calculated  TODO: make this check more robust
    if path.isfile(
            filehandler.get_data_fusion_path(song.key, 'df', 'besttab',
                                             'CHF_2017')):
        return

    # Get list of symbolic lab files (all / expected best)
    # well_aligned_midis = get_well_aligned_midis(song)
    # all_symbolic_lab_paths = \
    #     [filehandler.get_full_midi_chord_labs_path(wam, 'bar') for wam in well_aligned_midis] + \
    #     [filehandler.get_full_midi_chord_labs_path(wam, 'beat') for wam in well_aligned_midis] + \
    #     [filehandler.get_full_tab_chord_labs_path(t) for t in song.full_tab_paths]
    # expected_best_symbolic_lab_paths = []
    # if well_aligned_midis:
    #     expected_best_symbolic_lab_paths.append(
    #         filehandler.get_full_midi_chord_labs_path(*get_expected_best_midi(song)))
    # if [filehandler.get_full_tab_chord_labs_path(t) for t in song.full_tab_paths]:
    #     expected_best_symbolic_lab_paths.append(
    #         filehandler.get_full_tab_chord_labs_path(get_expected_best_tab_lab(song)))

    well_aligned_midis = get_well_aligned_midis(song)
    all_midi_bar_lab_paths = [
        filehandler.get_full_midi_chord_labs_path(wam, 'bar')
        for wam in well_aligned_midis if filehandler.file_exists(
            filehandler.get_full_midi_chord_labs_path(wam, 'bar'))
    ]
    all_midi_beat_lab_paths = [
        filehandler.get_full_midi_chord_labs_path(wam, 'beat')
        for wam in well_aligned_midis if filehandler.file_exists(
            filehandler.get_full_midi_chord_labs_path(wam, 'beat'))
    ]
    all_midi_lab_paths = all_midi_bar_lab_paths + all_midi_beat_lab_paths
    all_tab_lab_paths = [
        filehandler.get_full_tab_chord_labs_path(t)
        for t in song.full_tab_paths
        if filehandler.file_exists(filehandler.get_full_tab_chord_labs_path(t))
    ]
    all_audio_lab_paths = {
        **song.full_mirex_chord_lab_paths,
        **{
            'CHF_2017': song.full_chordify_chord_labs_path
        }
    }

    # expected_best_symbolic_lab_paths = []
    # if well_aligned_midis:
    #     expected_best_symbolic_lab_paths.append(
    #         filehandler.get_full_midi_chord_labs_path(*get_expected_best_midi(song)))
    # if [filehandler.get_full_tab_chord_labs_path(t) for t in song.full_tab_paths]:
    #     expected_best_symbolic_lab_paths.append(
    #         filehandler.get_full_tab_chord_labs_path(get_expected_best_tab_lab(song)))

    expected_best_midi_lab_paths = []
    if well_aligned_midis:
        expected_best_midi_lab_paths.append(
            filehandler.get_full_midi_chord_labs_path(
                *get_expected_best_midi(song)))
    expected_best_tab_lab_paths = []
    if [
            filehandler.get_full_tab_chord_labs_path(t)
            for t in song.full_tab_paths
    ]:
        expected_best_tab_lab_paths.append(
            filehandler.get_full_tab_chord_labs_path(
                get_expected_best_tab_lab(song)))

    all_symbolic_lab_paths = all_midi_lab_paths + all_tab_lab_paths
    expected_best_symbolic_lab_paths = expected_best_midi_lab_paths + expected_best_tab_lab_paths

    # # Remove non-existing files (e.g. tab files in which too little chords were observed)
    # all_symbolic_lab_paths = [lab for lab in all_symbolic_lab_paths if filehandler.file_exists(lab)]
    # expected_best_symbolic_lab_paths = [lab for lab in expected_best_symbolic_lab_paths if filehandler.file_exists(lab)]

    # Get list of audio lab files
    # audio_labs = song.full_mirex_chord_lab_paths
    # audio_labs['CHF_2017'] = song.full_chordify_chord_labs_path

    # Sample every 10ms, so 100 samples per second
    song_duration = song.duration
    nr_of_samples = int(ceil(song_duration * 100))

    # Turn the chords list (a list of (key, mode-str, chroma-list) tuples) into an chord_vocabulary (a list of strings)
    alphabet = ChordAlphabet(chord_vocabulary)

    selection_dict = {
        'all': all_symbolic_lab_paths,
        'best': expected_best_symbolic_lab_paths,
        'allmidi': all_midi_lab_paths,
        'bestmidi': expected_best_midi_lab_paths,
        'alltab': all_tab_lab_paths,
        'besttab': expected_best_tab_lab_paths
    }

    # Iterate over the two types of selection (all / best)
    # for lab_list_i in [0, 1]:
    #     lab_list = [all_symbolic_lab_paths, expected_best_symbolic_lab_paths,
    #                 all_midi_lab_paths, expected_best_midi_lab_paths,
    #                 all_tab_lab_paths, expected_best_tab_lab_paths][lab_list_i]
    #     lab_list = [i for i in lab_list if i != '']
    # selection_name = ['all', 'best', 'allmidi', 'bestmidi', 'alltab', 'besttab'][lab_list_i]

    for selection_name, lab_list in selection_dict.items():
        lab_list = [i for i in lab_list if i != '']

        # Fill a numpy array with chord labels for each of the lab files
        chord_matrix = np.zeros((len(lab_list) + 1, nr_of_samples), dtype=int)
        for lab_nr in range(len(lab_list)):
            load_lab_file_into_chord_matrix(lab_list[lab_nr], lab_nr,
                                            chord_matrix, alphabet,
                                            nr_of_samples)

        # Iterate over the audio types:
        for audio_name, audio_lab in all_audio_lab_paths.items():
            if filehandler.file_exists(audio_lab):
                if any([
                        not filehandler.file_exists(
                            filehandler.get_data_fusion_path(
                                song.key, df_type_str, selection_name,
                                audio_name))
                        for df_type_str in ['rnd', 'mv', 'df']
                ]):
                    # Add the lab file to our chord matrix
                    load_lab_file_into_chord_matrix(audio_lab, len(lab_list),
                                                    chord_matrix, alphabet,
                                                    nr_of_samples)

                    # Iterate over the three combination types; calculate labels and write them:
                    if not filehandler.file_exists(
                            filehandler.get_data_fusion_path(
                                song.key, 'rnd', selection_name, audio_name)):
                        final_labels_random = _random_chord_label_combination(
                            chord_matrix, nr_of_samples)
                        _write_final_labels(
                            final_labels_random,
                            filehandler.get_data_fusion_path(
                                song.key, 'rnd', selection_name, audio_name),
                            alphabet)

                    if not filehandler.file_exists(
                            filehandler.get_data_fusion_path(
                                song.key, 'mv', selection_name, audio_name)):
                        final_labels_majority = _majority_vote_chord_label_combination(
                            chord_matrix, nr_of_samples, alphabet)
                        _write_final_labels(
                            final_labels_majority,
                            filehandler.get_data_fusion_path(
                                song.key, 'mv', selection_name, audio_name),
                            alphabet)

                    if not filehandler.file_exists(
                            filehandler.get_data_fusion_path(
                                song.key, 'df', selection_name, audio_name)):
                        final_labels_data_fusion = _data_fusion_chord_label_combination(
                            chord_matrix, nr_of_samples, alphabet)
                        _write_final_labels(
                            final_labels_data_fusion,
                            filehandler.get_data_fusion_path(
                                song.key, 'df', selection_name, audio_name),
                            alphabet)