function [events, tempo, notes, info] = midi2eventmat(A)
   // Convert a standard MIDI file to an event array
   //
   // Usage:
   //        [events, tempo, notes, info] = midi2eventmat(A)
   //
   // Where 
   //        events: Cell array containing all events with
   //                corresponding data
   //        tempo:  Array containing tempo track 
   //        notes:  Cell array containing a simplified list
   //                of notes
   //        info:   Vector containing important information
   //        A:      File path string or vector containing MIDI
   //                data. The function decides intelligently
   //                the proper data type
   //
   // The structure of an event cell array is as follows: There
   // is one matrix per track, whose rows correspond to events
   // according to the following format: 
   //
   // Column    Content 
   // ---------------------------------------------------------
   //    1      Delta-times in ticks
   //    2      MIDI Channel (0-15)
   //    3      Program (1-128)
   //    4      MIDI pitch of Note ON (0-127)
   //    5      MIDI Velocity of Note ON (0-127)
   //    6      MIDI pitch of Note OFF (0-127)
   //    7      MIDI Velocity of Note OFF (0-127)
   //    8      Control (0-127) 
   //    9      Control value (0-127)
   //   10      Polyphonic aftertouch (MIDI pitch) (0-127)
   //   11      Polyphonic aftertouch pressure value (0-127)
   //   12      Channel aftertouch mean pressure (0-127)
   //   13      Pitch wheel (0-16383, 8192 = no change))
   //   14      Metaevent number (0-127)
   //   15      Link to metavent (not yet implemented)
   //
   // The strucure of a tempo array is as follows:
   //
   // Column    Content
   // -------------------------------------------------------
   //   1       Delta-times in ticks
   //   2       Tempo in microseconds per metronome beat
   //   3       Time signature numerator NUM as in score
   //   4       Time signature denominator DEN as in score
   //
   // NOTES: 1) A metronome beat is usually a quarter note.
   //        2) Time signature denominator must be a power
   //           of 2  
   //
   // The structure of a notes cell array is as follows:
   // There is one matrix per track whose rows correspond
   // to notes according to the following format:
   //
   // Column    Content 
   // -------------------------------------------------------
   //    1      Absolute initial time of note, in ticks
   //    2      Note duration, in ticks
   //    3      MIDI pitch of note
   //    4      MIDI velocity of note
   //    5      MIDI Program
   //
   // Output argument info has the following structure:
   //
   // info = [Div, NTracks, MIDICLK]
   //
   // Definitions
   // -----------
   //
   // NUM          Time signature numerator
   // DEN          Time signature denominator as appears in 
   //              the score. Note that, on the contrary, the
   //              MIDI file contains it expresed as its base 
   //              2 logarithm. For instance, if DEN = 4, 
   //              then it is log2(4) = 2.
   // Ticks        Is the minimum unit of musical time to be 
   //              used in a MIDI file.
   // Div          Division, i.e., number of ticks per quarter
   //              note
   // MIDICLK      MIDI clocks per metronome beat. According
   //              to MIDI Specification, there are 24 MIDI
   //              Clocks per quarter note. Often, but not
   //              always, a beat corresponds to a quarter note
   // 32th_24Clk   32th notes per 24 MIDI Clocks. This should
   //              be always 8
   // METRO        Is the duration of a metronome beat in
   //              microseconds. The duration of a quarter note
   //              in microseconds is
   // 
   //                                         24
   //                  T_quarter_note  =  --------- METRO
   //                                      MIDIClk
   //
   //                The duration of a tick in microseconds is
   //
   //                                    24 x METRO
   //                       Ttick  =  ----------------- 
   //                                  MIDICLK x Ticks
   //
   // Delta-time       Time between the begining of the previous 
   //                  event and the begining of present event.
   //                  It is expressed as a  variable-length
   //                  quantity. Since delta-times are very 
   //                  frequent data (every event has one) they
   //                  should be as  short as practical. However
   //                  room should be reserved  for very long
   //                  delta times. A compromise can be reached
   //                  by allowing each delta-time just the
   //                  necessary space to be represented
   //                  unequivocally. As many as 4 bytes can be
   //                  used, but the first 3 must have the most
   //                  significant bit set to 1 and the last one
   //                  must have it reset to 0. Thus,  only 7
   //                  bits are usable per byte. There are at 
   //                  most 28 bits available--which makes quite
   //                  a long time!
   //
   // Requires deltatime 
   //
   // ----------------------------------------------------------
   // Author: Federico Miyara
   // Date:   2005-07-07
   //         2012-12-26
   //         2012-12-27
   //         2013-01-04
   //         2013-01-07
   //         2013-01-09
   //         2024-09-12

   // clear
   // A = "ejemplo1.mid";
   // A = "r:\Mis documentos\Música\Sonata en do menor.mid";
 

   // Read file into A if argument A is a path+filename,
   // otherwise A is already a vector with MIDI content 
   if type(A)==10
      fid = mopen(A, "rb");
      A = mgeti(fileinfo(A)(1), "uc", fid)';
      A = double(A);
      mclose(fid);
   end

   // Size of A
   L = length(A);

   // Header information retrieval
   // ----------------------------

   // Header length
   LH = ((A(5)*256 + A(6))*256 + A(7))*256 + A(8);

   // MIDI File Type
   MIDIType = A(10);

   // Total number of tracks
   ntrks = A(11)* 256 + A(12);

   NTracks = ntrks; 

   // Ticks per quarter note
   Div = A(13)*256 + A(14);


   // Tracks
   // Get the byte number NT(q) where track q data begins and
   // the length LT(q) of track data
   NT(1) = 23;
   LT(1) = ((A(19)*256 + A(20))*256 + A(21))*256 + A(22);

   for q = 2:NTracks
      NT(q) = NT(q-1) + LT(q-1) + 8; 
      LT(q) = ((A(NT(q) - 4)*256 + ..
                             A(NT(q) - 3))*256 + ..
                                           A(NT(q) - 2))*256 + ..
                                                         A(NT(q) - 1);
   end

   // NT, LT
   
   // Load function that converts variable-length 
   // hexadecimal numbers to decimal representation
   exec deltatime.sci;

   // Tempo track
   // Contains tempo and time-signature information
   // This ie a logical track, not a real one, since
   // its information is distributed in the note 
   // tracks
   // Initialize track number
   q = 1;
   // Byte number where the track data begins
   i = NT(q);
   // Initialize event number along track
   // (events correspond to rows)
   h = 1;
   while i < NT(q) + LT(q) + 1
      // Calculate delta-time
      // Find first byte < 128 from delta-time begining 
      // (it is necessary to find out the length in bytes
      // of delta-time)
      io = min(find(A(i:i+3)<128));
      // Apply delta-time function
      d = deltatime(A(i:i+io-1), 0);
      // Paste to tempo array
      tempo(h,1) = d;
      // Now go to the event the delta time 
      // is asociated to  
      i = i + io;
      // Metaevent
      if A(i)==255                        
         // Go to byte of type of metaevent
         // Only time-related metaevents are
         // considered, such as tempo, time
         // signature and end-of-track 
         i = i + 1;
         select A(i)
         case 88 
            // Time signature
            // Time signature data length
            LTS = A(i+1);            
            // Numerator
            NUM = A(i+2);            
            // Denominator
            DEN = 2^A(i+3);          
            // MIDI Clocks per beat
            MIDICLK = A(i+4);        
            // 32th notes per 24 MIDI Clocks
            CLK32th_24 = A(i+5);     
            // Paste NUM and DEN to tempo track
            tempo(h,3) = NUM;
            tempo(h,4) = DEN;
            // Go to next event
            i = i + 6;               
            h = h + 1;
         case 81
            // Metronome
            // Tempo data length
            LTempo = A(i+1);
            // Tempo
            METRO = (A(i+2)*256 + A(i+3))*256 + A(i+4);
            // Paste to tempo track
            tempo(h,2) = METRO;
            // Go to next event
            i = i + 5;               
            h = h + 1;
         case 47 
            // End of track
            // Number of eventos in track                
            H(q) = h;
            // Go to next track
            q = q + 1;
            // Abort loop that corresponds to ending track                   
            break                    
         otherwise
            // Unknown or unsupported
            // Metaevent (might be included in the future)
            // Length of unrecognised data in order to go 
            // to next event
            Ldata = A(i+1);
            // Go to next event
            i = i + Ldata + 2;
         end
      end
   end

   // Remove last row, which corresponds to an end-of-track
   // metaevent and is irrelevant
   tempo = tempo(1:$-1,:);
   
   // Start event tracks at track 1 (track 1 is tempo track
   // but may also contain note information in untidy MIDI
   // files)
   q = 1;
   while q <= NTracks
      // Byte number where the track data begins
      i = NT(q);
      // h is the event number along track
      h = 0;
      while i < NT(q) + LT(q) + 1 
         h = h + 1;
         // Calculate delta-time
         // Each event is preceded by a delta-time.
         // Delta-times start with 0 to 3 bytes whose
         // bit 7 (MSB) is 1 (number >= 128) and end 
         // with a byte whose bit 7 is 0 (number < 128).
         // Delta-times start immediately after the
         // preceding event ends. Since delta-times 
         // have variable length, to detect where they
         // end, the first byte < 128 from delta-time 
         // begining is searched  
         io = min(find(A(i:i+3)<128));
         // Apply delta-time function
         d = deltatime(A(i:i+io-1), 0);

         // Delta-time corresponding to event h from track q
         events{q}(h,1) = d;

         // Now we get to the event 
         i = i + io;

         // Running status
         // We enter running status when a status byte (e.g.,
         // note-on) has been previously issued and we keep
         // sending new data that is compatible with that
         // status (for instance, several notes-on). It is
         // easily recognised because after a new delta-time 
         // there is no new status byte but a data byte (whose
         // MSB is 0). The event type had been recorded in the
         // variable STATUS during the first event.
         // NOTE: The first event of a track must begin with
         //       a type of event, it cannot begin with data
         //       or there will be an error  
         if A(i) < 128
            // During running status MIDI channel cannot change  
            events{q}(h,2) = events{q}(h-1,2);

            if STATUS == 8 
               // Note OFF
               // Note to turn off  
               NOTAOFF = A(i);
               // Velocity with which it will be turned off
               VELOFF = A(i+1);
               // Go to next event
               i = i + 2;
               events{q}(h,6) = NOTAOFF;
               events{q}(h,7) = VELOFF;

            elseif STATUS == 9
               // Note ON
               // Note to turn on
               NOTEON = A(i);
               // Velocity with which it will be turned on
               VELON = A(i+1);
               // Go to next event
               i = i + 2;                      
               events{q}(h,4) = NOTEON;
               events{q}(h,5) = VELON;
            elseif STATUS == 10
               // Polyphonic aftertouch 
               // Note on which the polyphonic aftertouch
               // is active
               NOTEAFTER = A(i)
               // Polyphonic aftertouch pressure
               PRESSSUREAFTER = A(i+1)
               // Go to next event
               i = i + 2;                      
               events{q}(h,10) = NOTEAFTER;
               events{q}(h,11) = PRESSUREAFTER;
            elseif STATUS == 11
               // Control change
               CONTROL = A(i);
               VALUE = A(i+1);
               // Go to next event
               i = i + 2;                      
               events{q}(h,8) = CONTROL;
               events{q}(h,9) = VALUE;
            elseif STATUS == 12
               // Program change 
               PROG = A(i) + 1;
               // Go to next event
               i = i + 1;                      
               events{q}(h,3) = PROG;
            elseif STATUS == 13
               // Channel Aftertouch  
               PRESSUREAFTER = A(i);
               // Go to next event
               i = i + 1 ;                     
               events{q}(h,12) = PRESSUREAFTER;
            elseif STATUS == 14
               // Pitch change 
               PITCH = A(i) + A(i+1)*128;
               // Go to next event
               i = i + 2;                      
               events{q}(h,13) = PITCH;
            end
            // STATUS Change
            // It takes place whenever a different type of event
            // starts
         elseif (128 <= A(i))*(A(i) <= 143)
            // Note OFF
            STATUS = 8;
            CHANNEL = A(i) - 128;
            NOTEOFF = A(i+1);
            VELOFF = A(i+2);
            // Go to next event
            i = i + 3;                         
            events{q}(h,2) = CHANNEL;
            events{q}(h,6) = NOTEOFF;
            events{q}(h,7) = VELOFF;
         elseif (144 <= A(i))*(A(i) <= 159)
            // Note ON
            STATUS = 9;
            CHANNEL = A(i) - 144;
            NOTEON = A(i+1);
            VELON = A(i+2);
            // Salto a evento siguiente
            i = i + 3;                         
            events{q}(h,2) = CHANNEL;
            events{q}(h,4) = NOTEON;
            events{q}(h,5) = VELON;
         elseif (160 <= A(i))*(A(i) <= 175)
            // Polyphonic Aftertouch
            STATUS = 10;
            CHANNEL = A(i) - 160;
            NOTEAFTER = A(i+1);
            PRESSUREAFTER = A(i+2);
            // Go to next event
            i = i + 3;                         
            events{q}(h,2) = CHANNEL;
            events{q}(h,10) = NOTEAFTER;
            events{q}(h,11) = PRESSSUREAFTER;
         elseif (176 <= A(i))*(A(i) <= 191)
            // Control change
            STATUS = 11; 
            CHANNEL = A(i) - 176;
            CONTROL = A(i+1);
            VALUE = A(i+2);
            // Salto a evento siguiente
            i = i + 3;                         
            events{q}(h,2) = CHANNEL;
            events{q}(h,8) = CONTROL;
            events{q}(h,9) = VALUE;
         elseif (192 <= A(i))*(A(i) <= 207)
            // Program change
            STATUS = 12;
            CHANNEL = A(i) - 192;
            PROG = A(i+1)+1;
            // Go to next event
            i = i + 2;                         
            events{q}(h,2) = CHANNEL;
            events{q}(h,3) = PROG;
         elseif (208 <= A(i))*(A(i) <= 223)
            // Channel Aftertouch 
            STATUS = 13;
            CHANNEL = A(i) - 208;
            PRESSUREAFTER = A(i+1);
            // Go to next event 
            i = i + 2;                         
            events{q}(h,2) = CHANNEL;
            events{q}(h,12) = PRESSUREAFTER;
         elseif (224 <= A(i))*(A(i) <= 239)
            // Pitch change
            STATUS = 14;
            CHANNELL = A(i) - 224;
            PITCH = A(i+1) + A(i+2)*128;
            // Go to next event
            i = i + 3;                         
            events{q}(h,2) = CHANNEL;
            events{q}(h,13) = PITCH;
         elseif A(i) == 255
            // Metaevent
            // Go to byte of type of metaevent 
            i = i + 1;                         
            select A(i)
               // We do not consider here events such as
               // time signature or tempo that belong to
               // tempo track, even if it is an untidy
               // MIDI file that has notes in the tempo
               // track, since first track has already
               // been processed as tempo track
            case 47
               // End of track
               // Number of events in track
               H(q) = h;
               // Go to next track
               q = q + 1;
               // Abort loop that corresponds to ending track
               break

            otherwise
               // Unknown or unsupported
               // Metaevent (to be included
               // in the future)
               // Length of data in order to go to next event
               Ldata = A(i+1);
               // Go to next event
               i = i + Ldata + 2;
            end
         end
      end
   end 

   // Remove last row, which corresponds to an end-of-track
   // metaevent and is irrelevant
   for q=1:NTracks
      events{q} = events{q}(1:$-1,:);
   end

   // Number of events per track (first one is tempo track)
   H = H-1;


   if 1==1
      // Remove tracks which do not contain events (usually
      // this is the case for tempo track only but there
      // might be some very untidy MIDI files which have
      // empty tracks)
      r = 0;
      for q = 1:NTracks
         // Test if track actually contains events
         if size(events{q},2)>1
            // If there are events keep the track
            r = r + 1;
            events1{r} = events{q};
            H1(r) = H(q);
         end
      end

      // Update number of events per track considering 
      // only non-empty and non-tempo tracks
      H = H1;

      // Update variable events considering only non-empty
      // and non-tempo tracks
      events = events1;

      // Update number of non-empty and non-tempo tracks
      NTracks = size(events, 1);
   end


   //NUM, DEN, MIDICLK, CLK32th_24, METRO  


   // Convert to notes array
   // ----------------------

   // Convert delta-times to absolute time
   for q = 1:NTracks
      // Cumulative sum along first column
      notes{q}(:,1) = cumsum(events{q}(:,1));
   end

   // Convert absolute time in ticks of each pair 
   // Note ON - Note OFF (or pair  NOTE ON - Note ON 
   // with 0 velocity) to note duration in ticks 
   for q = 1:NTracks
      for h = 1:H(q)
         // Test if track actually contains notes to
         // avoid an error from next procedure (which
         // needs notes to exist)
         if size(events{q},2)>=5
            // Test if note-on velocity is > 0. Otherwise
            // it is not a  meaningful note
            if events{q}(h,5)>0
               // Asign MIDI pitch and velocity of h-th
               // note. It may happen that the event is
               // not a note, in which case pitch and
               // velocity are 0.
               notes{q}(h,3) = events{q}(h,4);
               notes{q}(h,4) = events{q}(h,5);

               // Note duration. Events from h through
               // the end are reviewed until a note-on
               // is found for which MIDI pitch equals
               // that for event h and has velocity 0
               // (equivalent to note off), or else it
               // is a note-off with the same MIDI pitch. 
               // When any of these conditions are 
               // reached, the current note ends.
               for hh = h:H(q)
                  // noteend is 1 if pitch equals pitch 
                  // of event h and has zero velocity or
                  // else if there there is a 6th column 
                  // (note off) and its pitch equals pitch
                  // of event h.
                  noteend = ((events{q}(hh,4)==events{q}(h,4)) & ...
                             (events{q}(hh,5)==0)) | ...
                             ((size(events{q},2) >= 6) & ...
                             (events{q}(hh,6)==events{q}(h,4))); 

                  // noteend is 0 until a note ending event
                  // is found. In such case we subtract
                  // absolute MIDI times of note start and 
                  // end
                  if noteend
                     notes{q}(h,2) = notes{q}(hh,1) - notes{q}(h,1);
                     // Terminate internal loop to go on
                     // to next note
                     break
                  end
               end
            end
         end
      end
   end


   // Add program change column
   for q = 1:NTracks
      // Test if track actually has a program change 
      // column to avoid an error in the following
      // procedure, which requires a program change
      // column (tempo track, if neat, usually does
      // not have any note, hence it does not have
      // any program change)
      if size(events{q},2)>=3
         prog = events{q}(1,3);
         for h=1:H(q)
            // Update program change, but only if
            // there is actually a change. Note that
            // a change is detected when column 3 of
            // track is > 0. This is correct, since
            // in this function we have increased 
            // program change number by 1
            if events{q}(h,3)~=prog & events{q}(h,3)>0
               prog = events{q}(h,3);
            end
            notes{q}(h,5) = prog;
         end
      end
   end


   // Remove tracks which do not contain a note 
   // (usually this is the case for tempo track
   // only, however there could be some non-empty
   // tracks which have only program change
   // events)
   r = 0;
   for q = 1:NTracks
      // Test if track actually contains notes
      if size(notes{q},2)>=4
         // If there are notes keep the track
         r = r + 1;
         notes1{r} = notes{q};
         H1(r) = H(q);
      end
   end

   clear notes

   // Remove rows which do not contain a note 
   for q = 1:size(notes1,1)
      k = 0;
      for h = 1:H1(q)
         if notes1{q}(h,4)~=0
            k = k + 1;
            notes{q}(k,:) = notes1{q}(h,:);
         end
      end
      HN(q) = k;
   end

   clear notes1

   // Update number of non-empty tracks including 
   // tempo track
   NTracks = NTracks + 1;

   info = [Div, NTracks, MIDICLK];

endfunction



