get it working, on music21 and sequence style enoding

This commit is contained in:
Cezary Pukownik 2019-06-01 17:05:38 +02:00
parent 76f26837cd
commit fede93c36e
3 changed files with 118 additions and 74 deletions

View File

@ -1,7 +1,6 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
import numpy as np import numpy as np
import midi
import tensorflow as tf import tensorflow as tf
import pypianoroll as roll import pypianoroll as roll
from keras.layers import Input, Dense, Conv2D from keras.layers import Input, Dense, Conv2D
@ -11,29 +10,59 @@ from keras.layers import Input, Dense, Conv2D, Flatten, LSTM, Dropout, TimeDistr
from keras.models import Model, Sequential from keras.models import Model, Sequential
import matplotlib.pyplot as plt import matplotlib.pyplot as plt
import settings import settings
import random
import pickle import pickle
from tqdm import trange, tqdm
import sys import sys
from music21 import converter, instrument, note, chord, stream
trained_model_path = sys.argv[1] trained_model_path = sys.argv[1]
output_path = sys.argv[2] output_path = sys.argv[2]
treshold = float(sys.argv[3])
# load and predict # load and predict
print('Loading... {}'.format(trained_model_path))
model = pickle.load(open(trained_model_path, 'rb')) model = pickle.load(open(trained_model_path, 'rb'))
int_to_note = pickle.load(open('{}_dict'.format(trained_model_path), 'rb'))
music = np.empty((4,96,128)) seed = [random.randint(0,50) for x in range(8)]
for x in range(4):
generate_seed = np.random.randint(0, 127, 12288).reshape(1,96,128)
music[x] = model.predict(generate_seed).reshape(96,128)
generated_sample = music.reshape(4*96,128) music = []
# binarize generated music print('Generating...')
generated_sample = generated_sample > treshold * generated_sample.max() for i in trange(500):
# generated_sample = np.clip(generated_sample,0,1) * 128 predicted_vector = model.predict(np.array(seed).reshape(1,8,1))
predicted_index = np.argmax(predicted_vector)
# save to midi music.append(int_to_note[predicted_index])
generated_midi = midi.to_midi(generated_sample, output_path='{}.mid'.format(output_path), is_drum=True, program=0, )
#save plot for preview seed.append(predicted_index)
roll.plot(generated_midi, filename='{}.png'.format(output_path)) seed = seed[1:9]
print('Saving...')
offset = 0
output_notes = []
for event in tqdm(music):
if (' ' in event) or event.isdigit():
notes_in_chord = event.split(' ')
notes = []
for current_note in notes_in_chord:
new_note = note.Note(current_note)
new_note.storedInstrument = instrument.Piano()
notes.append(new_note)
new_chord = chord.Chord(notes)
new_chord.offset = offset
output_notes.append(new_chord)
else:
new_note = note.Note(event)
new_note.offset = offset
new_note.storedInstrument = instrument.Piano()
output_notes.append(new_note)
offset += 0.5
midi_stream = stream.Stream(output_notes)
midi_stream.write('midi', fp='{}.mid'.format(output_path))
print('Done!')

View File

@ -9,74 +9,62 @@ from math import floor
import sys import sys
from collections import defaultdict from collections import defaultdict
import pickle import pickle
from music21 import converter, instrument, note, chord, stream
import music21
midi_folder_path = sys.argv[1] midi_folder_path = sys.argv[1]
output_path = sys.argv[2] output_path = sys.argv[2]
def to_samples(multitrack, midi_res=settings.midi_resolution, how='by_group'): def to_sequence(midi_path):
seq_by_instrument = defaultdict( lambda : [] )
midi_file = music21.converter.parse(midi_path)
stream = music21.instrument.partitionByInstrument(midi_file)
for part in stream:
for event in part:
if part.partName != None:
# TODO: add note lenght as parameter
if isinstance(event, music21.note.Note):
# to_export_event = (str(event.pitch), event.quarterLength)
to_export_event = str(event.pitch)
seq_by_instrument[part.partName].append(to_export_event)
elif isinstance(event, music21.chord.Chord):
to_export_event = ' '.join(str(note) for note in event.pitches)
# to_export_event = (' '.join(str(note) for note in event.pitches), event.quarterLength)
seq_by_instrument[part.partName].append(to_export_event)
#how = 'by_group', 'by_instrument', 'merged', X_train_by_instrument = defaultdict( lambda : [] )
y_train_by_instrument = defaultdict( lambda : [] )
# TODO: add transpositions of every sample to every possible key transposition for instrument, sequence in seq_by_instrument.items():
# np.roll(sample, pitch_interval, axis=1) for transposition for i in range(len(sequence)-8) :
# np.roll(sample, time_steps, axis=0) for time shifting X_train_by_instrument[instrument].append(np.array(sequence[i: i + 8])) # <seq lenth
y_train_by_instrument[instrument].append(np.array(sequence[i + 8]))
# TODO: Notes to integers
samples_by_instrument = defaultdict( lambda : [] ) return X_train_by_instrument, y_train_by_instrument
for track in multitrack.tracks:
key = settings.midi_group[track.program + 1] if not track.is_drum else 'Drums'
# this makes pack of samples of N x 96 x 128 shape
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)
# save collected pack of data to dictionary with samples packs for groups of instruments
for sample in track_beats:
if sample.sum() != 0:
samples_by_instrument[key].append(sample)
# TODO: add posibility of choosing between saving samples to groups of instrument, or to every instrument separatly or with no differance
# TODO: add option, for looking only for one instrument/group
# TODO: add option for colecting, more than one beat per sample (min 4)
return samples_by_instrument
def to_midi(samples, output_path=settings.generated_midi_path, program=0, tempo=120, is_drum=False, beat_resolution=settings.beat_resolution):
tracks = [roll.Track(samples, program=program, is_drum=is_drum)]
return_midi = roll.Multitrack(tracks=tracks, tempo=tempo, downbeat=[0, 96, 192, 288], beat_resolution=beat_resolution)
roll.write(return_midi, output_path)
return return_midi
# TODO: Make optial function to erase information of note lenth - ??
def ignore_note_lenght():
pass
def main(): def main():
print('Exporting...') print('Exporting...')
samples_pack_by_instrument = defaultdict( lambda : list() ) train_X = defaultdict( lambda : [] )
train_y = defaultdict( lambda : [] )
for directory, subdirectories, files in os.walk(midi_folder_path): for directory, subdirectories, files in os.walk(midi_folder_path):
for midi_file in tqdm(files): for midi_file in tqdm(files):
midi_file_path = os.path.join(directory, midi_file) midi_file_path = os.path.join(directory, midi_file)
#load midi ro pypianoroll - Multirack _X_train, _y_train = to_sequence(midi_file_path)
try:
multitrack = roll.parse(midi_file_path)
except:
# IDEA: Log errors, and save to file?
continue
for key, value in to_samples(multitrack).items(): for (X_key, X_value), (y_key, y_value) in zip(_X_train.items(), _y_train.items()):
samples_pack_by_instrument[key].extend(value) train_X[X_key].extend(np.array(X_value))
train_y[y_key].extend(np.array(y_value))
# this is for intrument separation # this is for intrument separation
print('Saving...') print('Saving...')
if not os.path.exists(output_path): if not os.path.exists(output_path):
os.makedirs(output_path) os.makedirs(output_path)
for key, value in tqdm(samples_pack_by_instrument.items()): for (X_key, X_value), (y_key, y_value) in tqdm(zip(train_X.items(), train_y.items())):
np.savez_compressed('{}/{}.npz'.format(output_path, key), np.array(value)) if X_key == y_key:
np.savez_compressed('{}/{}.npz'.format(output_path, X_key), np.array(X_value), np.array(y_value))
print('Done!') print('Done!')

View File

@ -5,35 +5,62 @@ import settings
from tensorflow.keras import layers from tensorflow.keras import layers
from keras.layers import Input, Dense, Conv2D, Flatten, LSTM, Dropout, TimeDistributed, RepeatVector, Activation, Bidirectional, Reshape from keras.layers import Input, Dense, Conv2D, Flatten, LSTM, Dropout, TimeDistributed, RepeatVector, Activation, Bidirectional, Reshape
from keras.models import Model, Sequential from keras.models import Model, Sequential
from keras.utils.np_utils import to_categorical
import numpy as np import numpy as np
import sys import sys
import pickle import pickle
def load_data(samples_path):
print('Loading... {}'.format(train_data_path))
train_X = np.load(train_data_path, allow_pickle=True)['arr_0']
train_y = np.load(train_data_path, allow_pickle=True)['arr_1']
return train_X, train_y
# TODO: make transformer class with fit, transform and reverse definitions
def preprocess_samples(train_X, train_y):
vocab = np.unique(train_X)
n_vocab = vocab.shape[0]
note_to_int = dict((note, number) for number, note in enumerate(vocab))
int_to_note = dict((number, note) for number, note in enumerate(vocab))
_train_X = []
_train_y = []
for sample in train_X:
# TODO: add normalizasion
_train_X.append([note_to_int[note] for note in sample])
train_X = np.array(_train_X).reshape(train_X.shape[0], train_X.shape[1], 1)
train_y = np.array([note_to_int[note] for note in train_y]).reshape(-1,1)
train_y = to_categorical(train_y)
return train_X, train_y, n_vocab, int_to_note
train_data_path = sys.argv[1] train_data_path = sys.argv[1]
train_X, train_y = load_data(train_data_path)
train_X, train_y, n_vocab, int_to_note = preprocess_samples(train_X, train_y)
save_model_path = sys.argv[2] save_model_path = sys.argv[2]
epochs = int(sys.argv[3]) epochs = int(sys.argv[3])
model = Sequential() model = Sequential()
model.add(LSTM(128,input_shape=(96, 128),return_sequences=True)) model.add(LSTM(512, input_shape=(train_X.shape[1], train_X.shape[2]), return_sequences=True))
model.add(Dropout(0.3)) model.add(Dropout(0.3))
model.add(LSTM(512, return_sequences=True)) model.add(LSTM(512, return_sequences=True))
model.add(Dropout(0.3)) model.add(Dropout(0.3))
model.add(LSTM(128)) model.add(LSTM(512))
model.add(Dense(128)) model.add(Dense(256))
model.add(Dropout(0.3)) model.add(Dropout(0.3))
model.add(Dense(128*96)) model.add(Dense(n_vocab))
model.add(Activation('softmax')) model.add(Activation('softmax'))
model.add(Reshape((96, 128)))
model.compile(loss='categorical_crossentropy', optimizer='rmsprop') model.compile(loss='categorical_crossentropy', optimizer='rmsprop')
# load training data
print('Traing Samples: {}'.format(train_data_path))
train_X = np.load(train_data_path)['arr_0']
# model training # model training
model.fit(train_X, train_X, epochs=epochs, batch_size=32) print('Training...')
model.fit(train_X, train_y, epochs=epochs, batch_size=64)
# save trained model # save trained model
pickle_path = '{}.pickle'.format(save_model_path) pickle.dump(model, open(save_model_path,'wb'))
pickle.dump(model, open(pickle_path,'wb')) pickle.dump(int_to_note, open('{}_dict'.format(save_model_path),'wb'))
print("Model save to {}".format(pickle_path)) print('Done!')
print("Model saved to: {}".format(save_model_path))