Spaces:
Sleeping
Sleeping
import os | |
import json | |
import numpy as np | |
import music21 as m21 | |
import streamlit as st | |
import tensorflow.keras as keras | |
MAPPING_PATH = "mapping.json" | |
SEQUENCE_LENGTH = 64 | |
class MelodyGenerator: | |
def __init__( self, model_path = "model.keras" ): | |
self.model_path = model_path | |
self.model = keras.models.load_model(model_path) | |
with open(MAPPING_PATH , "r") as fp: | |
self._mappings = json.load(fp) | |
self._start_symbol = ["/"] * SEQUENCE_LENGTH | |
def generate_melody(self, seed, num_steps, max_sequence_length, temperature): | |
# create seed with start symbols | |
seed = seed.split() | |
melody = seed | |
seed = self._start_symbol + seed | |
# map seed to int | |
seed = [self._mappings[symbol] for symbol in seed] | |
for _ in range( num_steps ): | |
# limit the seed to max_sequence_length | |
seed = seed[-max_sequence_length :] | |
#one-hot encode the seed | |
onehot_seed = keras.utils.to_categorical( seed, num_classes=len(self._mappings)+1) | |
# currently the onehot_seed is a 2d array having shape : ( max_sequence_length , num of symbol in the vocabulary ) | |
# but generaly model.predict accepts a 3d array beacause it calculates probability for bunch of data not for 1 so . we are adding a extra dimention | |
onehot_seed = onehot_seed[np.newaxis , ...] | |
# make a prediction | |
probabilities = self.model.predict( onehot_seed )[0] | |
# [0.1, 0.2, 0.1, 0.6] -> 1 | |
output_int = self._sample_with_temperature(probabilities , temperature) | |
#update the seed | |
seed.append(output_int) | |
# map int to our encodeing | |
output_symbol = [ k for k,v in self._mappings.items() if v == output_int ][0] | |
# check whether we'r at the end of a melody | |
if output_symbol == "/": | |
break | |
# update the melody | |
else : | |
melody.append(output_symbol) | |
return melody | |
def _sample_with_temperature( self, probabilities , temperature ): | |
# temperature -> infinity | |
# temperature -> 0 | |
# temperature = 1 | |
predictions = np.log(probabilities) / temperature | |
probabilities = np.exp( predictions ) / np.sum(np.exp(predictions)) | |
choices = range(len(probabilities)) | |
index = np.random.choice(choices , p = probabilities) | |
return index | |
def save_melody( self, melody ,step_duration = 0.25, format = "midi" , file_name = "mel.mid") : | |
#create a music21 stream | |
stream = m21.stream.Stream() | |
#parse all the symbol in the melody and create note/rest objects | |
# 60 _ _ _ r _ 62 _ | |
start_symbol = None | |
step_counter = 1 | |
for i, symbol in enumerate(melody) : | |
#handle case in which we have a note/rest | |
if symbol != "_" or i + 1 == len(melody): | |
#ensure we're dealing with note/rest beyond the first one | |
if start_symbol is not None: | |
quarter_length_duration = step_duration * step_counter # 0.25 * 4 = 1 | |
# handle rest | |
if start_symbol == "r": | |
m21_event = m21.note.Rest( quarterLength = quarter_length_duration) | |
#hadle notes | |
else : | |
m21_event = m21.note.Note( int(start_symbol) , quaterLegth = quarter_length_duration ) | |
stream.append(m21_event) | |
# reset the step counter | |
step_counter = 1 | |
start_symbol = symbol | |
# handle case in which we have a prolongation sign "_" | |
else : | |
step_counter = step_counter + 1 | |
# write the m21 strem to a midi file | |
stream.write( format , file_name ) | |
# app initiallize | |
# st.title('Melody Craft 1.0') | |
st.markdown( | |
""" | |
<style> | |
/* Define keyframes for the animation */ | |
@keyframes glow { | |
0% { text-shadow: 0 0 10px #fff, 0 0 20px #fff, 0 0 30px #fff, 0 0 40px #ff00e4, 0 0 70px #ff00e4, 0 0 80px #ff00e4, 0 0 100px #ff00e4, 0 0 150px #ff00e4; } | |
50% { text-shadow: 0 0 20px #fff, 0 0 30px #fff, 0 0 40px #ff00e4, 0 0 50px #ff00e4, 0 0 80px #ff00e4, 0 0 90px #ff00e4, 0 0 110px #ff00e4, 0 0 160px #ff00e4; } | |
100% { text-shadow: 0 0 10px #fff, 0 0 20px #fff, 0 0 30px #fff, 0 0 40px #ff00e4, 0 0 70px #ff00e4, 0 0 80px #ff00e4, 0 0 100px #ff00e4, 0 0 150px #ff00e4; } | |
} | |
/* Apply animation to the title */ | |
.glowing-title { | |
font-size: 6rem; | |
font-weight: bold; | |
color: #ff00e4; | |
animation: glow 2s ease-in-out infinite; | |
} | |
</style> | |
""" | |
, unsafe_allow_html=True | |
) | |
st.markdown('<h1 class="glowing-title">Tune Genie</h1>', unsafe_allow_html=True) | |
seeds ={ 'seed1' : "60 _ _ _ _ _ _ _ 60 _ _ _ _ _ _ _ 60 _ _ _ _ _ _ _ 62",'seed2' : "69 _ _ _ _ _ _ _",'seed3' : "69 _ _ _ 69 _ _ _ 69 _ _ _ 72 _ _ _ 72 _ _ _ 69",'seed4' : "76 _ _ _ 76 _ _ _ _ _ 76 _ 76 _ _ _ 79 _ _ _ 74 ",'seed5': "71 _ _ _ 69 _" } | |
seed = st.selectbox( 'choose seed ' , ('seed1','seed2','seed3','seed4','seed5','Try your own seed' )) | |
if seed == "Try your own seed": | |
seed = st.text_input("Enter your custom seed:") | |
else: | |
seed = seeds[seed] | |
if st.button('GENERATE'): | |
mg = MelodyGenerator() | |
melody = mg.generate_melody( seed , 200, SEQUENCE_LENGTH , 0.4) | |
print(melody) | |
mg.save_melody(melody) | |
st.download_button( | |
label="Download Midi File", | |
data=open("mel.mid", "rb").read(), | |
file_name="example.midi", | |
mime="audio/midi" | |
) | |