예제 #1
0
bool MD_MFTrack::getNextEvent(MD_MIDIFile *mf, uint16_t tickCount)
// track_event = <time:v> + [<midi_event> | <meta_event> | <sysex_event>]
{
	uint32_t deltaT;

	// is there anything to process?
	if (_endOfTrack)
	  return(false);

	// move the file pointer to where we left off
	mf->_fd.seekSet(_startOffset+_currOffset);

	// Work out new total elapsed ticks - include the overshoot from
	// last event.
	_elapsedTicks += tickCount;

	// Get the DeltaT from the file in order to see if enough ticks have
	// passed for the event to be active.
	deltaT = readVarLen(&mf->_fd);

  // If not enough ticks, just return without saving the file pointer and 
  // we will go back to the same spot next time.	
  if (_elapsedTicks < deltaT)
		return(false);

	// Adjust the total elapsed time to the error against actual DeltaT to avoid 
	// accumulation of errors, as we only check for _elapsedTicks being >= ticks,
	// giving positive biased errors every time.
	_elapsedTicks -= deltaT;

	DUMP("\ndT: ", deltaT);
	DUMP(" + ", _elapsedTicks);
	DUMPS("\t");

	parseEvent(mf);

	// remember the offset for next time
	_currOffset = mf->_fd.curPosition() - _startOffset;

  // catch end of track when there is no META event  
  _endOfTrack = _endOfTrack || (_currOffset >= _length);
  if (_endOfTrack) DUMPS(" - OUT OF TRACK");

	return(true);
}
예제 #2
0
int event () {
  int i;
  char b[80];
  int data;
  int event;
  int velocity;
  unsigned long length;
  char c;
  unsigned long deltime;
  int channel;

  deltime = readVarLen();

  event = *fp++;
  event &= 0xff;
  data = *fp++;
  data &= 0xff;
  if (deltime) 
    count++;
  if (brief) {
    printf("\nd-t %3d %3x ", count, deltime);
  }
  else
    printf("d-time %3d %3x ", count, deltime);


  // DATA BYTE - first bit not set
  if (!(event & 0x80)) {	/* if first bit not set it is a data byte */
    *fp--;			/* here we have many of the same events strung together */
    *fp--;
    length = old_length;
    printf(" continued data bytes "); 
    while (length--) {
      data = (*fp++)&0xFF;
      printf(" %x ", data); 
    }
    printf("\n"); 
    event = old_event;           /* where only the first is specified */
  }

  // CHANNEL EVENTS ? 8 -> E
  if (((event & 0xf0) >= 0x80) && ((event & 0xf0) <= 0xE0)) {
    channel = event & 0x0f;
    /*    if (event & 0x0f)
      (fprintf(stderr, " non zero channel set %x\n", event & 0x0f ));
    */
    if (!brief)
      printf ("channel %d ", channel);
    event &= 0xf0;
    old_event = event;
  }
  else
    old_event = event;

  switch (event) {
  case 0x80:
    velocity = *fp++;
    if (brief) 
      printf("OFF %2x ", data);
    else
      printf("Note OFF  note %2x %s velocity %x \n", data, note_to_text(data), velocity);
    on[data]= 0;
    break;
  case 0x90:
    velocity = *fp++;
    if (brief) 
      printf("On  %2x ", data);
    else
      printf("Note ON   note %2x %s velocity %x \n", data, note_to_text(data), velocity);
    on[data]= 1;
    break; 
  case 0xa0: /* AFTERTOUCH */
    printf("Polyphonic Key Pressure   data %d length %d\n", data, length);
    break;
  case 0xb0: /* MODE Message  */
    length = *fp++&0xFF;
    printf("Controller Change %d  %d\n", data, length);
    break;
  case 0xc0:
    printf("Program Change %x\n", data );
    break;
  case 0xd0: /* AFTERTOUCH */
    printf("Channel Key Pressure %x\n", data );
    break;
  case 0xe0: /* PITCH BEND */
    {
      int f;
      f=*fp++&0xFF;
      printf("Pitch Bend  %x  %x      ", data, f );

      /* extreme hack */
      /*      for (i=0; i< (f >> 4); i++ ) 
	      printf(" %x ", *fp++);
      */
      printf("\n");
    }
    break;
  case 0xf0:
    printf("Sysex event %x\n", data);
    if (data & 0x80)
      fprintf(stderr, "Sysex event, length not handled\n");
    else {
      length = data;
      for (i=0; i< data; i++ ) 
	printf(" %x ", *fp++ & 0xff);
      printf("\n"); 
    }
    break;
  case 0xff:
    if (!(data & 0xf0)) 
      length = readVarLen(fp);
    else 
      length = *fp++;
    switch (data) {
    case 0:
      printf("Sequence Number");
      {
	int seq;
	if (length != 2)
	  fprintf(stderr, "Sequence Number length %x ERROR\n", length);
	for (i=0; i< length; i++ ) 
	  b[i] = *fp++;
	seq = ((b[0]&0xff)<<8) + (b[1]&0xff);
	printf("  %x\n", seq);
      }
      break;
    case 1:
      printf("Text Event length %d\n", length);
      while (length--)
	printf("%c", *fp++);
      printf("\n");
      break;
    case 2:
      printf("Copyright length %d\n", length);
      while (length--)
	printf("%c", *fp++);
      printf("\n");
      break;
    case 3:
      printf("Sequence/Track Name length %d  ", length);
      while (length--)
	printf("%c", *fp++);
      printf("\n");
      break;
    case 4:
      printf("Instrument Name length %d  ", length);
      while (length--)
	printf("%c", *fp++);
      printf("\n");
      /*     fprintf(stderr, "NOT Implimented Yet\n"); */
      break;
    case 5:
      printf("Lyric length %d  ", length);
      while (length--)
	printf("%c", *fp++);
      printf("\n");
      break;
    case 6:
      printf("Marker length %d  ", length);
      while (length--)
	printf("%c", *fp++);
      printf("\n");
      break;
    case 7:
      printf("Cue Point length %d  ", length);
      while (length--)
	printf("%c", *fp++);
      printf("\n");
      break;
    case 0x20:
      printf("MIDI Channel Prefix  channel ");
      /*     *fp++; */
      printf("%x \n", *fp++);
      break;
    case 0x21:
      printf("MIDI Port (obsolete) length %x  port number ", length);
      while (length--)
	printf("%x", *fp++);
      printf("\n");
      break;
    case 0x51:
      {
	int i=0;
	int tmp=0;
	printf("Set Tempo microseconds/quarter note  ");
	if (length != 3)
	  fprintf(stderr, "Set Tempo length ERROR\n");
	{ 
	  int v;
	  while (length--) {
	    v = *fp++&0xff;
	    tmp += (v << (length * 8 ));
	    /*	    printf("%02x ", v); */
	  }
	  printf(" %x %d \n", tmp, tmp);
	}
      }
      break;
    case 0x53:
      printf("Unknown Meta Event 0x53 %d  ", length);
      fprintf(stderr, "NOT Implimented Yet\n");
      break;
    case 0x54:
      printf("SMPTE Offset length %x  ", length);
      while (length--)
	printf("%x ", *fp++);
      printf("\n");
      break;
    case 0x58:
      {
	int num, denom, clocks, bb;
	printf("Time Signature ");
	if (length != 4)
	  fprintf(stderr, "Time Signature length ERROR %d\n", length);
	num = *fp++&0xff;
	denom = *fp++&0xff;
	clocks = *fp++&0xff;
	bb = *fp++&0xff;
	printf(
	       "%d/%d   %x clocks per metronome tick, %x 32nd notes per 24 midi clocks\n",
	       num, (int)pow(2.0, (double)denom), clocks, bb);
      }
      break;
    case 0x59:
      {
	int sf, mi;
	printf("Key Signature ");
	if (length != 2)
	  fprintf(stderr, "Key Signature length ERROR\n");
	sf = *fp++&0xff;
	mi = *fp++&0xff;
	printf(
	       "num sharps or flats %d major or minor %x\n", sf, mi);
      }
      break;
    case 0x7f:
      printf("Sequencer Specific Meta-Event ");
      { 
	int v;
	while (length--) {
	  v = *fp++&0xff;
	  printf(" %x ", v);
	}
	printf("\n");
      }
      break;
    case 0x2f:
      printf("End of Track\n", length);
      return (0);
      break;
    }
    break;
  default:
    fprintf(stderr, "UNHANDLED EVENT data %x length %x event %x\n", 
	    data, length, event);
    exit(0);

    break;
  }
  return (1);
}
예제 #3
0
int parseEvents(uint8_t * data, uint32_t size)
{
//for debugging chunks
  int total = 0;
  int pos = 0;
  
  //remember last event type
  uint8_t lastEvent;
  while(pos < size)
    {
      int unknown = 0;
      uint32_t delta = readVarLen(data + pos, &pos);
      uint8_t type = *(data + pos) & 0xF0;
      uint8_t chan = *(data+pos) & 0x0F;
      //check if the MSB of the type is set
      if(type & 0x80)//then we have a new event
	{
	      	lastEvent = type | chan;
	      	pos++;
	      	printf("==Parsing MIDI event==\n");
	      	printf("delta = %d, pos = 0x%X\n", delta, pos);
	      	printf("Type = 0x%02X, Channel = 0x%02X\n", type, chan);
	}
	else //same event 
	{
		type = lastEvent & 0xF0;
		chan = lastEvent & 0x0F;
		printf("Same event detected: Type = 0x%02X, Channel = 0x%02X\n", type, chan);
	}
      switch (type)
	{
	  uint8_t param1;
	  uint8_t param2;
	  uint8_t m_type;
	  uint32_t m_len;
	  uint8_t * m_data;

	case 0x80:
	  //TODO NOTE OFF event
	  param1 = *(uint8_t *)(data + pos++);
	  param2 = *(uint8_t *)(data + pos++);
	  printf("Note OFF: Note=%d, Velocity=%d, delta=%d\n", param1, param2, delta);
	  if(!chan && !firstNote)
	  {
	  		if(delta)
	  		{
		  		printf("%d/%d\n", sumNote, numNote);
		  		int note = sumNote / numNote;
		  		//printf("play(%d, %d)\n", note, delta);
		  		printf("play(%d, %d)\n",
		  			 //(int)(220 * pow(2,  (note-57)/12.0 )),
		  			 ((1.0/(220 * pow(2,  (note-57)/12.0 )))/(64.0/20000000.0)) - 1,
		  			 (int)( (0.0029585799 * delta) * (220 * pow(2,  (note-57)/12.0 ) ) )
		  			 );
		  		 
		  		sumNote -= param1;
		  		numNote--;
	  		}
	  		else
	  		{
	  			sumNote -= param1;
		  		numNote--;
	  		}
	  }
	  break;

	case 0x90:
	  //TODO NOTE ON event
	  param1 = *(uint8_t *)(data + pos++);
	  param2 = *(uint8_t *)(data + pos++);
	  printf("Note ON: Note=%d, Velocity=%d, delta=%d\n", param1, param2, delta);
	  _d(data + pos - 3, 3);
	  if(!chan)
	  {
	  	//printf("Note ON: Note=%d, Velocity=%d, delta=%d\n", param1, param2, delta);
	  	if(firstNote)
	  	{
	  		sumNote += param1;
	  		numNote++;
	  		firstNote = 0;
	  	}
	  	else
	  	{
	  		sumNote += param1;
	  		numNote++;
	  	}
	  }
	  break;

	case 0xA0:
	  param1 = *(uint8_t *)(data + pos++);
	  param2 = *(uint8_t *)(data + pos++);
	  printf("Note Aftertouch: Note=%d, Amount=%d\n", param1, param2);
	  _d(data + pos - 3, 3);
	  break;

	case 0xB0:
	  param1 = *(uint8_t *)(data + pos++);
	  param2 = *(uint8_t *)(data + pos++);
	  printf("Controller Event: Type=%d, Value=%d\n", param1, param2);
	  break;
	  
	case 0xC0:
	  param1 = *(uint8_t *)(data + pos++);
	  printf("Program Change Event: Program Numbe=%d\n", param1);
	  break;

	case 0xF0://SysEx event
	  
	  //SysEx parsed below META
	  

	  //case 0xFF://META event
	  if(chan == 0x0F)//META event	  
	    {
	      m_type = *(data + pos);
	      pos++;
	      m_len = readVarLen(data + pos, &pos);
	  

	      //now each meta event
	      switch (m_type)
		{
		case 0x01:
		
		//experimental
		//if(strlen(data+pos) > m_len)
		//	m_len = strlen(data+pos)+1;
		//
		  m_data = (uint8_t *)malloc(m_len);
		  memcpy(m_data, data+pos, m_len);
		  pos += m_len;
		  printf("\tMETA: Text [%s]\n", m_data);
		  _d(m_data, m_len);
		  free(m_data);
		  break;
		case 0x02:
		  m_data = (uint8_t *)malloc(m_len);
		  memcpy(m_data, data+pos, m_len);
		  pos += m_len;
		  printf("\tMETA: Copyright notice [%s]\n", m_data);
		  free(m_data);
		  break;

		case 0x03:
		  m_data = (uint8_t *)malloc(m_len);
		  memcpy(m_data, data+pos, m_len);
		  pos += m_len;
		  printf("\tMETA: Track Name [%s]\n", m_data);
		  free(m_data);
		  break;
		  
		case 0x07:
		  m_data = (uint8_t *)malloc(m_len);
		  memcpy(m_data, data+pos, m_len);
		  pos += m_len;
		  printf("\tMETA: Cue Point [%s]\n", m_data);
		  free(m_data);
		  break;

		case 0x2F:
		  printf("\tMETA: End of Track.\n");
		  break;

		case 0x51:
		  //TODO: this may be important
		  m_data = (uint8_t *)malloc(m_len);
		  memcpy(m_data, data+pos, m_len);
		  pos += m_len;
		  printf("\tMETA: MPQN = %d\n",
			 ( *(uint8_t *)(m_data) << 16) + 
			 ( *(uint8_t *)(m_data+1)  << 8) +
			 *(uint8_t *)(m_data+2)  
			 );
		  free(m_data);
		  break;

		case 0x58:
		  //TODO time signature
		  m_data = (uint8_t *)malloc(m_len);
		  memcpy(m_data, data+pos, m_len);
		  pos += m_len;
		  printf("\tMETA: Time Signature: Number=%d, Denom=%d, Metro=%d 32nds=%d\n",
			 *(uint8_t *)(m_data),
			 *(uint8_t *)(m_data+1),
			 *(uint8_t *)(m_data+2),
			 *(uint8_t *)(m_data+3)
			 );
		  free(m_data);
		  break;
		  
		case 0x59:
		  //TODO key signature
		  m_data = (uint8_t *)malloc(m_len);
		  memcpy(m_data, data+pos, m_len);
		  pos += m_len;
		  printf("\tMETA: Key:%d, Scale:%d\n",
			 *(uint8_t *)(m_data),
			 *(uint8_t *)(m_data+1)
			 );
		  free(m_data);
		  break;
		  
		default:
		  unknown = 1;
		  printf("\tUnknown Meta-Event... [%02X]\n", m_type);
		}
	      break;//end META event
	    }

	  //SysEx event
	  m_len = readVarLen(data + pos, &pos);
	  m_data = (uint8_t *)malloc(m_len);	  
	  pos += m_len;
	  printf("\tSysEx: read %d bytes... skipping.\n", m_len);
	  free(m_data);
	  break;

	default:
	  unknown = 1;
	  printf("Unknown Event... [0x%02X]\n", type);
	}

      if(unknown)
	break;
    }

  
}
예제 #4
0
//
// Got a lot of good ideas from pykaraoke by Kelvin Lawson ([email protected]). Thanks!
//
void CKaraokeLyricsTextKAR::parseMIDI()
{
  m_midiOffset = 0;

  // Bytes 0-4: header
  unsigned int header = readDword();

  // If we get MS RIFF header, skip it
  if ( header == 0x52494646 )
  {
    setPos( currentPos() + 16 );
    header = readDword();
  }

  // MIDI header
  if ( header != 0x4D546864 )
    throw( "Not a MIDI file" );

  // Bytes 5-8: header length
  unsigned int header_length = readDword();

  // Bytes 9-10: format
  unsigned short format = readWord();

  if ( format > 2 )
    throw( "Unsupported format" );

  // Bytes 11-12: tracks
  unsigned short tracks = readWord();

  // Bytes 13-14: divisious
  unsigned short divisions = readWord();

  if ( divisions > 32768 )
    throw( "Unsupported division" );

  // Number of tracks is always 1 if format is 0
  if ( format == 0 )
    tracks = 1;

  // Parsed per-channel info
  std::vector<MidiLyrics> lyrics;
  std::vector<MidiTempo> tempos;
  std::vector<MidiChannelInfo> channels;

  channels.resize( tracks );

  // Set up default tempo
  MidiTempo te;
  te.clocks = 0;
  te.tempo = 500000;
  tempos.push_back( te );

  int preferred_lyrics_track = -1;
  int lastchannel = 0;
  int laststatus = 0;
  unsigned int firstNoteClocks = 1000000000; // arbitrary large value
  unsigned int next_line_flag = 0;

  // Point to first byte after MIDI header
  setPos( 8 + header_length );

  // Parse all tracks
  for ( int track = 0; track < tracks; track++ )
  {
    char tempbuf[1024];
    unsigned int clocks = 0;

    channels[track].total_lyrics = 0;
    channels[track].total_lyrics_space = 0;

    // Skip malformed files
    if ( readDword() != 0x4D54726B )
      throw( "Malformed track header" );

    // Next track position
    int tracklen = readDword();
    unsigned int nexttrackstart = tracklen + currentPos();

    // Parse track until end of track event
    while ( currentPos() < nexttrackstart )
    {
      // field length
      clocks += readVarLen();
      unsigned char msgtype = readByte();

      //
      // Meta event
      //
      if ( msgtype == 0xFF )
      {
        unsigned char metatype = readByte();
        unsigned int metalength = readVarLen();

        if ( metatype == 3 )
        {
          // Track title metatype
          if ( metalength >= sizeof( tempbuf ) )
            throw( "Meta event too long" );

          readData( tempbuf, metalength );
          tempbuf[metalength] = '\0';

          if ( !strcmp( tempbuf, "Words" ) )
            preferred_lyrics_track = track;
        }
        else if ( metatype == 5 || metatype == 1 )
        {
          // Lyrics metatype
          if ( metalength >= sizeof( tempbuf ) )
            throw( "Meta event too long" );

          readData( tempbuf, metalength );
          tempbuf[metalength] = '\0';

          if ( (tempbuf[0] == '@' && tempbuf[1] >= 'A' && tempbuf[1] <= 'Z')
          || strstr( tempbuf, " SYX" ) || strstr( tempbuf, "Track-" )
          || strstr( tempbuf, "%-" ) || strstr( tempbuf, "%+" ) )
          {
            // Keywords
            if ( tempbuf[0] == '@' && tempbuf[1] == 'T' && strlen( tempbuf + 2 ) > 0 )
            {
              if ( m_songName.empty() )
                m_songName = convertText( tempbuf + 2 );
              else
              {
                if ( !m_artist.empty() )
                  m_artist += "[CR]";

                m_artist += convertText( tempbuf + 2 );
              }
            }
          }
          else
          {
            MidiLyrics lyric;
            lyric.clocks = clocks;
            lyric.track = track;
            lyric.flags = next_line_flag;

            if ( tempbuf[0] == '\\' )
            {
              lyric.flags = CKaraokeLyricsText::LYRICS_NEW_PARAGRAPH;
              lyric.text = convertText( tempbuf + 1 );
            }
            else if ( tempbuf[0] == '/' )
            {
              lyric.flags = CKaraokeLyricsText::LYRICS_NEW_LINE;
              lyric.text = convertText( tempbuf + 1 );
            }
            else if ( tempbuf[1] == '\0' && (tempbuf[0] == '\n' || tempbuf[0] == '\r' ) )
            {
              // An empty line; do not add it but set the flag
              if ( next_line_flag == CKaraokeLyricsText::LYRICS_NEW_LINE )
                next_line_flag = CKaraokeLyricsText::LYRICS_NEW_PARAGRAPH;
              else
                next_line_flag = CKaraokeLyricsText::LYRICS_NEW_LINE;
            }
            else
            {
              next_line_flag = (strchr(tempbuf, '\n') || strchr(tempbuf, '\r')) ? CKaraokeLyricsText::LYRICS_NEW_LINE : CKaraokeLyricsText::LYRICS_NONE;
              lyric.text = convertText( tempbuf );
            }

            lyrics.push_back( lyric );

            // Calculate the number of spaces in current syllable
            for ( unsigned int j = 0; j < metalength; j++ )
            {
              channels[ track ].total_lyrics++;

              if ( tempbuf[j] == 0x20 )
                channels[ track ].total_lyrics_space++;
            }
          }
        }
        else if ( metatype == 0x51 )
        {
          // Set tempo event
          if ( metalength != 3 )
            throw( "Invalid tempo" );

          unsigned char a1 = readByte();
          unsigned char a2 = readByte();
          unsigned char a3 = readByte();
          unsigned int tempo = (a1 << 16) | (a2 << 8) | a3;

          // MIDI spec says tempo could only be on the first track...
          // but some MIDI editors still put it on second. Shouldn't break anything anyway, but let's see
          //if ( track != 0 )
          //  throw( "Invalid tempo track" );

          // Check tempo array. If previous tempo has higher clocks, abort.
          if ( tempos.size() > 0 && tempos[ tempos.size() - 1 ].clocks > clocks )
            throw( "Invalid tempo" );

          // If previous tempo has the same clocks value, override it. Otherwise add new.
          if ( tempos.size() > 0 && tempos[ tempos.size() - 1 ].clocks == clocks )
            tempos[ tempos.size() - 1 ].tempo = tempo;
          else
          {
            MidiTempo mt;
            mt.clocks = clocks;
            mt.tempo = tempo;

            tempos.push_back( mt );
          }
        }
        else
        {
          // Skip the event completely
          setPos( currentPos() + metalength );
        }
      }
      else if ( msgtype== 0xF0 || msgtype == 0xF7 )
      {
        // SysEx event
        unsigned int length = readVarLen();
        setPos( currentPos() + length );
      }
      else
      {
        // Regular MIDI event
        if ( msgtype & 0x80 )
        {
          // Status byte
          laststatus = ( msgtype >> 4) & 0x07;
          lastchannel = msgtype & 0x0F;

          if ( laststatus != 0x07 )
            msgtype = readByte() & 0x7F;
        }

        switch ( laststatus )
        {
          case 0:  // Note off
            readByte();
            break;

          case 1: // Note on
            if ( (readByte() & 0x7F) != 0 ) // this would be in fact Note off
            {
              // Remember the time the first note played
              if ( firstNoteClocks > clocks )
                firstNoteClocks = clocks;
            }
            break;

          case 2: // Key Pressure
          case 3: // Control change
          case 6: // Pitch wheel
            readByte();
            break;

          case 4: // Program change
          case 5: // Channel pressure
            break;

          default: // case 7: Ignore this event
            if ( (lastchannel & 0x0F) == 2 ) // Sys Com Song Position Pntr
              readWord();
            else if ( (lastchannel & 0x0F) == 3 ) // Sys Com Song Select(Song #)
              readByte();
            break;
        }
      }
    }
  }
예제 #5
0
void MD_MFTrack::parseEvent(MD_MIDIFile *mf)
// process the event from the physical file
{
	uint8_t	 eType;
	uint32_t eLen, mLen;
	sysex_event	sev;		// used for sysex callback function

	// now we have to process this event
	eType = mf->_fd.read();

	switch (eType)
	{
// ---------------------------- MIDI

	  // midi_event = any MIDI channel message, including running status
	  // Midi events (status bytes 0x8n - 0xEn) The standard Channel MIDI messages, where 'n' is the MIDI channel (0 - 15).
	  // This status byte will be followed by 1 or 2 data bytes, as is usual for the particular MIDI message. 
	  // Any valid Channel MIDI message can be included in a MIDI file.
	case 0x80 ... 0xBf:	// MIDI message with 2 parameters
	case 0xe0 ... 0xef:
		_mev.size = 3;
		_mev.data[0] = eType;
		_mev.channel = _mev.data[0] & 0xf;	// mask off the channel
		_mev.data[0] = _mev.data[0] & 0xf0;	// just the command byte
		_mev.data[1] = mf->_fd.read();
		_mev.data[2] = mf->_fd.read();
		DUMP("[MID2] Ch: ", _mev.channel);
		DUMPX(" Data: ", _mev.data[0]);
		DUMPX(" ", _mev.data[1]);
		DUMPX(" ", _mev.data[2]);
#if !DUMP_DATA
		if (mf->_midiHandler != NULL)
			(mf->_midiHandler)(&_mev);
#endif // !DUMP_DATA
	break;

	case 0xc0 ... 0xdf:	// MIDI message with 1 parameter
		_mev.size = 2;
		_mev.data[0] = eType;
		_mev.channel = _mev.data[0] & 0xf;	// mask off the channel
		_mev.data[0] = _mev.data[0] & 0xf0;	// just the command byte
		_mev.data[1] = mf->_fd.read();
		DUMP("[MID1] Ch: ", _mev.channel);
		DUMPX(" Data: ", _mev.data[0]);
		DUMPX(" ", _mev.data[1]);

#if !DUMP_DATA
		if (mf->_midiHandler != NULL)
			(mf->_midiHandler)(&_mev);
#endif
	break;

	case 0x00 ... 0x7f:	// MIDI run on message
	{
	  // If the first (status) byte is less than 128 (0x80), this implies that MIDI 
	  // running status is in effect, and that this byte is actually the first data byte 
	  // (the status carrying over from the previous MIDI event). 
	  // This can only be the case if the immediately previous event was also a MIDI event, 
	  // ie SysEx and Meta events clear running status. This means that the _mev structure 
	  // should contain the info from the previous message in the structure's channel member 
	  // and data[0] (for the MIDI command). 
	  // Hence start saving the data at byte data[1] with the byte we have just read (eType) 
	  // and use the size member to determine how large the message is (ie, same as before).
	  _mev.data[1] = eType;
	  for (uint8_t i = 2; i < _mev.size; i++)
	  {
		  _mev.data[i] = mf->_fd.read();	// next byte
	  }	

	  DUMP("[MID+] Ch: ", _mev.channel);
	  DUMPS(" Data:");
	  for (uint8_t i = 0; i<_mev.size; i++)
	  {
  		DUMPX(" ", _mev.data[i]);
	  }

#if !DUMP_DATA
	  if (mf->_midiHandler != NULL)
		  (mf->_midiHandler)(&_mev);
#endif
	}
	break;
	  
// ---------------------------- SYSEX

	case 0xf0:		// sysex_event = 0xF0 + <len:1> + <data_bytes> + 0xF7 
	case 0xf7:		// sysex_event = 0xF7 + <len:1> + <data_bytes> + 0xF7 
	{
 		uint8_t	c, i;
		sysex_event sev;

		// collect all the bytes until the 0xf7 - boundaries are included in the message
		sev.track = _trackId;
		sev.data[0] = eType;
		sev.size = mf->_fd.read();
		// The length parameter includes the 0xF7 but not the start boundary. 
		// However, it may be bigger than our buffer will allow us to store.
		sev.size = (sev.size > BUF_SIZE(sev.data) - 2 ? BUF_SIZE(sev.data) - 2 : sev.size + 1);
		for (i = 1; (i < sev.size) && (c != 0xf7); i++)
		{
			c = mf->_fd.read();	// next char
			sev.data[i] = c;
		}	

		// check if we had an overflow
		if (c != 0xf7)
		{
			while ((c = mf->_fd.read()) != 0xf7)
				; // skip read all data
			sev.data[sev.size] = 0xf7;		// terminate properly - data is probably nuked anyway
		}

		DUMPS("[SYSX] Data:");
		for (uint8_t i = 0; i<sev.size; i++)
		{
  			DUMPX(" ", sev.data[i]);
		}

#if !DUMP_DATA
		if (mf->_sysexHandler != NULL)
			(mf->_sysexHandler)(&sev);
#endif
	}
	break;

// ---------------------------- META

	case 0xff:		// meta_event = 0xFF + <meta_type:1> + <length:v> + <event_data_bytes>
	  eType = mf->_fd.read();
	  mLen =  readVarLen(&mf->_fd);
	  DUMPX("[META] Type: 0x", eType);
	  DUMP("\tLen: ", mLen);
	  DUMPS("\t");

	  switch (eType)
	  {
	  case 0x2f:	// End of track
		_endOfTrack = true;
	  DUMPS("END OF TRACK");
	  break;

	  case 0x51:	// set Tempo - really the microseconds per tick
		mf->setMicrosecondPerQuarterNote(readMultiByte(&mf->_fd, MB_TRYTE));
		DUMP("SET TEMPO to ", mf->getTickTime());
		DUMP(" us/tick or ", mf->getTempo());
		DUMPS(" beats/min");
	  break;

	  case 0x58:	// time signature
		mf->setTimeSignature(mf->_fd.read(), (1 << mf->_fd.read()));	// denominator is 2^n
		mf->_fd.seekCur(mLen-2);
		DUMP("SET TIME SIGNATURE to ", mf->getTimeSignature()>>8);
		DUMP("/", mf->getTimeSignature()&0xf);
	  break;

#if SHOW_UNUSED_META
	  case 0x0:	// Sequence Number
		DUMP("SEQUENCE NUMBER ", readMultiByte(&mf->_fd, MB_WORD));
	  break;

	  case 0x1:	// Text
		DUMPS("TEXT ");
		for (int i=0; i<mLen; i++)
			DUMP("", (char)mf->_fd.read());
	  break;

	  case 0x2:	// Copyright Notice
		DUMPS("COPYRIGHT ");
		for (uint8_t i=0; i<mLen; i++)
			DUMP("", (char)mf->_fd.read());
	  break;

	  case 0x3:	// Sequence or Track Name
		DUMPS("SEQ/TRK NAME ");
		for (uint8_t i=0; i<mLen; i++)
			DUMP("", (char)mf->_fd.read());
	  break;

	  case 0x4:	// Instrument Name
		DUMPS("INSTRUMENT ");
		for (uint8_t i=0; i<mLen; i++)
			DUMP("", (char)mf->_fd.read());
	  break;

	  case 0x5:	// Lyric
		DUMPS("LYRIC ");
		for (uint8_t i=0; i<mLen; i++)
			DUMP("", (char)mf->_fd.read());
	  break;

	  case 0x6:	// Marker
		DUMPS("MARKER ");
		for (uint8_t i=0; i<mLen; i++)
			DUMP("", (char)mf->_fd.read());
	  break;

	  case 0x7:	// Cue Point
		DUMPS("CUE POINT ");
		for (uint8_t i=0; i<mLen; i++)
			DUMP("", (char)mf->_fd.read());
	  break;

	  case 0x20:// Channel Prefix
		DUMP("CHANNEL PREFIX ", readMultiByte(&mf->_fd, MB_BYTE));
	  break;

	  case 0x21:// Port Prefix
		DUMP("PORT PREFIX ", readMultiByte(&mf->_fd, MB_BYTE));
	  break;

	  case 0x54:// SMPTE Offset
		DUMPS("SMPTE OFFSET");
		for (uint8_t i=0; i<mLen; i++)
		{
			DUMP(" ", mf->_fd.read());
		}
	  break;

	  case 0x59:// Key Signature
		DUMPS("KEY SIGNATURE");
		for (uint8_t i=0; i<mLen; i++)
		{
			DUMP(" ", mf->_fd.read());
		}
	  break;

	  case 0x7F:	// Sequencer Specific Metadata
		DUMPS("SEQ SPECIFIC");
		for (uint8_t i=0; i<mLen; i++)
		{
			DUMPX(" ", mf->_fd.read());
		}
	  break;
#endif // SHOW_UNUSED_META

	  default:
		  mf->_fd.seekCur(mLen);
		  DUMPS("IGNORED");
	  }
	  break;

// ---------------------------- UNKNOWN

	default:
		// stop playing this track as we cannot identify the eType
		_endOfTrack = true;	
		DUMPX("[UKNOWN 0x", eType);
		DUMPS("] Track aborted");
	}
}