# ---
# jupyter:
#   jupytext:
#     text_representation:
#       extension: .py
#       format_name: light
#       format_version: '1.5'
#       jupytext_version: 1.14.4
#   kernelspec:
#     display_name: Python 3
#     name: python3
# ---

# + [markdown] id="OGbKdJlYqkDB" colab_type="text"
# # **Imports**

# + id="Td8GtVvPqnzI" colab_type="code" colab={}
# !pip install mido
# !pip install keras-rl
# !wget https://bin.equinox.io/c/4VmDzA7iaHb/ngrok-stable-linux-amd64.zip
# !unzip ngrok-stable-linux-amd64.zip

# + id="jZG_hVAFqoSX" colab_type="code" colab={}
from mido import MidiFile, MidiTrack, Message
from keras.layers import CuDNNLSTM, LSTM, Dense, Activation, Dropout
from keras.preprocessing import sequence
from keras.models import Sequential
from keras.optimizers import RMSprop,Adam,SGD,Adagrad
from keras.callbacks import TensorBoard
from rl.callbacks import FileLogger, ModelIntervalCheckpoint
import numpy as np
import mido
import random

from google.colab import files

# + [markdown] id="1ZaVT-gVqqDw" colab_type="text"
# # **Dataset Import**

# + id="wYTDW0hiquyQ" colab_type="code" colab={}
uploaded = files.upload()

fileNames = [];

for fn in uploaded.keys():
  fileNames.append(fn)

# + [markdown] id="SL3XU3KQsAqd" colab_type="text"
# # **Data Preparation**

# + id="FjSTpRYvsDef" colab_type="code" colab={}
'''
Retrieve all notes from songs and seperate them into noteSets
'''

noteSets = []
noteDescriptors = 3

#Iterate through all songs
for song in fileNames:
  time=float(0)
  prev=float(0)
  midi = MidiFile(song)
  notes = []
  #Iterate through messages in midi file
  for msg in midi:
    time += msg.time
    if not msg.is_meta:
      if msg.type == 'note_on':
        '''
        note are in the form [type, note, velocity]
        we want to convert to [note, velocity, time]
        '''
        note = msg.bytes()
        note = note[1:3]
        note.append(time-prev)
        prev = time
        notes.append(note)
  #Add notes from song to noteSets        
  noteSets.append(notes)

# + id="csCMuVYKupF7" colab_type="code" colab={}
'''
Scale all notes to to [0,1]
[note, velocity, time] -> [(note-24)/88, velocity/127, time/max_time]
'''

n = []

for notes in noteSets:
  for note in notes:
    note[0] = (note[0] - 24) / 88
    note[1] = note[1] / 127
    n.append(note[2])

max_time = max(n)      
      
for notes in noteSets:
  for note in notes:
    note[2] = note[2] / max_time

# + id="FRJw2EUEvhDY" colab_type="code" colab={}
'''
Constructing sentences and labels for training
'''
sentences = []
labels = []

window = 50

for notes in noteSets:
  for i in range(len(notes) - window):
    sentence = notes[i : i + window]
    label = notes[i + window]
    sentences.append(sentence)
    labels.append(label)
    
#Conversion to numpy array for feeding into model
sentences = np.array(sentences)
labels = np.array(labels)

# + [markdown] id="cjoO2x8jazh6" colab_type="text"
# # **TensorBoard**

# + id="J8kEowLHa1Xq" colab_type="code" colab={}
# Retrieved from https://www.dlology.com/blog/quick-guide-to-run-tensorboard-in-google-colab/

LOG_DIR = './log'
get_ipython().system_raw(
    'tensorboard --logdir {} --host 0.0.0.0 --port 6006 &'
    .format(LOG_DIR)
)

get_ipython().system_raw('./ngrok http 6006 &')

# ! curl -s http://localhost:4040/api/tunnels | python3 -c \
#     "import sys, json; print(json.load(sys.stdin)['tunnels'][0]['public_url'])"
    
tbCallBack = TensorBoard(log_dir='./log',
                         write_graph=True,
                         write_grads=True,
                         write_images=True)

# + [markdown] id="0awtNx4awnGA" colab_type="text"
# # **Model Definition**

# + id="ks1snlhRwp0a" colab_type="code" colab={}
model=Sequential()
model.add(CuDNNLSTM(512,input_shape=(window, noteDescriptors),return_sequences=True))
model.add(Dropout(0.3))
model.add(CuDNNLSTM(512, return_sequences=True))
model.add(Dropout(0.3))
model.add(CuDNNLSTM(512))
model.add(Dense(1024))
model.add(Dropout(0.3))
model.add(Dense(512))
model.add(Dropout(0.3))
model.add(Dense(noteDescriptors,activation="softmax"))

model.summary()

# + [markdown] id="zbwhczbQxIDC" colab_type="text"
# # **Model Training**

# + id="7OTXxkRrxJpr" colab_type="code" colab={}
model.compile(loss="categorical_crossentropy",
              optimizer="RMSprop",
              metrics=["accuracy"]) 

# + id="oAtXh204xpN-" colab_type="code" colab={}
weights_filename = 'model_weights_final.h5f'
callbacks = [tbCallBack]

model.fit(sentences,
          labels,
          epochs=500,
          batch_size=200,
          validation_split=0.1,
          callbacks=callbacks)

model.save_weights(weights_filename, overwrite=True) 

# + [markdown] id="u2cNz5wMyJ6B" colab_type="text"
# # **Music Generation**

# + id="TINMYrhryNNo" colab_type="code" colab={}
'''
Generate a song based on database
'''
songLength = 500;

#Pick a random song to start generation
noteSetSeed = random.randint(0, len(noteSets)-1)
seed = noteSets[noteSetSeed][0 : window]
x = seed
x = np.expand_dims(x, axis = 0)
predict=[]
for i in range(songLength):
	p=model.predict(x)
	x=np.squeeze(x) #squeezed to concateneate
	x=np.concatenate((x, p))
	x=x[1:]
	x=np.expand_dims(x, axis=0) #expanded to roll back
	p=np.squeeze(p)
	predict.append(p)

# + id="yw2hjfQIzdv1" colab_type="code" colab={}
'''
Reversescale all notes back
[note, velocity, time] -> [note * 88 + 24, velocity * 127, time * max_time]
'''

for a in predict:
	a[0] = int(88*a[0] + 24)
	a[1] = int(127*a[1])
	a[2] *= max_time
	# reject values out of range  (note[0]=24-102)(note[1]=0-127)(note[2]=0-__)
	if a[0] < 24:  
		a[0] = 24
	elif a[0] > 102:
		a[0] = 102
	if a[1] < 0:    
		a[1] = 0
	elif a[1] > 127:
		a[1] = 127
	if a[2] < 0:     
		a[2] = 0

# + id="89sUtz4Mz0Qk" colab_type="code" colab={}
'''
Save track from bytes data
'''
m=MidiFile()
track=MidiTrack()
m.tracks.append(track)

for note in predict:
	#147 means note_on
	note=np.insert(note, 0, 147)
	bytes=note.astype(int)
	msg = Message.from_bytes(bytes[0:3]) 
	time = int(note[3]/0.001025) # to rescale to midi's delta ticks. arbitrary value
	msg.time = time
	track.append(msg)

m.save('Ai_song.mid')

StarcoderdataJupyterScriptsDedupFiltered