File size: 5,904 Bytes
3388d00
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
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"
        )