def plot_interpolation_sotw_to_lick(): interpolations = [] for i in range(0, 10, 3): path = "Sampled/interpolation_examples/SotW_to_lick/1/interpolate_" + str( i) + ".midi" midi = ppr.parse(path, beat_resolution=4) # get Multitrack object midi = midi.tracks[0] # get first/only track midi.name = "" if i == 0: midi.name = "start sequence" if i == 10: midi.name = "end sequence" interpolations.append(midi) mt = ppr.Multitrack(tracks=interpolations, beat_resolution=4) p, _ = ppr.plot(mt, yticklabel="off", xtick='beat', xticklabel=True, grid="both") #p.set_size_inches((8, 8), forward=True) filename = "interpolation_SotW_to_lick.png" p.savefig(filename)
def generate_pianoroll_music_corpus(self, max_length): ''' Reads in MIDI files placed in dir self.path_to_music in a pianoroll format and generates a corpus array of shape (time steps x 88) or (max_length x 88). Corpus consists of pianorolls which are basically concatenated n-hot arrays. Like that, polyphonic music is allowed. Arguments: max_length -- maximum length of the corpus ''' corpus = np.empty((0, 128)).astype(bool) for i in os.listdir(self.path_to_music): if i.endswith('.mid') or i.endswith('.midi'): multitrack = piano.parse(os.path.join(self.path_to_music, i)) multitrack.trim_trailing_silence() multitrack.binarize() multitrack.downsample( int(multitrack.beat_resolution / self.beat_resolution) ) # sample down to self.beat_resolution time steps per beat #multitrack.tempo = self.tempo #multitrack.beat_resolution = self.beat_resolution singletrack = multitrack.tracks[0] singletrack.program = self.instrument p_roll = singletrack.pianoroll corpus = np.append(corpus, p_roll, axis=0) if len(corpus) > max_length: corpus = corpus[:max_length] # stop reading in MIDIs if max corpus size is reached break return corpus[:, 21: 109] # only select MIDI indices that correspond to notes on a grand piano
def plot_pianoroll(path="Sampled/4bar_samples/sample_4_0.midi"): midi = ppr.parse(path, beat_resolution=4) # get Multitrack object midi = midi.tracks[0] # get first/only track pianoroll = midi.pianoroll print(pianoroll.shape) ppr.plot(midi, filename="testppr.png", beat_resolution=4)
def find_data_with_no_empty_tracks(): root_dir = 'E:/merged_midi/' total = 0 midi_collection = get_midi_collection() for midi in midi_collection.find({'NotEmptyTracksNum': { '$exists': False }}): instr_tracks = { 'Drums': None, 'Piano': None, 'Guitar': None, 'Bass': None, 'Strings': None } num = 0 try: path = root_dir + midi['Genre'] + '/' + midi['md5'] + '.mid' mult = pypianoroll.parse(path) for track in mult.tracks: num += 1 midi_collection.update_one({'_id': midi['_id']}, {'$set': { 'NotEmptyTracksNum': num }}) print('Progress: {:.2%}\n'.format( midi_collection.count({'NotEmptyTracksNum': { '$exists': True }}) / midi_collection.count())) except: total += 1 # midi_collection.delete_one({'_id': midi['_id']}) print(total)
def process_data(name, beat_res=4, num_of_beats=4, max_tokens=100): ''' Utility function for each data function to extract required data. ''' data_lst = [] rhythm_lst = [] note_density_lst = [] chroma_lst = [] track = pypianoroll.parse(name, beat_resolution=beat_res).tracks if len(track) > 0: try: pm = pretty_midi.PrettyMIDI(name) beats = pm.get_beats() tempo = pm.get_tempo_changes() cur_idx, tempo_new = 0, [] except Exception as e: print(e) pr = track[0].pianoroll # extract segment by segment for j in range(0, len(pr), beat_res * num_of_beats): start_idx = j end_idx = j + beat_res * num_of_beats if end_idx // beat_res < len(beats): new_pr = pr[start_idx:end_idx] new_pm = slice_midi(pm, beats, start_idx // beat_res, end_idx // beat_res) new_pm.write("tmp.mid") ms = np.argmax(new_pr, axis=-1) # ensure each segment is not empty and contain unique notes if len(new_pm.instruments[0].notes) > 0 and \ len(np.unique(ms)) > 2 and np.count_nonzero(ms) >= 0.75 * len(ms): # get musical attributes _, rhythm, note_density, chroma, \ velocity = get_music_attributes(new_pr, beat=beat_res) # get midi encoding sequence events = magenta_encode_midi("tmp.mid") events.append(1) # EOS token # filter out segments that start with 0 and limit token length if rhythm[0] == 1 and len(events) <= max_tokens: chroma = get_harmony_vector( ) # read from saved "tmp.mid" file # aggregate data points data_lst.append(torch.Tensor(events)) rhythm_lst.append(rhythm) note_density_lst.append(note_density) chroma_lst.append(chroma) return data_lst, rhythm_lst, note_density_lst, chroma_lst
def parse(a_file): pianoroll = pr.parse(a_file).tracks[0].pianoroll # retrieve 1000 timestamp from the middle. # and take [21, 109) range of pitches. https://usermanuals.finalemusic.com/Finale2012Mac/Content/Finale/MIDI_Note_to_Pitch_Table.htm matrix_x = ((pianoroll[1000:2000, :] > 0) * 1).reshape( 1, num_timestamps, num_pitchs) matrix_y = pianoroll[1000:2000, :].reshape(1, num_timestamps, num_pitchs) return (matrix_x, matrix_y)
def get_full_performance(self, index, beat_resolution=resolution_per_beat): infos = self.csv_data_frame.iloc[index] midi_filename = infos['midi_filename'] path = "" + self.root_dir + "/" + midi_filename midi = ppr.parse( path, beat_resolution=beat_resolution) # get Multitrack object midi = midi.tracks[0] # get first/only track return midi
def load_MIDI(self, path): multitrack = piano.parse(path) multitrack.trim_trailing_silence() multitrack.binarize() single_tracks = np.empty((0, 128)) for i in range(len(multitrack.tracks)): singletrack = multitrack.tracks[i].pianoroll legal=True if self.type=='mono': # check if the shortest note is not shorter than our beat resolution count = 0 for i in range(len(singletrack)-1): if np.any(singletrack[i]) and not np.any(singletrack[i+1]) and count < int(multitrack.beat_resolution/self.beat_resolution): legal = False break if np.any(singletrack[i]) and np.array_equal(singletrack[i],singletrack[i+1]) : count+=1 else: count = 0 if legal: # shortest pitch in track is representable by our beat resolution downsampled_track = [] for ts in range(0,len(singletrack)): if ts%int(multitrack.beat_resolution/self.beat_resolution)==0 or not np.any(singletrack[ts]): downsampled_track.append(singletrack[ts]) ''' # we have to find the silent time steps between two consecutive same pitches ts_zeros = np.squeeze(np.where(~singletrack.any(axis=1))) # do not consider first or last time step ts_zeros = ts_zeros[(ts_zeros!=0) & (ts_zeros!=len(singletrack))] for j in range(len(ts_zeros)): # if pitch before and after silence is the same and this time step will be swallowed by downsampling if np.array_equal(singletrack[ts_zeros[j]-1,:],singletrack[ts_zeros[j]+1,:]) and ts_zeros[j]%int(multitrack.beat_resolution/self.beat_resolution) and not np.any(singletrack[ts_zeros[j]]): # insert silence at the corresponding position such that in downsampled array silence will occur singletrack = np.insert(singletrack, int(np.round(ts_zeros[j]/int(multitrack.beat_resolution/self.beat_resolution),0)), np.zeros(128), axis=1) ts_zeros+=1 # now we downsample singletrack = singletrack[::int(multitrack.beat_resolution/self.beat_resolution)] single_tracks = np.append(single_tracks, singletrack, axis=0) ''' single_tracks = np.append(single_tracks, np.array(downsampled_track), axis=0) #multitrack.downsample(int(multitrack.beat_resolution/self.beat_resolution)) # sample down to self_beat_resolution time steps per beat #multitrack.tempo = self.tempo #for i in range(len(multitrack.tracks)): # singletrack = multitrack.tracks[i] # single_tracks = np.append(single_tracks, singletrack.pianoroll, axis=0) # singletrack = multitrack.tracks[0] # return singletrack.pianoroll return single_tracks
def encode_midi(fname, beat=24, is_pr=False): if not is_pr: track = pypianoroll.parse(fname, beat_resolution=beat) pr = track.get_merged_pianoroll()[:beat * 8] else: pr = fname pitch_lst, velocity_lst = convert_pr_to_pitch_lst(pr) rhythm = pitch_lst_to_rhythm(pitch_lst) events = pr_to_events(pitch_lst, velocity_lst) return events, pitch_lst, velocity_lst, pr, rhythm
def import_one(filename, beat_resolution, binarize=0): """Import one file and generate a piano roll.""" pr = pypianoroll.parse(filename, beat_resolution) if binarize: pr = pypianoroll.binarize(pr) merged = pr.get_merged_pianoroll() return merged
def midi_to_small_one_hot_pianoroll(path, beat_resolution=4): midi = ppr.parse(path, beat_resolution=beat_resolution) # get Multitrack object midi = midi.tracks[0] midi = ppr.binarize(midi) midi = midi.pianoroll midi = full_to_small_pianoroll(midi) midi = pianoroll_to_mono_pianoroll(midi) midi = pianoroll_to_one_hot_pianoroll(midi) midi = torch.from_numpy(midi).float() return midi
def parse_midis(): midis = [] for midi in os.listdir('./' + directory): print('Parsing ' + midi) a_midi = parse('./' + directory + '/' + midi, beat_resolution=beat_resolution) midis.append(a_midi) print('SONGS LOADED') return midis
def midi2pianoroll(file_path, max_beats=GLOBAL_VAR.MAX_BEATS): multitrack = pypianoroll.parse(file_path) pianoroll = multitrack.get_merged_pianoroll() pianorolls = [] start = 0 while len(pianoroll) - start >= max_beats: pianoroll_piece = pianoroll[start:(start + max_beats)] pianorolls.append(pianoroll_piece) start += max_beats break return pianorolls
def generate_MIDIindex_music_corpus(self, max_length): ''' Reads in MIDI files placed in dir self.path_to_music in a pianoroll format. Generates a symbolic one-dimensional corpus array of length sum of time steps of all files or self.max_corpus_size and saves it in self.corpus. It is an array of MIDI indices, with -1 corresponding to silence Arguments: max_length -- maximum length of the corpus ''' sym_sequence = np.array([]).astype(np.int8) for i in os.listdir(self.path_to_music): if i.endswith('.mid') or i.endswith('.midi'): # remove trailing silence and binarize (same loudness) multitrack = piano.parse(os.path.join(self.path_to_music, i)) multitrack.trim_trailing_silence() multitrack.binarize() multitrack.downsample( int(multitrack.beat_resolution / self.beat_resolution) ) # sample down to self_beat_resolution time steps per beat #multitrack.tempo = self.tempo #multitrack.beat_resolution = self.beat_resolution singletrack = multitrack.tracks[0] singletrack.program = self.instrument p_roll = singletrack.pianoroll if piano.metrics.polyphonic_rate( p_roll ) > 0: # we only work with monophonic input when we have symbolic alphabet pass else: temp = np.full( p_roll.shape[0], -1) # make a sequence of -1 (stands for silence) indices_non_zero = p_roll.nonzero( ) # find all time steps where a tone is played temp[indices_non_zero[0]] = indices_non_zero[ 1] # set non_zero time steps with the current index for pitch played sym_sequence = np.append(sym_sequence, temp) if len(sym_sequence) > max_length: break corpus = np.array(sym_sequence).flatten().astype(np.int8) if len(corpus) > max_length: corpus = corpus[:max_length] return corpus
def _convert_to_tfrecords(self, mode, filename_list): filename = self.DATA_PATH + "/" + mode + ".tfrecords" if check_path_exists(filename): return logging.info("Writing {}".format(filename)) with tf.python_io.TFRecordWriter(filename) as writer: for filename in tqdm(filename_list): if filename.endswith(".mid") or filename.endswith(".midi"): multi_track = ppr.parse(filename) else: multi_track = ppr.load(filename) TOTAL_STEPS = self._choose_total_steps(multi_track) if TOTAL_STEPS == 1e8: continue RANGE = self.INPUT_SIZE FINAL_STEPS = math.ceil(TOTAL_STEPS / 24) multi_data = np.zeros((FINAL_STEPS, RANGE)) for track in multi_track.tracks: if not self._is_valid_track(track): continue data = track.pianoroll.astype(int) data = self._sampling(data) multi_data = np.add(multi_data, data) multi_data = np.clip(multi_data, 0, 1).astype(int) RANGE = self._split_into_segments(multi_data, 1) length = self.MAX_LEN for start in RANGE: end = start + length if end >= FINAL_STEPS: break example = tf.train.Example( features=tf.train.Features( feature={ "pianoroll": self._int64_list_feature( multi_data[start:end + 1].flatten()) })) writer.write(example.SerializeToString())
def plot_interpolation_pianorolls(bars=16): interpolations = [] for i in range(0, 6, 2): path = "Sampled/" + str(bars) + "bar_interpolation/interpolate_" + str( i) + ".midi" midi = ppr.parse(path, beat_resolution=4) # get Multitrack object midi = midi.tracks[0] # get first/only track midi.name = "" if i == 0: midi.name = "start sequence" if i == 4: midi.name = "end sequence" pr = midi.pianoroll # padding to full length in case MIDI file ends earlier if pr.shape[0] != bars * 16: padding = np.zeros((bars * 16 - pr.shape[0], pr.shape[1])) pr = np.concatenate((pr, padding), axis=0) midi.pianoroll = pr interpolations.append(midi) mt = ppr.Multitrack(tracks=interpolations, beat_resolution=4) if bars == 16: p, _ = ppr.plot(mt, yticklabel="number", xtick='beat', xticklabel=False, grid="off") # there seems to be a bug in ppr, despite xticklabel=False, the plot still has the labels for each x-axis value else: p, _ = ppr.plot(mt, yticklabel="number", xtick='beat', xticklabel=True, grid="both") p.set_size_inches((8, 8), forward=True) filename = str(bars) + "bar_interpolation.png" p.savefig(filename)
def parse_multiple_dynamic(a_file): pianoroll = pr.parse(a_file).tracks[0].pianoroll shape = pianoroll.shape print(shape) matrix_x = [] matrix_y = [] total_timestamps = shape[0] t = 0 while t + 1000 <= total_timestamps: matrix_t = ((pianoroll[t:t + 1000, :] > 0) * 1).reshape( 1, num_timestamps, num_pitchs) matrix_t = pianoroll[t:t + 1000, :].reshape(1, num_timestamps, num_pitchs) matrix_x.append(matrix_t) matrix_y.append(matrix_t) t += 1000 print("done parsing {} dynamically into {} segments of 1000 timestamps". format(a_file, len(matrix_x))) return (matrix_x, matrix_y)
def parse_multiple(a_file): print("parsing ", a_file) pianoroll = pr.parse(a_file).tracks[0].pianoroll print(pianoroll.shape) # retrieve 1000 timestamp from the 3 segments. # for each take [21, 109) range of pitches. https://usermanuals.finalemusic.com/Finale2012Mac/Content/Finale/MIDI_Note_to_Pitch_Table.htm # stripped out xtracrispy.mid qhich is too short. matrix_x_1 = ((pianoroll[0:1000, :] > 0) * 1).reshape( 1, num_timestamps, num_pitchs) matrix_y_1 = pianoroll[0:1000, :].reshape(1, num_timestamps, num_pitchs) matrix_x_2 = ((pianoroll[1000:2000, :] > 0) * 1).reshape( 1, num_timestamps, num_pitchs) matrix_y_2 = pianoroll[1000:2000, :].reshape(1, num_timestamps, num_pitchs) matrix_x_3 = ((pianoroll[2000:3000, :] > 0) * 1).reshape( 1, num_timestamps, num_pitchs) matrix_y_3 = pianoroll[2000:3000, :].reshape(1, num_timestamps, num_pitchs) matrix_x_4 = ((pianoroll[3000:4000, :] > 0) * 1).reshape( 1, num_timestamps, num_pitchs) matrix_y_4 = pianoroll[3000:4000, :].reshape(1, num_timestamps, num_pitchs) return ((matrix_x_1, matrix_x_2, matrix_x_3, matrix_x_4), (matrix_y_1, matrix_y_2, matrix_y_3, matrix_y_4))
def get_track(midi_filename, voice, beat_resolution, transpose=True): ''' Convert midi file to track object :midi_filename: midi file to convert :voice: voice to select in the midi file (soprano, alto, tenor, bass) :beat_resolution: number of time steps per quarter notes :transpose: True if transpose to C Major :return: track object for the selected voice ''' csv_text = midi_utils.load_to_csv(midi_filename) # get semitones to C major semitones, _ = get_semitones_to_C(csv_text) # get multitrack object from midi multitrack = pypianoroll.parse(midi_filename, beat_resolution=beat_resolution) # get the voice track if transpose: track = pypianoroll.transpose(multitrack.tracks[voice], -semitones) else: track = multitrack.tracks[voice] return track
def main(): """Main function: Converts selected MIDI files to pianoroll.""" args = parse_arguments() songs = get_long_songs() if args.use_muspy: final_path = os.path.join(args.data_path, 'results', 'midi_files') if not os.path.exists(final_path): os.mkdir(final_path) for song in songs: midifile = midi_path(song) midi_class = pretty_midi.PrettyMIDI(midifile) final_midi_path = os.path.join(final_path, song) if not os.path.exists(os.path.join(final_midi_path, '.mid')): try: midi_class.write(final_midi_path + '.mid') except (IOError, IndexError) as e: pass else: final_path = os.path.join(args.data_path, 'results', 'npz_files') if not os.path.exists(final_path): os.mkdir(final_path) for song in songs: midifile = midi_path(song) npz_path = os.path.join(final_path, song) if not os.path.exists(os.path.join(npz_path, '.npz')): try: parsed = pypianoroll.parse(midifile) pypianoroll.save(npz_path, parsed) except (IOError, IndexError) as e: pass
def list_melodies(midi_path, out_dirname=None): """Lists extracted melodies of midi file given by path. Shows melody idx, length and optionally path to melody plot""" melodies = extract_melodies(midi_path) pp_multitrack = pp.parse(midi_path) for i, melody in enumerate(melodies): fig = plt.figure(figsize=(15, 10)) ax = fig.add_subplot(211) pianoroll = melody_lib.melody_to_pianoroll(melody["events"]) pp.plot_pianoroll(ax, pianoroll) ticks = np.arange(0, pianoroll.shape[0], STEPS_PER_BAR * 2) ax.set_xticks(ticks) ax.set_xticklabels(np.arange(0, len(ticks)) * 2) ax.grid(True, axis='x') plt.title('Extracted melody') ax = fig.add_subplot(212) pp.plot_pianoroll(ax, pp_multitrack.tracks[melody["instrument"]].pianoroll) plt.title('Original midi track') plot_basename = "" if out_dirname is not None: plot_basename = "lst_{}_{}.png".format(Path(midi_path).stem, i) fig.savefig(str(out_dirname / plot_basename)) plt.show() plt.close() print("idx: {:2d},\tlength: {:3d}{}".format( i, len(melody["events"]) // 16, "" if out_dirname is None else ",\t" + plot_basename))
# Used for observing distribution and mapping of MIDI instruments to classes melodyNumber = [0] * num_of_files stringsNumber = [0] * num_of_files chordsNumber = [0] * num_of_files vocalsNumber = [0] * num_of_files windsNumber = [0] * num_of_files percussionNumber = [0] * num_of_files bassNumber = [0] * num_of_files unclassifiedTracks = dict() for file in os.listdir(midi_folder): filename = os.fsdecode(file) print(str(j) + ' - ' + filename) j += 1 pr = pypianoroll.parse(midi_folder + filename) pm_song = pretty_midi.PrettyMIDI(midi_folder + filename) if (pm_song.resolution == tpqn ): # need the file resolution to be constant tracklist = pr.tracks for t, track in enumerate(tracklist): instrument_class = utils.readLabels(track.program, track.name, track.is_drum) if (t == 0): # do it for first track only as an initialization # Initialize every entry with an empty pianoroll percussion_pianorolls[i] = np.zeros(np.shape( track.pianoroll[:, note_shift:(note_shift +
def pooled_process_file(self, args): def check_four_fourth(time_sign): return time_sign.numerator == 4 and time_sign.denominator == 4 idx, filepath = args fetch_meta = { } # in this dict I will store the id of the corresponding metadata file store_meta = False pbc = 1 yeah = 0 max_bar_silence = 0 processed_folder = os.path.join(self.data_path, "pianorolls/") path, file = os.path.split(filepath) artist = path.split(path_sep)[-1] filename = file.split(".")[0] # test 0: check keysignature = 4/4 always. try: pm_song = pm.PrettyMIDI(filepath) except Exception: # print(f'{idx} Not Pretty MIDI {filepath}') return pbc, yeah, fetch_meta if not all( [check_four_fourth(tmp) for tmp in pm_song.time_signature_changes]): return pbc, yeah, fetch_meta del pm_song # don't need pretty midi object anymore, now i need pianorolls try: base_song = pproll.parse(filepath, beat_resolution=4) except Exception: return pbc, yeah, fetch_meta # find a guitar, a bass and a drum instrument guitar_tracks, bass_tracks, drums_tracks, string_tracks = self.get_guitar_bass_drums( base_song) try: assert (string_tracks) except AssertionError: return pbc, yeah, fetch_meta # if string_tracks: base_song.merge_tracks(string_tracks, mode="max", program=48, name="Strings", remove_merged=True) # merging tracks change order of them, need to re-find the new index of Trio track guitar_tracks, bass_tracks, drums_tracks, string_tracks = self.get_guitar_bass_drums( base_song) # take all possible combination of guitar, bass and drums for guitar_track in guitar_tracks: for bass_track in bass_tracks: for drums_track in drums_tracks: # select only trio tracks (and strings) current_tracks = [ drums_track, bass_track, guitar_track, -1 ] names = ["Drums", "Bass", "Guitar", "Strings"] # create temporary song with only that tracks song = pproll.Multitrack() song.remove_empty_tracks() for i, current_track in enumerate(current_tracks): song.append_track( pianoroll=base_song.tracks[current_track]. pianoroll, program=base_song.tracks[current_track].program, is_drum=base_song.tracks[current_track].is_drum, name=names[i]) song.beat_resolution = base_song.beat_resolution song.tempo = base_song.tempo song.binarize() song.assign_constant(1) # Test 1: check whether a track is silent during all the song if song.get_empty_tracks(): continue pianoroll = song.get_stacked_pianoroll() i = 0 while i + self.phrase_size <= pianoroll.shape[0]: window = pianoroll[i:i + self.phrase_size, :, :] # print("window from", i, "to", i+self.phrase_size) # keep only the phrases that have at most one bar of consecutive silence # for each track bar_of_silences = np.array([0] * self.n_tracks) for track in range(self.n_tracks): j = 0 while j + self.bar_size <= window.shape[0]: if window[j:j + self.bar_size, :, track].sum() == 0: bar_of_silences[track] += 1 j += 1 # self.bar_size # if the phrase is good, let's store it if not any(bar_of_silences > max_bar_silence): # data augmentation, random transpose bar for shift in np.random.choice( [-5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5, 6], 1, replace=False): tmp = pproll.Multitrack() tmp.remove_empty_tracks() for track in range(self.n_tracks): tmp.append_track( pianoroll=window[:, :, track], program=song.tracks[track].program, name=config.instrument_names[ song.tracks[track].program], is_drum=song.tracks[track].is_drum) tmp.beat_resolution = 4 tmp.tempo = song.tempo # tmp.name = str(yeah) tmp.name = f"{idx}_{yeah}" # breakpoint() tmp.transpose(shift) tmp.check_validity() # print(os.path.join(processed_folder, f"{idx}_{yeah}" + ".npz")) # tmp.save(os.path.join(processed_folder, f"{idx}_{yeah}" + ".npz")) del tmp store_meta = True # adding link to corresponding metadata file fetch_meta[f"{idx}_{yeah}"] = { "artist": artist, "song": filename } yeah += 1 i += self.bar_size del song del base_song return pbc, yeah, fetch_meta
def load_MIDI(self, path): multitrack = piano.parse(path) multitrack.trim_trailing_silence() multitrack.binarize() single_tracks = np.empty((0, 128)) if self.type == 'mono': for i in range(len(multitrack.tracks)): singletrack = multitrack.tracks[i].pianoroll if multitrack.beat_resolution != self.beat_resolution: # we downsample # check if the shortest note is not shorter than our beat resolution count = 0 legal = True for i in range(len(singletrack) - 1): if np.any(singletrack[i]) and not np.any( singletrack[i + 1]) and count < int( multitrack.beat_resolution / self.beat_resolution): legal = False break if np.any(singletrack[i]) and np.array_equal( singletrack[i], singletrack[i + 1]): count += 1 else: count = 0 if legal: # shortest pitch in track is representable by our beat resolution downsampled_track = [] downsampled_track.append( singletrack[0]) # first time step always in for ts in range(1, len(singletrack) - 1): if ts % int( multitrack.beat_resolution / self.beat_resolution) == 0 or not np.any( singletrack[ts]) and np.any( singletrack[ts - 1]) and np.any( singletrack[ts + 1]): downsampled_track.append(singletrack[ts]) if (len(singletrack) - 1) % int( multitrack.beat_resolution / self.beat_resolution) == 0: downsampled_track.append( singletrack[len(singletrack) - 1]) # also consider last time step single_tracks = np.append(single_tracks, np.array(downsampled_track), axis=0) else: print( 'skip track that is not representable by desired beat resolution', self.beat_resolution) elif self.beat_resolution == multitrack.beat_resolution: # we don't downsample because beat resolution is the same as the track single_tracks = np.append(single_tracks, np.array(singletrack), axis=0) if self.type == 'poly': # merge the tracks of the same instrument instruments = {} instrument = -1 for i in range(len(multitrack.tracks)): if instrument != multitrack.tracks[i].program: instruments[multitrack.tracks[i].program] = [ i ] # append track IDs to instrument instrument = multitrack.tracks[i].program elif instrument == multitrack.tracks[i].program: instruments[instrument].append(i) merged_tracks_per_instrument = [] for instrument in instruments: merged_tracks_per_instrument.append( multitrack[instruments[instrument]].get_merged_pianoroll()) if multitrack.beat_resolution != self.beat_resolution: # we want to find out if we can downsample the track # we need to know the shortest and longest note merged = multitrack.get_merged_pianoroll( ) # all tracks merged together # make a list of all possible notes indices_non_zero = np.where(merged) alphabet = list(np.arange(21, 109)) indices_per_note = {} min_per_note = {} max_per_note = {} # get indices in track where each note is played for note in alphabet: indices_per_note[note] = indices_non_zero[0][np.where( indices_non_zero[1] == note)] if indices_per_note[ note].size > 0: # else the note does not appear (length 0) min_per_note[note] = len(indices_per_note) max_per_note[note] = 1 count = 1 for i in range(len(indices_per_note[note]) - 1): if indices_per_note[note][ i + 1] == indices_per_note[note][i] + 1: count += 1 else: if count < min_per_note[note]: min_per_note[note] = count if count > max_per_note[note]: max_per_note[note] = count count = 1 shortest_note = int(min(min_per_note.values())) longest_note = int(max(max_per_note.values())) # get shortest possible beat resolution #for i in reversed(range(shortest_note)): # if beat_resolution%(i+1) == 0: # beat_resolution = int(beat_resolution/(i+1)) # break # is shortest note a divisor of beat_resolution? if shortest_note >= int( multitrack.beat_resolution / self.beat_resolution): #self.beat_resolution%shortest_note ==0 and shortest_note!=1: # then we down-sample for track in merged_tracks_per_instrument: downsampled_track = [] downsampled_track.append( track[0]) # first time step always in for ts in range(1, len(track) - 1): if ts % int( multitrack.beat_resolution / self.beat_resolution) == 0 or not np.any( track[ts]) and np.any( track[ts - 1]) and np.any( track[ts + 1]): downsampled_track.append(track[ts]) if (len(track) - 1) % int(multitrack.beat_resolution / self.beat_resolution) == 0: downsampled_track.append( track[len(track) - 1]) # also consider last time step single_tracks = np.append(single_tracks, np.array(downsampled_track), axis=0) if multitrack.beat_resolution == self.beat_resolution: for track in merged_tracks_per_instrument: single_tracks = np.append(single_tracks, np.array(track), axis=0) # else we ignore the track because it is not representable by the chosen beat resolution return single_tracks
# coding: utf-8 # In[1]: import os import pypianoroll as pn # In[3]: os.chdir('./archive/new_songs/') # In[8]: subdir = [dI for dI in os.listdir('.') if os.path.isdir(os.path.join('.', dI))] ko = 0 po = 0 for i in subdir: po += 1 os.chdir(i) a = pn.parse('all.mid') b = pn.parse('melody.mid') if (a.get_active_length() >= b.get_active_length()): ko += 1 os.chdir("..") print ko * 100.0 / po
def proc_midi_to_pianoroll(filename, beats_in_measure): piano_roll = pypianoroll.parse(filename) piano_roll.downbeat[0::piano_roll.beat_resolution*beats_in_measure] = True return piano_roll
return b def trunc_sequences(x_train): windows = [] for j in range(len(x_train)): temp_x = np.array( np.array_split(pad_along_axis(x_train[j], seq_len), np.ceil(len(x_train[j]) / seq_len))) windows.append(temp_x) return np.array(windows) x = [] for i in range(1, len(sys.argv)): a = pn.parse(sys.argv[i]) x.append(1 * np.sign(a.tracks[0].pianoroll)) x_final = np.array(x) x_con = trunc_sequences(x_final) for j in range(len(x_con)): bolo = [] xo = np.expand_dims(x_con[j], axis=1) for k in range(len(x_con[j])): bolo.extend(model.predict_on_batch(xo[k])) model.reset_states() bolo = np.concatenate(np.array(bolo)) result = pn.binarize(pn.Track(pianoroll=bolo * 100, program=0, is_drum=False, name='my awesome piano'),
def extract_real_song_names(self, pianorolls_folder, metadata_folder, early_exit): # helper functions def msd_id_to_dirs(msd_id): """Given an MSD ID, generate the path prefix. E.g. TRABCD12345678 -> A/B/C/TRABCD12345678""" return os.path.join(msd_id[2], msd_id[3], msd_id[4], msd_id) def msd_id_to_h5(h5): """Given an MSD ID, return the path to the corresponding h5""" return os.path.join(metadata_folder, msd_id_to_dirs(msd_id) + '.h5') def check_four_fourth(time_sign): return time_sign.numerator == 4 and time_sign.denominator == 4 # create necessary folders pianorolls_path = os.path.join(self.dataset_path, "pianorolls/") metadata_path = os.path.join(self.dataset_path, "metadata/") songs_path = os.path.join(self.dataset_path, "songs/") #instruments_path = os.path.join(self.dataset_path, "instruments/") dest_paths = [pianorolls_path, metadata_path, songs_path] #, instruments_path] for path in dest_paths: if not os.path.exists(path): os.makedirs(path) # count number of files of dataset (slow but ok) self.dataset_length = sum( [len(files) for _, _, files in os.walk(pianorolls_folder)]) # assign unique id for each song of dataset print("Extracting real song names...") bar = progressbar.ProgressBar(max_value=self.dataset_length) pbc = 0 yeah = 0 fetch_meta = { } # in this dict I will store the id of the corresponding metadata file max_bar_silence = 0 with open(os.path.join(self.dataset_path, "song_names.txt"), "a") as song_names_fp: for path, subdirs, files in os.walk(pianorolls_folder): for file in files: store_meta = False pbc += 1 msd_id = path.split("/")[-1] filename = file.split(".")[0] # early exit if early_exit != None and pbc > early_exit: return # test 0: check keysignature = 4/4 always. try: pm_song = pm.PrettyMIDI(os.path.join(path, file)) except Exception: continue if not all([ check_four_fourth(tmp) for tmp in pm_song.time_signature_changes ]): continue del pm_song # don't need pretty midi object anymore, now i need pianorolls try: base_song = pproll.parse(os.path.join(path, file), beat_resolution=4) except Exception: continue # trova uno strumento chitarra, uno basso e uno drums guitar_tracks, bass_tracks, drums_tracks, string_tracks = self.get_guitar_bass_drums( base_song) try: assert (string_tracks) except AssertionError: continue #if string_tracks: base_song.merge_tracks(string_tracks, mode="max", program=48, name="Strings", remove_merged=True) # merging tracks change order of them, need to re-find the new index of Trio track guitar_tracks, bass_tracks, drums_tracks, string_tracks = self.get_guitar_bass_drums( base_song) # take all possible combination of guitar, bass and drums for guitar_track in guitar_tracks: for bass_track in bass_tracks: for drums_track in drums_tracks: # select only trio tracks (and strings) current_tracks = [ drums_track, bass_track, guitar_track, -1 ] names = ["Drums", "Bass", "Guitar", "Strings"] # create temporary song with only that tracks song = pproll.Multitrack() song.remove_empty_tracks() for i, current_track in enumerate( current_tracks): song.append_track( pianoroll=base_song. tracks[current_track].pianoroll, program=base_song. tracks[current_track].program, is_drum=base_song. tracks[current_track].is_drum, name=names[i]) song.beat_resolution = base_song.beat_resolution song.tempo = base_song.tempo # Test 1: check whether a track is silent during all the song if song.get_empty_tracks(): continue pianoroll = song.get_stacked_pianoroll() i = 0 while i + self.phrase_size <= pianoroll.shape[ 0]: window = pianoroll[i:i + self.phrase_size, :, :] # keep only the phrases that have at most one bar of consecutive silence # for each track bar_of_silences = np.array([0] * self.n_tracks) for track in range(self.n_tracks): j = 0 while j + self.bar_size <= window.shape[ 0]: if window[j:j + self.bar_size, :, track].sum() == 0: bar_of_silences[track] += 1 j += 1 #self.bar_size # if the phrase is good, let's store it #print(bar_of_silences) if not any( bar_of_silences > max_bar_silence): store_meta = True # adding link to corresponding metadata file # yeah: pianorolls counter # pbc: song/metadata counter fetch_meta[str(yeah)] = pbc yeah += 1 i += self.bar_size del song # finished with pianorolls, storing rest (if needed) if store_meta: # fetching corresponding metadata from Million song dataset with tables.open_file(msd_id_to_h5(msd_id)) as h5: title = str(h5.root.metadata.songs.cols.title[0], "utf-8") artist = str( h5.root.metadata.songs.cols.artist_name[0], "utf-8") album = str(h5.root.metadata.songs.cols.release[0], "utf-8") genres = [ str(genre, "utf-8") for genre in list( h5.root.metadata.artist_terms[:]) ] metadata = { "title": title, "artist": artist, "album": album, "genres": genres } song_names_fp.write( str(pbc) + " -> " + artist + " - " + title + "\n") del base_song bar.update(pbc)
else: X = np.r_[X,aux_X]; return X; def transform_pitch(mapping,pitch): new_pitch = np.zeros(pitch.shape); for i in range(pitch.shape[0]): for j in range(128): n_o = j/12; n_j = 12*n_o + mapping[j%12]; if n_j < 128: new_pitch[i,n_j] = pitch[i,j]; return new_pitch; #Get first 4 bars of Ode to Joy SONG = pypianoroll.parse("ode_to_joy.mid"); SONG.tracks[0].transpose(-5); SONG_br = SONG.beat_resolution; SONG_bpm = SONG.tempo; SONG_pitch = SONG.tracks[0].pianoroll; MAX_LEN = int(0.25*SONG_bpm.shape[0]); SONG_pitch = SONG_pitch[:MAX_LEN,:]; SONG_bpm = SONG_bpm[:MAX_LEN]; SONG_audio = convert_to_wav(SONG_pitch,SONG_bpm,SONG_br); #Make multiplicative inverse table for the multiplicative Z13 group nnames = np.array(['C','C#','D','D#','E','F','F#','G','G#','A','A#','B']); Z13 = np.zeros((12,12),dtype=int);
)].beat_resolution #stats[str(n)][str(exp_n[0])].beat_resolution else: assert max_corpus_size == params[str( exp )].max_corpus_size #params[str(n)][str(exp_n[0])].max_corpus_size assert beat_resolution == stats[str( exp )].beat_resolution #stats[str(n)][str(exp_n[0])].beat_resolution # now we have to read in the corpus path_to_music = os.path.join(os.getcwd(), '..', 'midis/') sym_sequence = np.array([]).astype(np.int8) for i in os.listdir(path_to_music): if i.endswith('.mid') or i.endswith('.midi'): # remove trailing silence and binarize (same loudness) multitrack = piano.parse(os.path.join(path_to_music, i)) multitrack.trim_trailing_silence() multitrack.binarize() multitrack.downsample( int(multitrack.beat_resolution / beat_resolution )) # sample down to self_beat_resolution time steps per beat singletrack = multitrack.tracks[0] p_roll = singletrack.pianoroll if piano.metrics.polyphonic_rate( p_roll ) > 0: # we only work with monophonic input when we have symbolic alphabet pass else: temp = np.full(p_roll.shape[0], -1) # make a sequence of -1 (stands for silence)