praca-magisterska/project/midi_to_samples.py

250 lines
8.0 KiB
Python

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()