Spaces:
Sleeping
Sleeping
import streamlit as st | |
import numpy as np | |
import time | |
import pickle | |
import tensorflow as tf | |
from music21 import * | |
from keras.models import Sequential | |
from keras.layers import * | |
from midi2audio import FluidSynth | |
import shutil | |
import pretty_midi | |
import soundfile as sf | |
def midi_to_audio(midi_file, output_file): | |
# Load the MIDI file | |
midi_data = pretty_midi.PrettyMIDI(midi_file) | |
# Synthesize the audio from the MIDI data | |
audio_data = midi_data.synthesize() | |
# Save to a WAV file | |
sf.write(output_file, audio_data, 44100) | |
####################### Music Generation Functions ####################### | |
def generate(seq_len,x): | |
""" Generate a piano midi file """ | |
#load the notes used to train the model | |
with open('final_notes', 'rb') as filepath: | |
notes = pickle.load(filepath) | |
# Get all pitch names | |
pitchnames = sorted(set(item for item in notes)) | |
n_vocab = len(set(notes)) | |
network_input, normalized_input = prepare_sequences(notes, pitchnames, n_vocab , seq_length = seq_len) | |
model = create_network(normalized_input, n_vocab) | |
prediction_output = generate_notes(model, network_input, pitchnames, n_vocab, x) | |
create_midi(prediction_output) | |
def prepare_sequences(notes, pitchnames, n_vocab , seq_length): | |
""" Prepare the sequences used by the Neural Network """ | |
# map between notes and integers and back | |
note_to_int = dict((note, number) for number, note in enumerate(pitchnames)) | |
sequence_length = seq_length | |
network_input = [] | |
normalized_input = [] | |
output = [] | |
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]) | |
output.append(note_to_int[sequence_out]) | |
n_patterns = len(network_input) | |
# reshape the input into a format compatible with LSTM layers | |
normalized_input = np.reshape(network_input, (n_patterns, sequence_length, 1)) | |
# normalize input | |
normalized_input = normalized_input / float(n_vocab) | |
return (network_input, normalized_input) | |
def create_network(network_input, n_vocab): | |
""" create the structure of the neural network """ | |
adam = tf.keras.optimizers.Adam(0.001) | |
model = Sequential() | |
model.add(LSTM( | |
512, | |
input_shape=(network_input.shape[1], network_input.shape[2]), | |
recurrent_dropout=0.3, | |
return_sequences=True | |
)) | |
model.add(LSTM(512, return_sequences=True, recurrent_dropout=0.3,)) | |
model.add(LSTM(256)) | |
model.add(BatchNormalization()) | |
model.add(Dropout(0.2)) | |
model.add(Dense(256)) | |
model.add(Activation('relu')) | |
model.add(BatchNormalization()) | |
model.add(Dropout(0.2)) | |
model.add(Dense(n_vocab)) | |
model.add(Activation('softmax')) | |
# 'rmsprop' | |
model.compile(loss='categorical_crossentropy', optimizer=adam, metrics=['accuracy']) | |
# Load the weights to each node | |
model.load_weights('best2.h5') | |
return model | |
def generate_notes(model, network_input, pitchnames, n_vocab , x): | |
""" Generate notes from the neural network based on a sequence of notes """ | |
# pick a random sequence from the input as a starting point for the prediction | |
start = np.random.randint(0, len(network_input)-1) | |
int_to_note = dict((number, note) for number, note in enumerate(pitchnames)) | |
pattern = network_input[start] | |
prediction_output = [] | |
# generate x notes (x entered by user) | |
for note_index in range(x): | |
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.append(index) | |
pattern = pattern[1:len(pattern)] | |
return prediction_output | |
def create_midi(prediction_output): | |
""" convert the output from the prediction to notes and create a midi file from the notes """ | |
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) | |
elif pattern == 'r': | |
# Create a rest note with a default duration (e.g., 1.0) | |
new_note = note.Rest(1.0) # Set a valid duration | |
new_note.offset = offset | |
new_note.storedInstrument = instrument.Piano() | |
output_notes.append(new_note) | |
else: | |
new_note = note.Note(pattern) | |
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='test_output2.mid') | |
# Set page config | |
st.set_page_config(page_title="Music Generation", page_icon=":tada:", layout="wide") | |
# Header section | |
with st.container(): | |
left_column, right_column = st.columns(2) | |
with left_column: | |
st.subheader("Music Generation :musical_keyboard:") | |
st.write( | |
"Our website is an application of piano music generation, you can listen to new musical notes generated by LSTM artificial neural network, which is used in fields of AI and deep learning. Let's get it started :notes:" | |
) | |
with right_column: | |
# Display a GIF instead of Lottie animation | |
st.image("im.gif", use_column_width=True) | |
# Sidebar for user input | |
# Sidebar for user input | |
with st.sidebar: | |
# Set a default value for len_notes | |
default_len_notes = 100 # Example default value | |
len_notes = st.slider('Please Choose The Notes Length', 20, 750, default_len_notes, 4) | |
st.write("Notes Length = ", len_notes) | |
# Music generation functionality | |
if st.sidebar.button('Generate My Music'): | |
# Use the default value if len_notes is not explicitly set by the user | |
if len_notes is not None: | |
with st.container(): | |
st.write("---") | |
with st.spinner('✨ Your music is now under processing... ✨'): | |
time.sleep(10) # Simulate processing time | |
generate(10, len_notes) | |
midi_to_audio('test_output2.mid', 'output.wav') | |
st.audio('output.wav') | |
st.markdown("Here you are! You can download your music by right-clicking on the media player.") | |
else: | |
# Fallback to the default value if no selection is made | |
with st.container(): | |
st.write("---") | |
st.warning("No notes length selected. Using default value of 100.") | |
with st.spinner('✨ Your music is now under processing... ✨'): | |
time.sleep(10) # Simulate processing time | |
generate(10, default_len_notes) | |
midi_to_audio('test_output2.mid', 'output.wav') | |
st.audio('output.wav') | |
st.markdown("Here you are! You can download your music by right-clicking on the media player.") | |