@name OMFG-THIS IS A SORTING ALGORITHM MMKAY?!?!
@persist 
@persist RawMidi:array Midi:table [MidiSortedTrack MidiTracks]:table TrackID Index Time TrackCount
@persist [State LastState]:string Init EventTypes:table SorterIndecies:array FileIsLoading

#helper function
function void addToTrack(TrackIndex:number, Table:table)
{
    #weird bug - workaround
    T = MidiTracks[TrackIndex,table]
    T:pushTable(Table)
    MidiTracks[TrackIndex,table] = T
}

#=========================
# Event constants
#=========================
EVENT_NONE           = 0
EVENT_NOTE_ON        = 1
EVENT_NOTE_OFF       = 2
EVENT_TEMPO          = 3
EVENT_META           = 4
EVENT_META_TRACKNAME = 5
#=========================


#Init the e2 blah blah
#=========================
if (first())
{
    runOnTick(1)   
    runOnFile(1)
    
    #start the the e2
    State = "readfile"

    #helper - set the event types
    EventTypes["0",      number] = EVENT_NOTE_ON
    EventTypes["1",      number] = EVENT_NOTE_OFF
    EventTypes["Tempo",  number] = EVENT_TEMPO
    EventTypes["Meta",   number] = EVENT_META
    EventTypes["TrkName",number] = EVENT_META_TRACKNAME 
    
}


#Track state changes ====================================
Init = 0

#Initialize when state changes
if (LastState != State)
{
    LastState = State
    Init      = 1
}

#============================================================
# STATES ====================================================
#============================================================

if (State == "readfile")
{
    if (Init) {FileIsLoading = 0}
       
    #read the file as soon as possible
    if (fileCanLoad() & !FileIsLoading)
    {
        FileIsLoading = 1
        fileLoad("mid_test.txt")   
    }
    
    #read the file
    if (fileClk() & fileLoaded())
    {        
        RawMidi = fileRead():explode("\n")
        State   = "parsefile"
        
        print("File Loaded: " + RawMidi:count())
    }
}
#============================================================
elseif (State == "parsefile")
{
    if (Init)
    {   
        Midi       = table()
        MidiTracks = table()
        Index      = 1
        TrackID    = 1
    }
    
    while (perf())
    {
        Event = RawMidi[Index, string]:trim():explode(" ")
        
        if (Event[1,string] == "MFile")
        {
            #set the file attributes
            
            Midi["format",number]      = Event[2,string]:toNumber()
            Midi["timebase",number]    = Event[4,string]:toNumber()
            
        }
        elseif (Event[1,string] == "TrkEd")
        {            
            #Go to next track because we are done with this one
            TrackID++
            
            #make sure it is added to the tracks
            addToTrack(TrackID, table(0,0,0,0))
        }
        else #normal midi event that has time
        {
            
            Time = Event[1,string]:toNumber()           #get the event time
            Type = EventTypes[Event[2,string],number]   #get the event type
            
            #parse the correct info
            #----------------------------------------------------------------
            if (Type == EVENT_NOTE_ON)
            {
                Pitch = 2^((Event[4, string]:toNumber()+12) / 12)
                Vol   = Event[5, string]:toNumber()/127
                
                #the volume and pitch is converted so we don't have to do that later
                EventTable = table(Time, Type, TrackID, Pitch, Vol)
                
                #Push it to the track
                addToTrack(TrackID, EventTable)
            }
            elseif (Type == EVENT_NOTE_OFF)
            {
                EventTable = table(Time, Type, TrackID)
                
                #Push it to the track
                addToTrack(TrackID, EventTable)
            }
            #----------------------------------------------------------------
            elseif (Type == EVENT_META)
            {
                #get the meta event type
                SubType = EventTypes[Event[3,string],number]
                
                if (SubType == EVENT_META_TRACKNAME)
                {
                    #save the track name if ya want
                }   
            }
            #----------------------------------------------------------------
            elseif (Type == EVENT_TEMPO)
            {
                #tempo is already converted to bpm
                EventTable = table(Time, Type, (6000000/Event[3,string]:toNumber()))
                
                #Push it to the track
                addToTrack(TrackID, EventTable)
            }
        }
        
        #go to next event
        Index++
        
        #if we are done then lets sort the file :D
        if (Index > RawMidi:count())
        {
            State = "sortfile"
            break   
        }
    }
}
#============================================================
elseif (State == "sortfile")
{
    if (Init) 
    {
        TrackCount = MidiTracks:count()
        Index      = 0
        Time       = 0
        
        MidiSortedTrack:clear()
    }
    
    while (perf())
    {
        TracksDone = 0
        NextTime   = 99999999999
        
        for (I = 1, TrackCount)
        {
            ID    = SorterIndecies[I,number]
            Track = MidiTracks[I,table]
            
            if (ID < Track:count())
            {
                ThisTime = Track[ID,table][1,number]
                
                if (ThisTime == Time)
                {
                    SorterIndecies[I,number] = SorterIndecies[I,number] + 1 
                    MidiSortedTrack:pushTable(Track[ID,table])
                }
                
                if (NextTime > ThisTime)
                {
                    NextTime = ThisTime
                }      
            }
            else
            {
                TracksDone++
            }
        }
        
        Time = NextTime
        
        if (TracksDone >= TrackCount)
        {
            #set midi data
            Midi["tracks",table]       = MidiSortedTrack
            Midi["track_count",number] = TrackCount
            
            #show the info and then stop
            State = "showinfo"   
            break
        }
    }    
}
#============================================================
elseif (State == "showinfo")
{
    if (Init)
    {
        print("### MIDI INFO ###")
        print("Midi Format: " + Midi["format",number])
        print("TimeBase: "    + Midi["timebase",number])
        print("Track Count: " + Midi["track_count",number])
        print("Event Count: " + Midi["tracks",table]:count())
    }
}
