//=========================================================
//  MusE
//  Linux Music Editor
//    $Id: midirawin.cpp,v 1.1.1.1 2003/10/29 10:06:12 wschweer Exp $
//  (C) Copyright 1999 Werner Schweer (ws@seh.de)
//=========================================================

#include <stdio.h>
#include <string.h>
#include "midirawin.h"
#include "event.h"

//---------------------------------------------------------
//   MidiRawIn
//---------------------------------------------------------

MidiRawIn::MidiRawIn()
      {
      recState      = SYNC;
      sysexMsg      = 0;
      sysexSize     = 0;
      sysexMaxSize  = 0;
      }

//---------------------------------------------------------
//   processInput
//    push data into processor
//---------------------------------------------------------

void MidiRawIn::dataInput(unsigned char* buffer, int n)
      {
      unsigned char* p = buffer;

      for (int i = 0; i < n; ++i) {
            unsigned char c = p[i];
            //---------------------------------------------
            // filter Real Time Messages
            //---------------------------------------------

            if ((c & 0xf8) == 0xf8) {
                  realtimeSystemInput(c);
                  continue;
                  }
            switch (recState) {
                  case MTCQ:
                        mtcInputQuarter(c);
                        recState = SYNC;
                        break;

                  case SONGPTR1:
                        recData1 = c & 0x7f;
                        recState = SONGPTR2;
                        break;

                  case SONGPTR2:
                        recState = SYNC;
                        setSongPosition(((c & 0x7f) << 7) | recData1);
                        break;

                  case SONGSELECT:              // ignore song select
                        recState = SYNC;
                        break;

                  case SYSEX:
                        if (c == 0xf7) {
                              recState = SYNC;
                              sysexReceived(sysexMsg, sysexSize);
                              sysexSize = 0;
                              break;
                              }
                        if (c & 0x80) {
                              fprintf(stderr, "Sysex aborted, received 0x%02x\n", c);
                              channelStatus(c);
                              break;
                              }
                        sysexMsg[sysexSize++] = c;
                        if (sysexSize == sysexMaxSize) {
                              unsigned char* p = new unsigned char[sysexMaxSize*2];
                              memcpy(p, sysexMsg, sysexSize);
                              delete[] sysexMsg;
                              sysexMsg = p;
                              sysexMaxSize *= 2;
                              }
                        break;

                  case SYNC:
                        if ((c & 0x80) == 0)    // ignore input
                              break;
                        channelStatus(c);
                        break;

                  case CHANNEL:
                        if (c & 0x80) {
                              channelStatus(c);
                              break;
                              }
                        // else use running status

                  case DATA1:
                        if (c & 0x80) {
                              fprintf(stderr, "lost DATA\n");
                              channelStatus(c);
                              break;
                              }
                        switch (recChannel & 0xf0) {
                              case 0x80:
                              case 0x90:
                              case 0xa0:
                              case 0xb0:
                              case 0xe0:
                                    recData1 = c;
                                    recState = DATA2;
                                    break;
                              case 0xc0:
                              case 0xd0:
                                    recState = CHANNEL;
                                    eventReceived(recChannel, c, 0);
                                    break;
                              }
                        break;
                  case DATA2:
                        if (c & 0x80) {
                              fprintf(stderr, "lost DATA %02x %02x\n", recChannel, recData1);
                              channelStatus(c);
                              break;
                              }
                        recState = CHANNEL;
                        eventReceived(recChannel, recData1, c);
                        break;
                  }
            }
      }

//---------------------------------------------------------
//   channelStatus
//    channel status byte received
//---------------------------------------------------------

void MidiRawIn::channelStatus(unsigned char c)
      {
      if ((c & 0xf0) != 0xf0) {
            recChannel = c;
            recState = DATA1;
            return;
            }
      //
      //    System Common Messages
      //
      switch (c) {
            case 0xf0:
                  recState = SYSEX;
                  if (sysexMsg == 0) {
                        sysexMsg = new unsigned char[512];
                        sysexMaxSize = 512;
                        }
                  sysexSize = 0;
                  break;
            case 0xf1:
                  recState = MTCQ;
                  break;
            case 0xf2:  // song position
                  recState = SONGPTR1;
                  break;
            case 0xf3:  // song select
                  recState = SONGSELECT;
                  break;
            case 0xf4:
            case 0xf5:
            case 0xf6:  // tune request
                  break;

            default:
                  fprintf(stderr, "unknown RT %02x\n", recChannel);
                  break;
            }
      }
