becaliang/Music_Generation_Project
0
1from music21 import converter, instrument, key, note, stream, tempo, meter2from collections import OrderedDict3from itertools import groupby4from grammar import parse_melody5import copy6 7# ---------------------------- INTERNAL HELPERS ---------------------------- #8 9def _load_and_extract(data_path):10 midi_data = converter.parse(data_path)11 parts = midi_data.parts.stream()12 solo_parts = []13 14 for p in parts:15 voice = stream.Voice()16 for el in p.recurse():17 if isinstance(el, (note.Note, note.Rest)):18 if el.quarterLength == 0.0:19 el.quarterLength = 0.2520 voice.insert(el.offset, el)21 elif isinstance(el, (instrument.Instrument, tempo.MetronomeMark, key.KeySignature, meter.TimeSignature)):22 voice.insert(0, el)23 solo_parts.append(voice)24 25 return solo_parts26 27def _split_into_measures(tracks):28 melody_by_track = {}29 chords_by_track = {}30 31 for i, voice_stream in enumerate(tracks):32 track_name = f"Track_{i + 1}"33 melody_measures = OrderedDict()34 melody_offsets = [(int(n.offset / 4), n) for n in voice_stream if isinstance(n, (note.Note, note.Rest))]35 36 for idx, group in groupby(melody_offsets, lambda x: x[0]):37 melody_measures[idx] = [n[1] for n in group]38 39 chord_stream = copy.deepcopy(voice_stream)40 chord_stream.removeByClass(note.Note)41 chord_stream.removeByClass(note.Rest)42 chord_offsets = [(int(n.offset / 4), n) for n in chord_stream]43 44 chords = OrderedDict()45 for idx, group in groupby(chord_offsets, lambda x: x[0]):46 chords[idx] = [n[1] for n in group]47 48 min_len = min(len(chords), len(melody_measures))49 chords = OrderedDict(list(chords.items())[:min_len])50 melody_measures = OrderedDict(list(melody_measures.items())[:min_len])51 52 melody_by_track[track_name] = melody_measures53 chords_by_track[track_name] = chords54 55 return melody_by_track, chords_by_track56 57def _encode_grammars(melody_dict, chord_dict):58 grammars_by_track = {}59 60 for track_name in melody_dict:61 melody_measures = melody_dict.get(track_name)62 chord_measures = chord_dict.get(track_name)63 64 if not isinstance(melody_measures, dict) or not isinstance(chord_measures, dict):65 print(f"⚠️ Skipping {track_name}: Invalid measure format.")66 continue67 68 grammar_list = []69 for idx in sorted(melody_measures.keys()):70 if idx not in chord_measures:71 continue72 73 m_stream = stream.Voice()74 c_stream = stream.Voice()75 76 for n in melody_measures[idx]:77 m_stream.insert(n.offset, n)78 for c in chord_measures[idx]:79 c_stream.insert(c.offset, c)80 81 grammar = parse_melody(m_stream, c_stream)82 grammar_list.append(grammar)83 84 grammars_by_track[track_name] = grammar_list85 86 return grammars_by_track87 88# ---------------------------- PUBLIC INTERFACE ---------------------------- #89 90def get_multi_track_musical_data(data_path):91 solo_parts = _load_and_extract(data_path)92 melody_dict, chord_dict = _split_into_measures(solo_parts)93 grammar_dict = _encode_grammars(melody_dict, chord_dict)94 95 merged_chords = {}96 for chords in chord_dict.values():97 for k, v in chords.items():98 if k not in merged_chords:99 merged_chords[k] = []100 merged_chords[k].extend(v)101 102 return merged_chords, grammar_dict103 104def get_multi_track_corpus_data(grammar_dict):105 corpus_dict = {}106 all_tokens = []107 108 for track, grammar_list in grammar_dict.items():109 tokens = [token for phrase in grammar_list for token in phrase.split(" ")]110 corpus_dict[track] = tokens111 all_tokens.extend(tokens)112 113 vocab = sorted(set(all_tokens))114 token_to_index = {t: i for i, t in enumerate(vocab)}115 index_to_token = {i: t for t, i in token_to_index.items()}116 117 return corpus_dict, vocab, token_to_index, index_to_token118 119def get_musical_data(data_path):120 solo_parts = _load_and_extract(data_path)121 melody_dict, chords_dict = _split_into_measures(solo_parts)122 grammar_dict = _encode_grammars(melody_dict, chords_dict)123 124 merged_chords = {}125 for chords in chords_dict.values():126 for k, v in chords.items():127 if k not in merged_chords:128 merged_chords[k] = []129 merged_chords[k].extend(v)130 131 all_grammars = [g for track_grammars in grammar_dict.values() for g in track_grammars]132 return merged_chords, all_grammars133 134def get_corpus_data(grammar_list):135 corpus = [token for phrase in grammar_list for token in phrase.split()]136 vocab = sorted(set(corpus))137 token_to_index = {t: i for i, t in enumerate(vocab)}138 index_to_token = {i: t for i, t in enumerate(vocab)}139 return corpus, vocab, token_to_index, index_to_token140 