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

#include <unistd.h>
#include <cmath>

#include "sync.h"
#include "mtc.h"
#include "event.h"
#include "song.h"
#include "midithread.h"
#include "utils.h"
#include "synth.h"
#include "globals.h"
#include "mididev.h"
#include "midiitransform.h"
#include "utils.h"

extern void processMidiInputTransformPlugins(MidiPlayEvent*);

//---------------------------------------------------------
//   eventReceived
//---------------------------------------------------------

void MidiThread::eventReceived(int port, unsigned char channel, unsigned char c1, unsigned c2)
      {
      MidiPlayEvent* event = new MidiPlayEvent(port, channel & 0xf,
         channel & 0xf0, c1, c2);
      eventReceived(event);
      }

//---------------------------------------------------------
//   filterEvent
//    return true if event filtered
//---------------------------------------------------------

bool MidiThread::filterEvent(const MidiPlayEvent* event, int type, bool thru)
      {
      switch(event->type()) {
            case 0x90:
            case 0x80:
                  if (type & MIDI_FILTER_NOTEON)
                        return true;
                  break;
            case 0xa0:
                  if (type & MIDI_FILTER_POLYP)
                        return true;
                  break;
            case 0xb0:
                  if (type & MIDI_FILTER_CTRL)
                        return true;
                  if (!thru && (midiFilterCtrl1 == event->dataA()
                     || midiFilterCtrl2 == event->dataA()
                     || midiFilterCtrl3 == event->dataA()
                     || midiFilterCtrl4 == event->dataA())) {
                        return true;
                        }
                  break;
            case 0xc0:
                  if (type & MIDI_FILTER_PROGRAM)
                        return true;
                  break;
            case 0xd0:
                  if (type & MIDI_FILTER_AT)
                        return true;
                  break;
            case 0xe0:
                  if (type & MIDI_FILTER_PITCH)
                        return true;
                  break;
            case 0xf0:
                  if (type & MIDI_FILTER_SYSEX)
                        return true;
                  break;
            default:
                  break;
            }
      return false;
      }

//---------------------------------------------------------
//   Sequencer::eventReceived
//---------------------------------------------------------

void MidiThread::eventReceived(MidiPlayEvent* event)
      {
      int tick = recTimeStamp();

      if (midiInputTrace) {
            printf("MidiInput: ");
            event->dump();
            }
      //
      //  special handling for software synthesizer events
      //
      int port = event->port();           // hack
      bool fromSynthGui = (port & 0x80) == 0x80;
      if (!fromSynthGui) {
            for (iSynthI i = synthiInstances.begin(); i != synthiInstances.end(); ++i) {
                  SynthI* si = *i;
                  MidiDevice* dev = si->mdev();
                  if (dev && (dev->port() == port)) {
                        (*i)->writeToGui(event);
                        fromSynthGui = true;
                        break;
                        }
                  }
            }
      port &= 0xff;
      event->setPort(port);

      int typ = event->type();

      if (typ == 0xf0) {
            const unsigned char* p = event->data();
            int n = event->len();
            if (n >= 4) {
                  if ((p[0] == 0x7f)
                     && ((p[1] == 0x7f) || (p[1] == rxDeviceId))) {
                        if (p[2] == 0x06) {
                              mmcInput(p, n);
                              delete event;
                              return;
                              }
                        if (p[2] == 0x01) {
                              mtcInputFull(p, n);
                              delete event;
                              return;
                              }
                        }
                  else if (p[0] == 0x7e) {
                        nonRealtimeSystemSysex(p, n);
                        delete event;
                        return;
                        }
                  }
            }

      //
      // filter midi remote control events
      //
      int c1 = event->dataA();
      if (rcEnable && (typ == 0x90 || typ == 0x80)) {
            if (c1 == rcStopNote) {
                  if (typ == 0x90) {
                        write(sigFd, "0", 1);
                        }
                  delete event;
                  return;
                  }
            else if (c1 == rcRecordNote) {
                  if (typ == 0x90) {
                        write(sigFd, "2", 1);
                        }
                  delete event;
                  return;
                  }
            else if (c1 == rcGotoLeftMarkNote) {
                  if (typ == 0x90) {
                        write(sigFd, "3", 1);
                        }
                  delete event;
                  return;
                  }
            else if (c1 == rcPlayNote) {
                  if (typ == 0x90) {
                        write(sigFd, "1", 1);
                        }
                  delete event;
                  return;
                  }
            }

      processMidiInputTransformPlugins(event);

      if (filterEvent(event, midiRecordType, false)) {
            delete event;
            return;
            }
      if (!applyMidiInputTransformation(event)) {
            if (midiInputTrace)
                  printf("   midi input transformation: event filtered\n");
            delete event;
            return;
            }

      if ((event->type() == 0x90) && (noteFifoSize < REC_NOTE_FIFO_SIZE)) {
            int pv = ((event->dataA() & 0xff)<<8) + (event->dataB() & 0xff);
            recNoteFifo[noteFifoWindex] = pv;
            noteFifoWindex = (noteFifoWindex + 1) % REC_NOTE_FIFO_SIZE;
            ++noteFifoSize;
            }

      TrackList* tl = song->tracks();
      for (iTrack it = tl->begin(); it != tl->end(); ++it) {
            MidiTrack* mt = dynamic_cast<MidiTrack*>(*it);
            if (mt == 0 || !mt->recordFlag())
                  continue;
            if ((mt->inPortMask() & (1 << event->port())) && (mt->inChannelMask() & (1 << event->channel()))) {
                  if (state == PLAY && song->record()) {
                        if (loopPassed) {
                              // this is the first event in a new loop
                              //   - erase all events from previous loop
                              mt->events()->clear();
                              }
                        MidiEvent* ev = new MidiEvent(event);
                        ev->setPosTick(tick);
                        mt->events()->add(ev);
                        }

                  // echo event to current track output
                  // (if not from synth gui)

                  MidiPort* mport = &midiPorts[mt->outPort()];
                  if (!fromSynthGui && mt->midiThruFlag() && !filterEvent(event, midiThruType, true)) {
                        // event->setPort(mt->outPort());
                        event->setChannel(mt->outChannel());
                        if (event->isNote() || event->isNoteOff()) {
                              MidiPlayEvent e(*event);
                              int pitch = e.dataA() + mt->transposition;
                              if (pitch > 127)
                                    pitch = 127;
                              if (pitch < 0)
                                    pitch = 0;
                              e.setA(pitch);

                              //
                              // apply track values
                              //
                              if (!e.isNoteOff()) {
                                    int velo = e.dataB() + mt->velocity;
                                    velo = (velo * mt->compression) / 100;
                                    if (velo > 127)
                                          velo = 127;
                                    if (velo < 1)
                                          velo = 1;
                                    e.setB(velo);
                                    }
                              mport->putEvent(&e);
                              }
                        else
                              mport->putEvent(event);
                        }
                  }
            }
      loopPassed = false;
      }

