From 4aa59163460f3705d52dacd035bfcf1f7ce55130 Mon Sep 17 00:00:00 2001 From: Cezary Pukownik Date: Wed, 29 May 2019 10:36:34 +0200 Subject: [PATCH] simplify a little midi.py module --- project/midi.py | 80 ++++++++++++ project/midi_to_samples.py | 249 ------------------------------------- 2 files changed, 80 insertions(+), 249 deletions(-) create mode 100644 project/midi.py delete mode 100644 project/midi_to_samples.py diff --git a/project/midi.py b/project/midi.py new file mode 100644 index 0000000..04ca47f --- /dev/null +++ b/project/midi.py @@ -0,0 +1,80 @@ +#!/usr/bin/env python3 + +import settings +import pypianoroll as roll +import matplotlib.pyplot as plt +import numpy as np +import os +from tqdm import tqdm +from math import floor +import sys + +def to_samples(midi_file_path, midi_res=settings.midi_resolution): + + # this function export a samples from midi file: + # and for every track in midi file chopped pianoroll + # for a samples of given beat_lenth (midi_res) + # every track is single line + + print('Exporting samples from: {}'.format(midi_file_path)) + + all_beats = np.empty((0, settings.midi_resolution, 128)) + + for track in roll.Multitrack(midi_file_path).tracks: + print('Track: {}'.format(track.name)) + if not track.is_drum: + number_of_beats = floor(track.pianoroll.shape[0] / midi_res) + track_pianoroll = track.pianoroll[: number_of_beats * midi_res] + track_beats = track_pianoroll.reshape(number_of_beats, midi_res, 128) + all_beats = np.concatenate([track_beats, all_beats], axis=0) + + print('Exported {} samples of {}'.format(number_of_beats, settings.midi_program[track.program])) + else: + # add code for drums samples + pass + return all_beats + +def to_midi(samples, output_path=settings.generated_midi_path, program=0, tempo=120, beat_resolution=settings.beat_resolution): + tracks = [roll.Track(samples, program=program)] + return_midi = roll.Multitrack(tracks=tracks, tempo=tempo, downbeat=[0, 96, 192, 288], beat_resolution=beat_resolution) + roll.write(return_midi, settings.generated_midi_path) + +def delete_empty_samples(sample_pack): + print('Deleting empty samples...') + temp_sample_pack = sample_pack + index_manipulator = 1 + for index, sample in enumerate(sample_pack): + if sample.sum() == 0: + temp_sample_pack = np.delete(temp_sample_pack, index-index_manipulator, axis=0) + index_manipulator = index_manipulator + 1 + print('Deleted {} empty samples'.format(index_manipulator-1)) + return temp_sample_pack + +def main(): + + if sys.argv[1]=='export': + print('Exporting started...') + + sample_pack = np.empty((0,settings.midi_resolution,128)) + + for midi_file in os.listdir(settings.midi_dir): + midi_file_path = '{}/{}'.format(settings.midi_dir, midi_file) + midi_samples = to_samples(midi_file_path) + if midi_samples is None: + continue + sample_pack = np.concatenate((midi_samples, sample_pack), axis=0) + + # sample_pack = delete_empty_samples(sample_pack) + + np.savez_compressed(settings.samples_dir, sample_pack) + print('Exported {} samples'.format(sample_pack.shape[0])) + + fig, axes = plt.subplots(nrows=10, ncols=10, figsize=(20, 20)) + for idx, ax in enumerate(axes.ravel()): + n = np.random.randint(0, sample_pack.shape[0]) + sample = sample_pack[n] + ax.imshow(sample, cmap = plt.get_cmap('gray')) + plt.savefig(settings.sample_preview_path) + +if __name__ == '__main__': + main() diff --git a/project/midi_to_samples.py b/project/midi_to_samples.py deleted file mode 100644 index 532191b..0000000 --- a/project/midi_to_samples.py +++ /dev/null @@ -1,249 +0,0 @@ -import settings -import pypianoroll as roll -import matplotlib.pyplot as plt -import numpy as np -import os -from tqdm import tqdm -from math import floor -import sys -from sklearn.preprocessing import MinMaxScaler - -midi_program = { - 0 : 'Perc', - 1 : 'Acoustic Grand Piano', - 2 : 'Bright Acoustic Piano', - 3 : 'Electric Grand Piano', - 4 : 'Honky-tonk Piano', - 5 : 'Electric Piano 1', - 6 : 'Electric Piano 2', - 7 : 'Harpsichord', - 8 : 'Clavi', - 9 : 'Celesta', - 10 : 'Glockenspiel', - 11 : 'Music Box', - 12 : 'Vibraphone', - 13 : 'Marimba', - 14 : 'Xylophone', - 15 : 'Tubular Bells', - 16 : 'Dulcimer', - 17 : 'Drawbar Organ', - 18 : 'Percussive Organ', - 19 : 'Rock Organ', - 20 : 'Church Organ', - 21 : 'Reed Organ', - 22 : 'Accordion', - 23 : 'Harmonica', - 24 : 'Tango Accordion', - 25 : 'Acoustic Guitar (nylon)', - 26 : 'Acoustic Guitar (steel)', - 27 : 'Electric Guitar (jazz)', - 28 : 'Electric Guitar (clean)', - 29 : 'Electric Guitar (muted)', - 30 : 'Overdriven Guitar', - 31 : 'Distortion Guitar', - 32 : 'Guitar harmonics', - 33 : 'Acoustic Bass', - 34 : 'Electric Bass (finger)', - 35 : 'Electric Bass (pick)', - 36 : 'Fretless Bass', - 37 : 'Slap Bass 1', - 38 : 'Slap Bass 2', - 39 : 'Synth Bass 1', - 40 : 'Synth Bass 2', - 41 : 'Violin', - 42 : 'Viola', - 43 : 'Cello', - 44 : 'Contrabass', - 45 : 'Tremolo Strings', - 46 : 'Pizzicato Strings', - 47 : 'Orchestral Harp', - 48 : 'Timpani', - 49 : 'String Ensemble 1', - 50 : 'String Ensemble 2', - 51 : 'SynthStrings 1', - 52 : 'SynthStrings 2', - 53 : 'Choir Aahs', - 54 : 'Voice Oohs', - 55 : 'Synth Voice', - 56 : 'Orchestra Hit', - 57 : 'Trumpet', - 58 : 'Trombone', - 59 : 'Tuba', - 60 : 'Muted Trumpet', - 61 : 'French Horn', - 62 : 'Brass Section', - 63 : 'SynthBrass 1', - 64 : 'SynthBrass 2', - 65 : 'Soprano Sax', - 66 : 'Alto Sax', - 67 : 'Tenor Sax', - 68 : 'Baritone Sax', - 69 : 'Oboe', - 70 : 'English Horn', - 71 : 'Bassoon', - 72 : 'Clarinet', - 73 : 'Piccolo', - 74 : 'Flute', - 75 : 'Recorder', - 76 : 'Pan Flute', - 77 : 'Blown Bottle', - 78 : 'Shakuhachi', - 79 : 'Whistle', - 80 : 'Ocarina', - 81 : 'Lead 1 (square)', - 82 : 'Lead 2 (sawtooth)', - 83 : 'Lead 3 (calliope)', - 84 : 'Lead 4 (chiff)', - 85 : 'Lead 5 (charang)', - 86 : 'Lead 6 (voice)', - 87 : 'Lead 7 (fifths)', - 88 : 'Lead 8 (bass + lead)', - 89 : 'Pad 1 (new age)', - 90 : 'Pad 2 (warm)', - 91 : 'Pad 3 (polysynth)', - 92 : 'Pad 4 (choir)', - 93 : 'Pad 5 (bowed)', - 94 : 'Pad 6 (metallic)', - 95 : 'Pad 7 (halo)', - 96 : 'Pad 8 (sweep)', - 97 : 'FX 1 (rain)', - 98 : 'FX 2 (soundtrack)', - 99 : 'FX 3 (crystal)', - 100 : 'FX 4 (atmosphere)', - 101 : 'FX 5 (brightness)', - 102 : 'FX 6 (goblins)', - 103 : 'FX 7 (echoes)', - 104 : 'FX 8 (sci-fi)', - 105 : 'Sitar', - 106 : 'Banjo', - 107 : 'Shamisen', - 108 : 'Koto', - 109 : 'Kalimba', - 110 : 'Bag pipe', - 111 : 'Fiddle', - 112 : 'Shanai', - 113 : 'Tinkle Bell', - 114 : 'Agogo', - 115 : 'Steel Drums', - 116 : 'Woodblock', - 117 : 'Taiko Drum', - 118 : 'Melodic Tom', - 119 : 'Synth Drum', - 120 : 'Reverse Cymbal', - 121 : 'Guitar Fret Noise', - 122 : 'Breath Noise', - 123 : 'Seashore', - 124 : 'Bird Tweet', - 125 : 'Telephone Ring', - 126 : 'Helicopter', - 127 : 'Applause', - 128 : 'Gunshot' -} - -# strasznie wolna funcja ;/ -def trim_notes(pianoroll): - now_block = [] - for x in pianoroll: - this = None - prev = None - new_line =[] - for y in x: - this = y - if prev != None: - if this > 0 and prev > 0: - new_line.append(0) - else: - new_line.append(y) - else: - new_line.append(y) - prev = this - now_block.append(new_line) - return np.array(now_block) - -def metrum_check(midi_lenght, metrum=4, beat_resolution=24): - return True if midi_lenght % (metrum * beat_resolution) == 0 else False - -# >>> data = [[-1, 2], [-0.5, 6], [0, 10], [1, 18]] -# >>> scaler = MinMaxScaler() -# >>> print(scaler.fit(data)) -# MinMaxScaler(copy=True, feature_range=(0, 1)) -# >>> print(scaler.data_max_) -# [ 1. 18.] -# >>> print(scaler.transform(data)) -# [[0. 0. ] -# [0.25 0.25] -# [0.5 0.5 ] -# [1. 1. ]] -# >>> print(scaler.transform([[2, 2]])) -# [[1.5 0. ]] - - -def to_samples(midi_file_path, midi_res=settings.midi_resolution, ignore_note_lenght=settings.ignore_note_lenght): - print('exporting samples from: {}'.format(midi_file_path)) - midi_file = roll.Multitrack(midi_file_path) - samples = None - all_samples = np.empty((0,settings.midi_resolution,128)) - for track in midi_file.tracks: - # if not track.is_drum: - if not metrum_check(track.pianoroll.shape[0]): - print('Track skipped') - continue - else: - instrument_track = track.pianoroll - instrument_track = trim_notes(instrument_track.T).T if ignore_note_lenght else instrument_track - scaler = MinMaxScaler() - instrument_track = scaler.fit_transform(instrument_track) - whole_beats = int(instrument_track.shape[0] / midi_res) - samples = instrument_track.reshape(whole_beats, midi_res, 128) - print('Exported {} samples of {}'.format(whole_beats, midi_program[track.program])) - all_samples = np.concatenate([samples, all_samples], axis=0) - return all_samples - -def to_midi(samples, output_path=settings.generated_midi_path, program=0, tempo=120, beat_resolution=settings.beat_resolution): - tracks = [roll.Track(samples, program=program)] - return_midi = roll.Multitrack(tracks=tracks, tempo=tempo, downbeat=[0, 96, 192, 288], beat_resolution=beat_resolution) - roll.write(return_midi, settings.generated_midi_path) - -def to_png(samples, output_path=settings.generated_pianoroll_path, horizontal=True): - img = samples.T if horizontal else samples - plt.imshow(img, cmap='gray') - plt.savefig(output_path) - -def delete_empty_samples(sample_pack): - temp_sample_pack = sample_pack - index_manipulator = 1 - for index, sample in enumerate(sample_pack): - if sample.sum() == 0: - temp_sample_pack = np.delete(temp_sample_pack, index-index_manipulator, axis=0) - index_manipulator = index_manipulator + 1 - print('Deleted {} empty samples'.format(index_manipulator-1)) - return temp_sample_pack - -def main(): - if sys.argv[1]=='-e': - print('Exporting started...') - sample_pack = np.empty((0,settings.midi_resolution,128)) - for midi_file in os.listdir(settings.midi_dir): - midi_file_path = '{}/{}'.format(settings.midi_dir, midi_file) - midi_samples = to_samples(midi_file_path) - if midi_samples is None: - continue - sample_pack = np.concatenate((midi_samples, sample_pack), axis=0) - - sample_pack = delete_empty_samples(sample_pack) - np.savez_compressed(settings.samples_dir, sample_pack) - print('Exported {} samples'.format(sample_pack.shape[0])) - elif sys.argv[1]=='-c': - to_midi(settings.generated_sample_path) - print('Samples to midi saved to {}'.format(settings.generated_sample_path)) - elif sys.argv[1]=='-p': - sample_pack = np.load(settings.samples_path)['arr_0'] - for i, sample in tqdm(enumerate(sample_pack)): - to_png(sample, output_path='data/preview/{}.png'.format(i)) - if i>50: - sys.exit() - else: - print('type command afrer -e to export samples, -c to convert samples to midi, -p to preview samples in png') - -if __name__ == '__main__': - main()