Thursday, 4 February 2021

keras 19 rnn generate music 2

code link: https://github.com/chuanshuoge6/keras-rnn-music



#main.py

import glob
import numpy as np
from music21 import *
import tensorflow as tf
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Dense, Dropout, LSTM, Activation
from tensorflow.keras.models import load_model

#extract all notes composed in songs
notes = []
for file in glob.glob("midi_songs/*.mid"):
    midi = converter.parse(file)
    notes_to_parse = None
    parts = instrument.partitionByInstrument(midi)
    if parts: # file has instrument parts
        notes_to_parse = parts.parts[0].recurse()
    else: # file has notes in a flat structure
        notes_to_parse = midi.flat.notes
    for element in notes_to_parse:
        if isinstance(element, note.Note):
            notes.append(str(element.pitch))
        elif isinstance(element, chord.Chord):
            notes.append('.'.join(str(n) for n in element.normalOrder))

#print(notes)
#'A3', 'C4', 'D3', 'F3', 'G4', 'A3', 'C4'

sequence_length = 100
# remove mulitplication of notes, sort
pitchnames = sorted(set(item for item in notes))
#print(pitchnames)
n_vocab = len(pitchnames)

# create a dictionary to map pitches to integers
note_to_int = dict((note, number) for number, note in enumerate(pitchnames))
int_to_note = dict((number, note) for number, note in enumerate(pitchnames))
#print(note_to_int)
#there are 308 different notes

network_input = []
network_output = []

#100 notes in succession produce the 101st note,
#slide 100 notes block through music to generate input array,
#gather cosresponding output to each input block to form output array
# create input sequences and the corresponding outputs
for i in range(0, len(notes) - sequence_length, 1):
    sequence_in = notes[i:i + sequence_length]
    sequence_out = notes[i + sequence_length]
    network_input.append([note_to_int[char] for char in sequence_in])
    network_output.append(note_to_int[sequence_out])
n_patterns = len(network_input)

# reshape the input into a format compatible with LSTM layers
network_input = np.reshape(network_input, (n_patterns, sequence_length, 1))
# normalize input
network_input = network_input / float(n_vocab)
network_output = tf.keras.utils.to_categorical(network_output)

#print(network_input)
#print(network_output)
#print(network_input.shape)
#(43990, 100, 1)
#print(network_output.shape)
#(43990, 308)

# train on GPU
pysical_devices = tf.config.experimental.list_physical_devices('GPU')
# print("Num GPUs Available: ", len(pysical_devices))
tf.config.experimental.set_memory_growth(pysical_devices[0], True)
"""
model = Sequential()

model.add(LSTM(
    256,
    input_shape=(network_input.shape[1], network_input.shape[2]),
    return_sequences=True
))

model.add(Dropout(0.3))
model.add(LSTM(512, return_sequences=True))

model.add(Dropout(0.3))
model.add(LSTM(256))

model.add(Dense(256))
model.add(Dropout(0.3))

model.add(Dense(n_vocab))
model.add(Activation('softmax'))
model.summary()

model.compile(loss='categorical_crossentropy', optimizer='adam')
model.fit(network_input, network_output, epochs=200, batch_size=64)
model.save('model.h5')
"""
#start generating music
#pick random note block as a start
#start = np.random.randint(0, len(network_input)-1)
start = 40000

pattern = network_input[start]
prediction_output = []

model = load_model('model.h5')

# generate 500 notes, use 100 notes as start to generate 101st note
# then use 1-101 to generate 102...
for note_index in range(500):
    prediction_input = np.reshape(pattern, (1, len(pattern), 1))
    prediction_input = prediction_input / float(n_vocab)

    prediction = model.predict(prediction_input, verbose=0)
    index = np.argmax(prediction)

    result = int_to_note[index]
    prediction_output.append(result)

    pattern = np.append(pattern, index)
    pattern = pattern[1:len(pattern)]

#music 21 translates
offset = 0
output_notes = []
# create note and chord objects based on the values generated by the model
for pattern in prediction_output:
    # pattern is a chord
    if ('.' in pattern) or pattern.isdigit():
        notes_in_chord = pattern.split('.')
        notes = []
        for current_note in notes_in_chord:
            new_note = note.Note(int(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)
    # pattern is a note
    else:
        new_note = note.Note(pattern)
        new_note.offset = offset
        new_note.storedInstrument = instrument.Piano()
        output_notes.append(new_note)
    # increase offset each iteration so that notes do not stack
    offset += 0.5

midi_stream = stream.Stream(output_notes)
midi_stream.write('midi', fp='test_output.mid')

reference:

How to Upload Audio to YouTube

No comments:

Post a Comment