#!/usr/bin/env python
# coding: utf-8

# ## Music Generation V1

# When doing music generation I realized that the reason the generated musics was not similar to the source, was likely due to me incorrectly reading / encoding the source. In this scrips I simple read the source and write it back to file. notice that they sound different

import os, random
import numpy as np
import pandas as pd
import glob

# Libs for music processing 
import msgpack
import mido
from mido import MidiFile

# Using music 21
from music21 import midi
from music21 import converter, instrument, note, chord, stream

# For preprocessing and modeling
from sklearn.preprocessing import MinMaxScaler
from keras.utils import np_utils
from keras.layers import LSTM, Input, Dense, Embedding, Flatten, Reshape
from keras.activations import relu, tanh
from keras import optimizers
from keras.models import Model, Sequential
from keras.preprocessing.text import one_hot


### Fetching data from midi files
# They are extracted as notes

filename = '../data/kalyani.mid'
e = None

def get_notes():
    global e
    """ Get all the notes and chords from the midi files in the ./midi_songs directory """
    notes = []

    for file in glob.glob("../data/kalyani.mid"):
        mf = converter.parse(file)

        print("Parsing %s" % file)

        notes_to_parse = None

        try: # file has instrument parts
            s2 = instrument.partitionByInstrument(mf)
            print(s2.parts)
            notes_to_parse = s2.parts[0].recurse() 
        except: # file has notes in a flat structure
            notes_to_parse = mf.flat.notes

        for element in notes_to_parse:
            e = element
            if isinstance(element, note.Note):
                notes.append(str(element.nameWithOctave))
                # print(element.nameWithOctave)
            elif isinstance(element, chord.Chord):
                # print("Parsing a chord")
                notes.append('.'.join(str(n) for n in element.normalOrder))
    return notes

def make_stream_from_notes(notes):
    offset = 0
    output_notes = [instrument.Piano()]
    # create note and chord objects based on the values generated by the model
    for pattern in notes:
        # 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 += 1.0
    return output_notes



notes = get_notes()
output_notes = make_stream_from_notes(notes)
midi_stream = stream.Stream(output_notes)
# midi_stream.show('midi')
midi_stream.write('midi', fp='../data/test_output_kalyani.mid')
