/******************************************/
/*  Simple MIDI Text File Reader Class,   */
/*  by Perry R. Cook, 1996                */
/*  This Object can open a MIDI Text File */
/*  and parse it.  The file spec is mine  */
/*  and mine alone, but it's all text so  */
/*  you should be able to figure it out.  */
/*                                        */
/*  TSIDI (ToolKit Synthesis Instrument   */
/*  Digital Interfaceis like MIDI, but    */
/*  allows for floating point control     */
/*  changes, note numbers, etc. Example:  */
/*  noteOn(1,60.01,111.132) plays a sharp */
/*  middle C with a velocity of 111.132   */
/*                                        */
/*  Warning:  Obey column spacing in the  */
/*  text file if you try to edit it or    */
/*  create your own files.                */
/******************************************/

/******************************************/
/*  Simple Realtime MIDI Input Object for */
/*  Linux OSS.                            */
/*  First version by Paul Leonard, 1997   */
/*  Substantially reworked by             */
/*  Gary P. Scavone, February 1998, using */
/*  code from RoseGarden as a model.      */
/*                                        */
/*  This object takes MIDI from the input */
/*  stream, parses it, and turns it into  */
/*  TSIDI.                                */
/******************************************/

#include "MIDIInpt.h"

#if defined(__USS_REALTIME_)
#define NONBLOCKING_MIDI

#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/soundcard.h>       

int _seqfd;
typedef unsigned char byte;

/********************/
/* System Messages. */
/********************/

#define MIDI_SYSTEM_MSG   ((byte)0xF0)
#define MessageType(MSG)  (byte)((MSG) & ((byte)0xF0))

#define SEQUENCER_PATH "/dev/sequencer"

/* It appears that the MIDI clock increments at about
   100 ticks per second.  This is a VERY rough estimate.
 */
#define OSS_MIDI_CLOCK_RATE 100

MIDIInpt :: MIDIInpt()
{
  _seqfd = 0;

#ifdef NONBLOCKING_MIDI
  if((_seqfd = open(SEQUENCER_PATH, O_RDONLY+O_NONBLOCK, 0)) == -1)  {
#else
  if((_seqfd = open(SEQUENCER_PATH, O_RDONLY, 0)) == -1)  {
#endif
	printf( "Cannot open " SEQUENCER_PATH ". \n");
	exit(-1);
  }
}

MIDIInpt :: ~MIDIInpt()
{
  if (_seqfd != 0) close(_seqfd);
}

int MIDIInpt::nextMessage()
{
  static byte NumArgs  = 0;
  static byte ArgsLeft = 0;
  static unsigned long int lastTime = 0;
  static unsigned long int newTime = 0;
  byte InBytes[4];
  int n;

  if ((n = read(_seqfd, &InBytes, sizeof(InBytes))) == -1) {
    #ifndef NONBLOCKING_MIDI
    printf("Error reading " SEQUENCER_PATH "\n");
    #endif
    return 0;
  }

  if (n == 0) return 0;

#ifdef DEBUG
  printf("%d %d %d %d %d \n",n,InBytes[0],InBytes[1],InBytes[2],InBytes[3]);
#endif

  switch ( InBytes[0] )
	{
	case SEQ_WAIT:
	  /* MIDI clock ticks ... it appears that OSS_MIDI_CLOCK_RATE (defined above)
		 is roughly about 100 ticks per second.  However, I found no documentation
		 from OSS to back this up.  Hopefully, the rate doesn't change with 
		 different soundcards and/or CPUs!  For now, the first MIDI message 
		 deltaTime is calculated with respect to the start of the MIDI clock.
	   */
	  newTime = ((InBytes[3]<<16)|(InBytes[2]<<8)| InBytes[1]);
	  break;

	case SEQ_ECHO:
	  /* no echo events yet defined */
#ifdef DEBUG
	  fprintf(stderr,"ECHO EVENT\n");
#endif
	  break;

	case SEQ_MIDIPUTC:
	  /* Determination of a full MIDI message from the input MIDI stream is based
		 here on the observation that MIDI status bytes and subsequent data bytes
		 are NOT returned in the same read() call.  Rather, they are spread out
		 over multiple read() returns, with only a single value per return.  So,
		 if we find a status byte, we then determine the number of expected
		 operands and process that number of subsequent read()s to determine the
		 complete MIDI message. 
		 */
	  if (InBytes[1] & 0x80) { /* Status Byte */
		if (MessageType(InBytes[1]) == MIDI_SYSTEM_MSG)
		  {
			NumArgs = 0; /* no timing info */
#ifdef DEBUG
			fprintf(stderr, "SYSTEM MESSAGE\n");
#endif
		  }
		else if (MessageType(InBytes[1]) == MIDI_PGM_CHANGE ||
				 MessageType(InBytes[1]) == MIDI_CHN_PRESSURE) 
		  {
			NumArgs = 1;
		  }
		else 
		  {
			NumArgs = 2;
		  }
		channel = (int) (InBytes[1] & 0x0f);
		messageType = (int) (InBytes[1] & 0xf0);
		ArgsLeft = NumArgs;
		byteTwo = byteThree = 0.0;
	  }

	  if (ArgsLeft && !(InBytes[1] & 0x80)) { /* not a status byte */
		if (ArgsLeft == NumArgs)
		  byteTwo = (float)InBytes[1];
		else
		  {
			if (MessageType(messageType) == (int) MIDI_PITCH_BEND)
			  byteTwo = (float) InBytes[1] + (byteTwo * NORM_7);
			else
			  byteThree = (float)InBytes[1];
		  }

		--ArgsLeft;

		/* If MIDI message complete, then setup for running status mode
		   (another event of the same type without status byte).
		 */
		if ( !ArgsLeft ) {
		  if (MessageType(messageType) == (int) MIDI_PGM_CHANGE ||
			  MessageType(messageType) == (int) MIDI_CHN_PRESSURE)
			{
			  ArgsLeft = 1;
			}
		  else
			{
			  ArgsLeft = 2;
			}
		  deltaTime = (float) (newTime - lastTime) / OSS_MIDI_CLOCK_RATE;
		  lastTime = newTime;
		  return 1; /* MIDI message complete */
		}
	  }
	default:
	  break;
	}
  return 0;
}

void MIDIInpt :: printMessage()
{
  printf("type = %d, channel = %d, byte2 = %f, byte3 = %f\n",
		 this->getType(), this->getChannel(), this->getByteTwo(),
		 this->getByteThree());
}

int MIDIInpt :: getType()
{
    return messageType;
}

int MIDIInpt :: getChannel()
{
    return channel;
}

MY_FLOAT MIDIInpt :: getByteTwo()
{
    return byteTwo;
}

MY_FLOAT MIDIInpt :: getByteThree()
{
    return byteThree;
}

MY_FLOAT MIDIInpt :: getDeltaTime()
{
    return deltaTime;
}

#endif

/************   Test Main Program   *****************/
/*
void main(int argc,char *argv[])
{
    MIDIInpt testMIDI;
    
    while(1)	{
        if (testMIDI.nextMessage() > 0)
	    testMIDI.printMessage();
    }
}
*/

