function A = eventmat2midi(events, tempo, info, filename)
   // Convert an event array to a midi file
   //
   // Usage:
   //       A = eventmat2midi(event, tempo, filename)
   //
   // Where
   //        A:        Vector containing MIDI data.
   //        events:   Cell array containing all events with
   //                  corresponding data
   //        tempo:    Array containing tempo track
   //        info:     Important information
   //        filename: Path to file where MIDI data in A is to
   //                  be saved. If filename does not end with
   //                  .mid then this file extension is added.
   //
   // The structure of an event cell array is as follows. Each 
   // cell is a matrix corresponding to a 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        Metavent (0-127)
   //   15        Link to metavent (not yet supported)
   //
   // Tempo array is an Nx4 array where N is the number of 
   // tempo metaevents. Structure is as follows:
   //
   // Column      Content
   // --------------------------------------------------------
   //   1         Delta-times in ticks
   //   2         Tempo in microseconds per metronome beat
   //   3         Time signature numerator as in score
   //   4         Time signature denominator as in score
   //
   // NOTES: 1) A metronome beat is usually a quarter note.
   //        2) Time signature denominator must be a power of 2  
   //
   // info array is an array [Div, NTracks, MIDICLK] where
   //        Div:     Division, i.e., number of ticks per
   //                 quarter note
   //        NTracks: Number of tracks 
   //        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
   //
   // --------------------------------
   // Author: Federico Miyara
   // Date:   2013-01-01
   //         2013-01-04 
   //         2013-01-08
   //         2024-09-12


   /////////////////////////////////////////////
   // NOTA: Aún no depurado
   /////////////////////////////////////////////


   if argn(2) < 4
      // If filename argument is absent no data will be saved
      fileflag = 0;
   else
      fileflag = 1;
   end

   if argn(2) < 3
      // If info argument is absent assume 240 ticks per
      // quarter note
      info(1) = 240;
   end

   if argn(2) < 2
      // If tempo argument is absent, assume 120 quarter 
      // notes per minute throughout the composition
      tempo = [0, 500000];
   end

   if 1==2
      clear
      A = "ejemplo1.mid";
      fileflag = 1;
      filename  = "ejemplo3.mid";
      //info = [Div, NTracks, MIDICLK]
      exec midi2eventmat.sci;
      [events,tempo,notes,info] = midi2eventmat(A);
   end
    


   // Number of note-related tracks. Tempo track does not
   // count here. 
   NTracks = size(events,1);

   // Total number of tracks (tempo + notes)
   ntrks = NTracks + 1;

   // -------------------------------------------------
   // Header
   // -------------------------------------------------

   // Contains "MThd", 4 bytes representing data length in
   // the header (i.e, 6 for header), 2 bytes por MIDI
   // file type (here type 1 is chosen), 2 bytes that 
   // represent the number of tracks, and two bytes that
   // represent the number of ticks per quarter note 
   // (240 is chosen)
   H = [77, 84, 104, 100, 0, 0, 0, 6, 0, 1];
   H = [H, floor(ntrks/256), modulo(ntrks, 256), 0, 240];


   // -------------------------------------------------
   // Tempo track
   // -------------------------------------------------

   // It is the first track which in  at tidy type 1 MIDI 
   // file should not contain any notes. It contains the
   // keyword "MTrk", data length in  track, several
   // metaevents such as time signature, MIDI clocks per
   // metronome beat, 32th notes per quarter note (8) and
   // pairs of  delta-time and tempo events.

   // "MTrk"
   T1 = [77, 84, 114, 107];

   // Tempo data initialization
   T2 = [];

   // Number of tempo events
   Ntempo = size(tempo, 1);
   
   exec deltatime.sci;

   for k=1:Ntempo
      if tempo(k,3)~=0
         // Add time signature metaevent (255, 88, 4), 
         // numerator, denominator (as log2 of actual value), 
         // MIDI cloks per metronome beat, 32th notes per 
         // MIDI quarter note
         T2 = [T2, deltatime(tempo(k,1),0), 255, 88, 4, ...
         tempo(k,3), round(log2(tempo(k,4))), 24, 8];
      end
      if tempo(k,2)~=0
         // Convert tempo to 3-byte representation
         y = dec2byte(tempo(k,2), 3);
         // Add tempo metaevent (255, 81, 3)    
         T2 = [T2, deltatime(tempo(k,1),0), 255, 81, 3, y];
      end
   end


   // Last delta-time, metaevent (255), end of track (47),
   // no data (0) 
   //dt_end = deltatime(tempo(Ntempo,1), 0);
   //T3 = [dt_end, 255, 47, 0]; 
   T3 = [0, 255, 47, 0];

   // Length of tempo data
   LTD = length(T2) + length(T3);

   // Convert length of tempo data to 4-byte representation
   z = dec2byte(LTD,4);

   // Tempo track
   T = [T1, z, T2, T3];


   // -------------------------------------------------
   // Note tracks
   // -------------------------------------------------

   // These tracks contain notes and related information

   N = [];
   // for h = 1:NTracks 
   for q = 1:NTracks
      // Keyword "MTrk"
      N1 = [77, 84, 114, 107];

      // Initialisation of N2
      N2 = [];

      // Initialisation of MIDI_Ch and MIDI_Prg
      MIDI_Ch = 0;
      MIDI_Prg = 0;

      // Initialisation of run_stat
      run_stat = 0;

      // Number of events
      Nevent = size(events{q},1);

      for k=1:Nevent
         // Indexes to non-zero data for k-th event 
         // except delta times
         I = find(events{q}(k,2:$));

         MIDI_Ch = 0;
         if length(I)>0
            select I(1)
            case 1
               // If MIDI channel is 0 it will not be detected
               // except at the begining

               // -------------------------------------------------
               // TRY TO FIX LATER
               // -------------------------------------------------

               MIDI_Ch = events{q}(k, 2);
            case 2
               // Start running status for program change
               // NOTE: Porgram change is saved in events 
               //       cell array in the range 1-128. Hence,
               //       it must be subtractd 1 in order to
               //       return to the standard 0-127 range
               MIDI_Prg = events{q}(k,3) - 1;
               run_stat1 = 12*16 + MIDI_Ch;
               if run_stat1 == run_stat
                  // If running status is the same as the
                  // previous one, then status byte is
                  // unnecessary
                  E = MIDI_Prg;
               else
                  // If running status is new then update
                  // variable run_stat and include status
                  // byte for program change in MIDI stream
                  run_stat = run_stat1;
                  E = [run_stat, MIDI_Prg];
               end
            case {3, 4}
               // Start running status for note on
               run_stat1 = 9*16 + MIDI_Ch;
               if run_stat1==run_stat
                  // If running status is the same as the
                  // previous one, then status byte is
                  // unnecessary
                  E = [events{q}(k,4), events{q}(k,5)];
               else
                  // If running status is new then update
                  // variable run_stat and include status
                  // byte for note on in MIDI stream
                  run_stat = run_stat1;
                  E = [run_stat, events{q}(k,4), events{q}(k,5)];
               end
            case {5, 6}
               // Start running status for note off
               run_stat1 = 8*16 + MIDI_Ch;
               if run_stat1==run_stat
                  // If running status is the same as the
                  // previous one, then status byte is
                  // unnecessary
                  E = [events{q}(k,6), events{q}(k,7)];
               else
                  // If running status is new then update
                  // variable run_stat and include status
                  // byte for note off in MIDI stream
                  run_stat = run_stat1;
                  E = [8*16 + MIDI_Ch, events{q}(k,6), events{q}(k,7)];
               end
            case {7, 8}
               // Start running status for control change
               run_stat1 = 11*16 + MIDI_Ch;
               if run_stat1==run_stat
                  // If running status is the same as the
                  // previous one, then status byte is
                  // unnecessary
                  E = [events{q}(k,8), events{q}(k,9)];
               else
                  // If running status is new then update
                  // variable run_stat and include status
                  // byte for control change in MIDI stream
                  run_stat = run_stat1;
                  E = [11*16 + MIDI_Ch, events{q}(k,8), events{q}(k,9)];
               end
               // NOTE: control change 0 with data value 0
               //       is not considered
            case {9,10}
               // Start running status for polyphonic
               // aftertouch
               run_stat1 = 10*16 + MIDI_Ch;
               if run_stat1==run_stat
                  // If running status is the same as the
                  // previous one, then status byte is
                  // unnecessary               
                  E = [events{q}(k,10), events{q}(k,11)];
               else
                  // If running status is new then update
                  // variable run_stat and include status
                  // byte for control change in MIDI stream
                  run_stat = run_stat1;               
                  E = [10*16 + MIDI_Ch, events{q}(k,10), events{q}(k,11)];
               end
            case 11
               // Start running status for Channel aftertouch
               // (Channel aftertouch with data value 0 is not
               // considered)
               run_stat1 = 13*16 + MIDI_Ch;
               if run_stat1==run_stat
                  // If running status is the same as the
                  // previous one, then status byte is
                  // unnecessary and only channel mean
                  // pressure must be transmitted
                  E = [events{q}(k,12)];
               else
                  // If running status is new then update
                  // variable run_stat and include status
                  // byte for control change in MIDI stream               
                  run_stat = run_stat1;               
                  E = [13*16 + MIDI_Ch, events{q}(k,12)];
               end
            case 12
               // Start running status for pitch wheel
               // (Pitch wheel with data value 0 is not
               // considered. Generally this is not
               // important since 0 pitch means maximum
               // negative deviation)
               run_stat1 = 14*16 + MIDI_Ch;               
               pitch = dec2byte(events{q}(k,13),2)
               if run_stat1==run_stat
                  // If running status is the same as the
                  // previous one, then status byte is
                  // unnecessary and only pitch change value
                  // must be transmitted.
                  // NOTE: As least significant byte goes
                  //       first, it is necessary to reverse
                  //       bytes. 
                  E = [pitch(2), pitch(1)];
               else
                  // If running status is new then update
                  // variable run_stat and include status
                  // byte for pitch change in MIDI stream               
                  run_stat = run_stat1;               
                  E = [14*16 + MIDI_Ch, pitch(2), pitch(1)];
               end
            case 13
               // A metaevent starts (metaevents do not
               // support running status)
               // NOTES: 1) Al metaevents except end of track
               //           are ignored This will change in a
               //           future version with the addition of
               //           a new metatrack in the events cell
               //           array which will contain the links
               //           to the  metaevent data (for instance,
               //           title, author, lyrics, cues)
               //        2) In consequence data length is forced to
               //           0. This may imply loss of information,
               //           but this is not relevant, since it is
               //           quite unlikely that an events cell
               //           array might contain metaevent data
               //           before a format is defined and supported
               //           by  this function
               if events{q}(k,14)~=47
                  E = [255, events{q}(k,14), 0];
               end
            end

            // Add delta time of event and the type of event
            // with its parameters E to the note track
            N2 = [N2, deltatime(events{q}(k,1), 0), E];

         end

      end

      // Add end-of-track metaevent
      N2 = [N2, 0, 255, 47, 0];

      // Length of track data expressed with 4 bytes
      u = dec2byte(length(N2),4);

      // Add note track
      N = [N, N1, u, N2];

   end

   // Put together header, tempo track and notes track 
   // into vector A
   A = [H, T, N];

   // If a filename was passed as an input argument save
   // A as a file
   if fileflag==1
      // Find out if file name ends with extension .mid.
      // If so keep the name unaltered, otherwise add 
      // .mid at the end so to make the file easily
      // recognizable by sequencing software
      u = fileext(filename)
      if u~=".mid"
         filename  = filename + ".mid";
      end
   end
   
   fid = mopen(filename , "wb");
   mput(A, "uc", fid);
   mclose(fid);

endfunction


// -------------------------------------------------
// Local functions
// -------------------------------------------------

function y = dec2byte(x, Nbytes)
   // This function converts a number x as an Nbytes
   // 256-nary 256-nary representation
   //
   // Usage:
   //        y = dec2byte(x, Nbytes)
   // where
   //        x:      Input number to convert
   //        Nbytes: Target number of bytes (even if the
   //                most significative ones are 0)
   //        y:      Output vector of length Nbytes, most
   //                significative bytes first
   //
   // ---------------------------------
   // Author: Federico Miyara
   // Date:   2013-01-02

   z = x;
   for k = 1:Nbytes
      y(k) = floor(z / 2^((Nbytes-k)*8));
      z = modulo(z, 2^((Nbytes-k)*8));
   end
   y = y';

endfunction

