Esempio n. 1
0
def evaluate_midis(all_songs) -> None:
    """
    Evaluate all lab files based on MIDI alignment and chord estimation

    :param all_songs: All songs in the data set
    """
    for segmentation_type in 'bar', 'beat':
        result_csv_path = filehandler.MIDILABS_RESULTS_PATHS[segmentation_type]
        if not path.isfile(result_csv_path):
            # Results were not calculated yet
            with open(result_csv_path, 'w') as write_file:
                for song_key in all_songs:
                    song = all_songs[song_key]
                    for midi_path in song.full_midi_paths:
                        midi_name = filehandler.get_file_name_from_full_path(
                            midi_path)
                        alignment_score = \
                            decibel.import_export.midi_alignment_score_io.read_chord_alignment_score(midi_name)
                        chord_probability = filehandler.read_midi_chord_probability(
                            segmentation_type, midi_name)
                        midi_lab_path = filehandler.get_full_midi_chord_labs_path(
                            midi_name, segmentation_type)

                        # Calculate CSR and write
                        csr, overseg, underseg, seg = evaluate(
                            song.full_ground_truth_chord_labs_path,
                            midi_lab_path)
                        write_file.write(
                            '{0};{1};{2};{3};{4};{5};{6};{7};{8}\n'.format(
                                str(song_key), str(song.duration),
                                str(midi_name), str(alignment_score),
                                str(chord_probability), str(csr), str(overseg),
                                str(underseg), str(seg)))
Esempio n. 2
0
def synthesize_midi_to_wav(midi_file_path_from: str, sampling_rate: int = 22050):
    """
    Converts a midi file, specified to its path, to a waveform and writes the result as a wav file

    :param midi_file_path_from: Path to the midi file which will be converted
    :param sampling_rate: Sampling rate of the audio
    """
    midi_file_name = fh.get_file_name_from_full_path(midi_file_path_from)
    wav_file_path_to = fh.get_full_synthesized_midi_path(midi_file_name)
    midi_object = pretty_midi.PrettyMIDI(midi_file_path_from)
    midi_audio = midi_object.fluidsynth(sampling_rate, fh.SOUND_FONT_PATH)
    librosa.output.write_wav(wav_file_path_to, midi_audio, sampling_rate)
Esempio n. 3
0
def align_midi(audio_cqt: np.ndarray,
               audio_times: np.ndarray,
               full_synthesized_midi_path: str,
               full_alignment_write_path: str,
               alignment_parameters: Optional[AlignmentParameters] = None):
    """
    Align audio (specified by CQT) to synthesized MIDI (specified by path), return path and score of the alignment

    :param alignment_parameters: Parameters for alignment
    :param audio_cqt: The CQT of the audio of the alignment
    :param audio_times: Array of times of the audio (from compute_cqt function)
    :param full_synthesized_midi_path: The path to the synthesized MIDI file
    :param full_alignment_write_path: The path to write the alignment to
    """
    # Make sure to have alignment parameters
    if alignment_parameters is None:
        alignment_parameters = AlignmentParameters()
    # Open the synthesized midi file
    midi_audio, _ = librosa.load(full_synthesized_midi_path,
                                 sr=alignment_parameters.sampling_rate)
    # Compute log-magnitude CQT of the synthesized midi file
    midi_cqt, midi_times = _compute_cqt(midi_audio, alignment_parameters)
    # Compute the distance matrix of the midi and audio CQTs, using cosine distance
    distance_matrix = scipy.spatial.distance.cdist(midi_cqt, audio_cqt,
                                                   'cosine')
    additive_penalty = float(np.median(np.ravel(distance_matrix)))
    multiplicative_penalty = 1.
    # Get lowest cost path in the distance matrix
    p, q, score = _dtw(distance_matrix, alignment_parameters.gully,
                       additive_penalty, multiplicative_penalty)

    # Compute MIDIAlignment
    midi_alignment = MIDIAlignment(midi_times.__getitem__(p),
                                   audio_times.__getitem__(q))

    # Normalize by path length and the distance matrix sub-matrix within the path
    score = score / len(p)
    score = score / distance_matrix[p.min():p.max(), q.min():q.max()].mean()

    # Write score
    midi_name = fh.get_file_name_from_full_path(full_synthesized_midi_path)
    decibel.import_export.midi_alignment_score_io.write_chord_alignment_score(
        midi_name, score)

    # Write alignment
    decibel.import_export.midi_alignment_io.write_alignment_file(
        midi_alignment, full_alignment_write_path)
Esempio n. 4
0
def get_well_aligned_midis(song: Song) -> [str]:
    """
    Return names of only the well-aligned MIDIs for this Song (excluding duplicates)
    :param song: Song in our data set
    """
    # Find duplicate MIDIs in this song; we will exclude them
    duplicate_midis = filehandler.find_duplicate_midis(song)

    well_aligned_midis = []
    for full_midi_path in song.full_midi_paths:
        midi_name = filehandler.get_file_name_from_full_path(full_midi_path)
        if midi_name not in duplicate_midis:
            alignment_score = read_chord_alignment_score(midi_name)
            if alignment_score.is_well_aligned:
                well_aligned_midis.append(midi_name)

    return well_aligned_midis
Esempio n. 5
0
def align_single_song(
        song: Song,
        alignment_parameters: Optional[AlignmentParameters] = None):
    """
    Align each MIDI file that is matched to this song to the song. As part of the procedure, each MIDI will be
    synthesized and the alignment of each MIDI will be written to a file.

    :param alignment_parameters: Parameters for alignment
    :param song: The Song object for which we align each MIDI file
    """
    # Make sure to have alignment parameters
    if alignment_parameters is None:
        alignment_parameters = AlignmentParameters()

    audio_loaded = False
    audio_cqt = np.ndarray([])
    audio_times = np.ndarray([])

    for midi_path in song.full_midi_paths:
        midi_name = fh.get_file_name_from_full_path(midi_path)
        write_path = fh.get_full_alignment_path(midi_name)
        if not fh.file_exists(write_path):
            # There is no alignment yet for this audio-midi combination, so let's calculate the alignment
            try:
                synthesized_midi_path = fh.get_full_synthesized_midi_path(
                    midi_name)
                if not fh.file_exists(synthesized_midi_path):
                    # The MIDI has not been synthesized yet
                    synthesizer.synthesize_midi_to_wav(
                        midi_path, alignment_parameters.sampling_rate)

                if not audio_loaded:
                    # Load audio if it is not loaded yet
                    audio_data, _ = librosa.load(
                        song.full_audio_path,
                        sr=alignment_parameters.sampling_rate)
                    audio_cqt, audio_times = _compute_cqt(
                        audio_data, alignment_parameters)
                    audio_loaded = True
                align_midi(audio_cqt, audio_times, synthesized_midi_path,
                           write_path, alignment_parameters)
                fh.remove_file(synthesized_midi_path)
            except:
                print(write_path + " failed.")
Esempio n. 6
0
def classify_aligned_midis_for_song(song: Song,
                                    chord_vocabulary: ChordVocabulary,
                                    segmenter: MIDISegmenterInterface):
    """
    Find chord labels for all re-aligned MIDIs of this song

    :param song: Song object for which we want to find the chord labels
    :param chord_vocabulary: List of all chords
    :param segmenter: Bar or beat segmenter
    """
    for full_midi_path in song.full_midi_paths:
        midi_name = filehandler.get_file_name_from_full_path(full_midi_path)
        full_alignment_path = filehandler.get_full_alignment_path(midi_name)
        write_path = filehandler.get_full_midi_chord_labs_path(
            midi_name, segmenter.segmenter_name)
        if not filehandler.file_exists(write_path):
            # The file does not exist yet, so we need to find the chords
            # try:
            # Realign the MIDI using the alignment path
            realigned_midi = RealignedMIDI(full_midi_path, full_alignment_path)
            # Find Events, using the specified partition method
            events = segmenter.find_events(realigned_midi)
            # Assign most likely chords to each event
            most_likely_chords = _assign_most_likely_chords(
                events, chord_vocabulary)
            # Compute average chord probability
            midi_chord_probability = _compute_midi_chord_probability(
                most_likely_chords)
            # Concatenate annotation items with the same chord labels into one annotation.
            concatenated_annotation = _get_midi_chord_annotation(
                most_likely_chords)
            # Export results
            export_chord_annotation(concatenated_annotation, write_path)
            filehandler.write_midi_chord_probability(segmenter.segmenter_name,
                                                     midi_name,
                                                     midi_chord_probability)
Esempio n. 7
0
def get_expected_best_midi(song: Song) -> (str, str):
    """
    Find name of the expected best well-aligned MIDI and segmentation type for this Song
    (based on MIDI chord probability)

    :param song: Song in our data set
    """
    # We only consider well-aligned MIDIs
    well_aligned_midis = get_well_aligned_midis(song)

    # Return path to the best MIDI of the song
    best_midi_name, best_midi_quality, best_segmentation = '', -9999999999, ''
    for segmentation_type in 'bar', 'beat':
        for full_midi_path in well_aligned_midis:
            midi_name = filehandler.get_file_name_from_full_path(
                full_midi_path)
            midi_chord_probability = filehandler.read_midi_chord_probability(
                segmentation_type, midi_name)
            if midi_chord_probability > best_midi_quality:
                # This is the best MIDI & segmentation type we have seen until now
                best_midi_name, best_midi_quality, best_segmentation = \
                    midi_name, midi_chord_probability, segmentation_type

    return best_midi_name, best_segmentation
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 + "."