|
"""preprocess_maestro.py""" |
|
import os |
|
import glob |
|
import re |
|
import json |
|
from typing import Dict, List, Tuple |
|
import numpy as np |
|
from utils.audio import get_audio_file_info |
|
from utils.midi import midi2note, note_event2midi |
|
from utils.note2event import note2note_event, note_event2event |
|
from utils.event2note import event2note_event |
|
from utils.note_event_dataclasses import Note, NoteEvent |
|
from utils.utils import note_event2token2note_event_sanity_check |
|
from utils.utils import assert_note_events_almost_equal |
|
|
|
|
|
def create_note_event_and_note_from_midi(mid_file: str, |
|
id: str, |
|
ignore_pedal: bool = False) -> Tuple[Dict, Dict]: |
|
"""Extracts note or note_event and metadata from midi: |
|
|
|
Returns: |
|
notes (dict): note events and metadata. |
|
note_events (dict): note events and metadata. |
|
""" |
|
notes, dur_sec = midi2note( |
|
mid_file, |
|
binary_velocity=True, |
|
ch_9_as_drum=False, |
|
force_all_drum=False, |
|
force_all_program_to=0, |
|
trim_overlap=True, |
|
fix_offset=True, |
|
quantize=True, |
|
verbose=0, |
|
minimum_offset_sec=0.01, |
|
drum_offset_sec=0.01, |
|
ignore_pedal=ignore_pedal) |
|
return { |
|
'maps_id': id, |
|
'program': [0], |
|
'is_drum': [0], |
|
'duration_sec': dur_sec + 0.01, |
|
'notes': notes, |
|
}, { |
|
'maps_id': id, |
|
'program': [0], |
|
'is_drum': [0], |
|
'duration_sec': dur_sec + 0.01, |
|
'note_events': note2note_event(notes), |
|
} |
|
|
|
|
|
def note_event2event_sanity_check(note_events: List[NoteEvent]): |
|
"""Sanity check for note events.""" |
|
events = note_event2event(note_events, None) |
|
note_events2, _, _ = event2note_event(events) |
|
assert_note_events_almost_equal(note_events, note_events2) |
|
|
|
|
|
def preprocess_maestro16k(data_home=os.PathLike, |
|
dataset_name='maestro', |
|
ignore_pedal=False, |
|
sanity_check=False) -> None: |
|
""" |
|
Splits: |
|
- train: 962 files |
|
- validation: 137 files |
|
- test: 177 files |
|
- all: 1276 file |
|
|
|
Writes: |
|
- {dataset_name}_{split}_file_list.json: a dictionary with the following keys: |
|
{ |
|
index: |
|
{ |
|
'maestro_id': maestro_id, |
|
'n_frames': (int), |
|
'mix_audio_file': 'path/to/mix.wav', |
|
'notes_file': 'path/to/notes.npy', |
|
'note_events_file': 'path/to/note_events.npy', |
|
'midi_file': 'path/to/midi.mid', |
|
'program': List[int], |
|
'is_drum': List[int], # 0 or 1 |
|
} |
|
} |
|
""" |
|
|
|
|
|
base_dir = os.path.join(data_home, dataset_name + '_yourmt3_16k') |
|
output_index_dir = os.path.join(data_home, 'yourmt3_indexes') |
|
os.makedirs(output_index_dir, exist_ok=True) |
|
|
|
|
|
metadata_file = os.path.join(base_dir, 'maestro-v3.0.0.json') |
|
with open(metadata_file, 'r') as f: |
|
_metadata = json.load(f) |
|
metadata = {} |
|
ids_all = list(range(len(_metadata['canonical_composer']))) |
|
assert len(ids_all) == 1276 |
|
for i in ids_all: |
|
metadata[i] = {} |
|
for key in ['split', 'midi_filename', 'audio_filename', 'duration']: |
|
metadata[i][key] = _metadata[key][str(i)] |
|
|
|
|
|
ids = {'all': ids_all, 'train': [], 'validation': [], 'test': []} |
|
for i in ids_all: |
|
m = metadata[i] |
|
ids[m['split']].append(i) |
|
|
|
m['midi_filename'] = os.path.join(base_dir, m['midi_filename']) |
|
m['audio_filename'] = os.path.join(base_dir, m['audio_filename']) |
|
|
|
|
|
if '.midi' in m['midi_filename'] and not os.path.exists(m['midi_filename'].replace( |
|
'.midi', '.mid')): |
|
os.rename(m['midi_filename'], m['midi_filename'].replace('.midi', '.mid')) |
|
m['midi_filename'] = m['midi_filename'].replace('.midi', '.mid') |
|
|
|
|
|
assert os.path.exists(m['midi_filename']) and '.mid' == m['midi_filename'][-4:] |
|
assert os.path.exists(m['audio_filename']) and '.wav' in m['audio_filename'] |
|
|
|
assert len(ids['train']) == 962 |
|
assert len(ids['validation']) == 137 |
|
assert len(ids['test']) == 177 |
|
|
|
|
|
file_list = {} |
|
for i in ids['all']: |
|
m = metadata[i] |
|
mix_audio_file = m['audio_filename'] |
|
fs, n_frames, n_channels = get_audio_file_info(mix_audio_file) |
|
assert fs == 16000 and n_channels == 1 |
|
n_frames = min(int(m['duration'] * 16000), n_frames) |
|
assert n_frames > 32001 |
|
|
|
notes_file = m['midi_filename'].replace('.mid', '_notes.npy') |
|
note_events_file = m['midi_filename'].replace('.mid', '_note_events.npy') |
|
midi_file = m['midi_filename'] |
|
|
|
file_list[i] = { |
|
'maestro_id': i, |
|
'n_frames': n_frames, |
|
'mix_audio_file': mix_audio_file, |
|
'notes_file': notes_file, |
|
'note_events_file': note_events_file, |
|
'midi_file': midi_file, |
|
'program': [0], |
|
'is_drum': [0], |
|
} |
|
|
|
|
|
notes, note_events = create_note_event_and_note_from_midi( |
|
mid_file=midi_file, id=i, ignore_pedal=ignore_pedal) |
|
|
|
if sanity_check: |
|
|
|
print(f'Sanity check for {i}: {midi_file}') |
|
note_event2token2note_event_sanity_check(note_events['note_events'], notes['notes']) |
|
|
|
np.save(notes_file, notes, allow_pickle=True, fix_imports=False) |
|
print(f'Created {notes_file}') |
|
np.save(note_events_file, note_events, allow_pickle=True, fix_imports=False) |
|
print(f'Created {note_events_file}') |
|
|
|
|
|
for split in ['all', 'train', 'validation', 'test']: |
|
fl = {} |
|
for i, maestro_id in enumerate(ids[split]): |
|
fl[i] = file_list[maestro_id] |
|
output_index_file = os.path.join(output_index_dir, f'{dataset_name}_{split}_file_list.json') |
|
with open(output_index_file, 'w') as f: |
|
json.dump(fl, f, indent=4) |
|
print(f'Created {output_index_file}') |
|
|