//=============================================================================
//  MusE
//  Linux Music Editor
//  $Id:$
//
//  Copyright (C) 2002-2006 by Werner Schweer and others
//
//  This program is free software; you can redistribute it and/or modify
//  it under the terms of the GNU General Public License version 2.
//
//  This program is distributed in the hope that it will be useful,
//  but WITHOUT ANY WARRANTY; without even the implied warranty of
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//  GNU General Public License for more details.
//
//  You should have received a copy of the GNU General Public License
//  along with this program; if not, write to the Free Software
//  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
//=============================================================================

#include "track.h"
#include "song.h"
#include "al/tempo.h"
#include "al/xml.h"
#include "icons.h"
#include "audio.h"
#include "gconfig.h"
#include "midictrl.h"
#include "part.h"
#include "gui.h"
#include "audiodev.h"

// synchronize with TrackType!:

const char* Track::_cname[] = {
      "AudioOut", "Group", "Wave", "AudioIn",
      "Synth", "Midi", "MidiOut", "MidiIn", "M-Synth"
      };

const char* Track::_clname[] = {
      "Audio Output", "Audio Group", "Wave Track", "Audio Input",
      "Synti", "Midi Track", "Midi Output", "Midi Input",
      "Midi Synth"
      };

//---------------------------------------------------------
//   ArrangerTrack
//---------------------------------------------------------

ArrangerTrack::ArrangerTrack()
	{
	tw   = 0;
      ctrl = -1;        // first ctrl in list
      controller = 0;
      h    = defaultTrackHeight;
      }

//---------------------------------------------------------
//   ccolor
//    return track specific track background color
//---------------------------------------------------------

QColor Track::ccolor() const
      {
      return config.trackBg[type()];
      }

//---------------------------------------------------------
//   pixmap
//---------------------------------------------------------

QPixmap* Track::pixmap(TrackType t)
      {
      switch(t) {
            case AUDIO_OUTPUT: return addtrack_audiooutputIcon;
            case AUDIO_GROUP:  return addtrack_audiogroupIcon;
            case WAVE:         return addtrack_wavetrackIcon;
            case AUDIO_INPUT:  return addtrack_audioinputIcon;
            case AUDIO_SOFTSYNTH: return addtrack_audioinputIcon; // DEBUG
            default:
            case MIDI:         return addtrack_addmiditrackIcon;
            case MIDI_OUT:     return addtrack_addmiditrackIcon;
            case MIDI_IN:      return addtrack_addmiditrackIcon;
            }
      }

//---------------------------------------------------------
//   Track
//---------------------------------------------------------

Track::Track()
      {
      _tt            = AL::TICKS;
      _recordFlag    = false;
      _monitor       = false;
      _mute          = false;
      _solo          = false;
      _off           = false;
      _channels      = 0;           // 1 - mono, 2 - stereo
      _selected      = false;
      _locked        = false;
      _autoRead      = autoReadDefault();
      _autoWrite     = autoWriteDefault();

      for (int i = 0; i < MAX_CHANNELS; ++i) {
            _meter[i]     = 0.0f;
            _peak[i]      = 0.0f;
            _peakTimer[i] = 0;
            }
      _sendSync       = false;
      _deviceId       = 127;
      _parts = new PartList;
      }

//---------------------------------------------------------
//   ~Track
//---------------------------------------------------------

Track::~Track()
	{
      delete _parts;

      for (int i = 0; i < MAX_CHANNELS; ++i) {
//            if (!_alsaPort[i].isZero())
//                  midiDriver->unregisterPort(_alsaPort[i]);
            if (!_jackPort[i].isZero())
                  audioDriver->unregisterPort(_jackPort[i]);
            }

      }

//---------------------------------------------------------
//   setDefaultName
//    generate unique name for track
//---------------------------------------------------------

void Track::setDefaultName()
      {
      QString base;
      switch(type()) {
            case MIDI:
            case WAVE:
                  base = QString("Track");
                  break;
            case AUDIO_GROUP:
                  base = QString("Group");
                  break;
            case AUDIO_SOFTSYNTH:
                  // base = QString("Synth");
            	return;
            case AUDIO_OUTPUT:
            case AUDIO_INPUT:
            case MIDI_OUT:
            case MIDI_IN:
            case MIDI_SYNTI:
            case TRACK_TYPES:
                  base = cname();
                  break;
            };
      //
      // create unique name
      //
      for (int i = 1;; ++i) {
            QString s;
            if (i == 1)
                  s = base;
            else
                  s = QString("%1 %2").arg(base).arg(i);
            bool found = false;
            TrackList* tl = song->tracks();
            for (iTrack it = tl->begin(); it != tl->end(); ++it) {
                  Track* track = *it;
                  if (track->name() == s) {
                        found = true;
                        break;
                        }
                  }
            if (!found) {
                  setName(s);
                  break;
                  }
            }
      }

//---------------------------------------------------------
//   dump
//---------------------------------------------------------

void Track::dump() const
      {
      printf("Track <%s>: typ %d, parts %zd sel %d\n",
         _name.toLatin1().data(), type(), _parts->size(), _selected);
      }

//---------------------------------------------------------
//   addPart
//---------------------------------------------------------

void Track::addPart(Part* p)
      {
      p->setTrack(this);
      _parts->add(p);
      }

//---------------------------------------------------------
//   findPart
//---------------------------------------------------------

Part* Track::findPart(unsigned tick)
      {
      for (iPart i = _parts->begin(); i != _parts->end(); ++i) {
            Part* part = i->second;
            if (tick >= part->tick() && tick < (part->tick()+part->lenTick()))
                  return part;
            }
      return 0;
      }

//---------------------------------------------------------
//   Track::writeProperties
//---------------------------------------------------------

void Track::writeProperties(Xml& xml) const
      {
      xml.tag("name", _name);
      if (!_comment.isEmpty())
            xml.tag("comment", _comment);
      if (_recordFlag)
            xml.tag("record", _recordFlag);
      if (mute() != muteDefault())
            xml.tag("mute", mute());
      if (solo())
            xml.tag("solo", solo());
      if (off())
            xml.tag("off", off());
      if (_channels)
            xml.tag("channels", _channels);
      if (_locked)
            xml.tag("locked", _locked);
      if (_monitor)
            xml.tag("monitor", _monitor);
      if (_autoRead != autoReadDefault())
            xml.tag("autoRead", _autoRead);
      if (_autoWrite != autoWriteDefault())
            xml.tag("autoWrite", _autoWrite);
      if (_selected)
            xml.tag("selected", _selected);
      for (ciCtrl icl = controller()->begin(); icl != controller()->end(); ++icl)
            icl->second->write(xml);
      if (arrangerTrack.tw)
            xml.tag("height", arrangerTrack.tw->height());
      for (ciArrangerTrack i = subtracks.begin(); i != subtracks.end(); ++i) {
            xml.stag("subtrack");
            xml.tag("height", (*i)->tw->height());
            xml.tag("ctrl", (*i)->ctrl);
            xml.etag("subtrack");
            }
      }

//---------------------------------------------------------
//   Track::readProperties
//---------------------------------------------------------

bool Track::readProperties(QDomNode node)
      {
      QDomElement e = node.toElement();
      QString tag(e.tagName());
      QString s(e.text());
      int i = s.toInt();

      if (tag == "name")
            setName(s);
      else if (tag == "comment")
            _comment = s;
      else if (tag == "record") {
            bool recordFlag = i;
            setRecordFlag(recordFlag);
            }
      else if (tag == "mute")
            _mute = i;
      else if (tag == "solo")
            _solo = i;
      else if (tag == "off")
            _off = i;
      else if (tag == "channels")
            _channels = i;
      else if (tag == "locked")
            _locked = i;
      else if (tag == "monitor")
            _monitor = i;
      else if (tag == "selected")
            _selected = i;
      else if (tag == "autoRead")
            _autoRead = i;
      else if (tag == "autoWrite")
            _autoWrite = i;
      else if (tag == "controller") {
            Ctrl* l = new Ctrl();
            l->read(node, false);

            iCtrl icl = controller()->find(l->id());
            if (icl == controller()->end())
                  controller()->add(l);
            else {  //???
                  Ctrl* d = icl->second;
                  for (iCtrlVal i = l->begin(); i != l->end(); ++i)
                        d->insert(i.key(), i.value());
                  d->setCurVal(l->curVal());
                  d->setDefault(l->getDefault());
                  delete l;
                  }
            }
      else if (tag == "height")
            arrangerTrack.h = i < minTrackHeight ? minTrackHeight : i;
      else if (tag == "subtrack") {
            ArrangerTrack* st = new ArrangerTrack;
            for (QDomNode n = node.firstChild(); !n.isNull(); n = n.nextSibling()) {
                  QDomElement e = n.toElement();
                  QString tag   = e.tagName();
                  QString s     = e.text();
                  int i         = s.toInt();
                  if (tag == "height")
                        st->h = i;
                  else if (tag == "ctrl") {
                        st->ctrl = i;
                        }
                  else
                        printf("Track::subtrack: unknown tag <%s>\n", tag.toLatin1().data());
                  }
            subtracks.push_back(st);
            }
      else
            return true;
      return false;
      }

//---------------------------------------------------------
//   addController
//---------------------------------------------------------

void Track::addController(Ctrl* list)
      {
      iCtrl i = controller()->find(list->id());
      if (i != controller()->end()) {
            // printf("%s(%s)::addController(%s): already there 0x%x\n",
            // cname().toLatin1().data(), name().toLatin1().data(), list->name().toLatin1().data(), list->id());
            // abort();
            return;
            }
      controller()->add(list);
      emit clChanged();
      }

//---------------------------------------------------------
//   addMidiController
//---------------------------------------------------------

void Track::addMidiController(MidiInstrument* mi, int ctrl)
      {
      iCtrl cl = _controller.find(ctrl);
      if (cl != _controller.end())
            return;

      MidiController* mc = mi->midiController(ctrl);
      Ctrl* pvl;
      if (mc) {
            pvl = new Ctrl(mc);
            }
      else {
            printf("unknown midi controller %x\n", ctrl);
            pvl = new Ctrl(ctrl, QString("unknown"));
            pvl->setCurVal(CTRL_VAL_UNKNOWN);
            pvl->setType(Ctrl::DISCRETE | Ctrl::INT);
            }
      addController(pvl);
      }

//---------------------------------------------------------
//   removeController
//---------------------------------------------------------

void Track::removeController(int id)
      {
      iCtrl i = controller()->find(id);
      if (i == controller()->end()) {
            printf("Track::removeController id 0x%x not found, listsize %zd\n",
               id, controller()->size());
            return;
            }
      controller()->erase(i);
      emit clChanged();
      }

//---------------------------------------------------------
//   changeCtrlName
//---------------------------------------------------------

void Track::changeCtrlName(Ctrl* c, const QString& s)
      {
      c->setName(s);
      emit clChanged();
      }

//---------------------------------------------------------
//   addControllerVal
//    return true if new controller value added
//---------------------------------------------------------

bool Track::addControllerVal(int id, unsigned time, CVal val)
      {
      iCtrl i = controller()->find(id);
      if (i == controller()->end()) {
            if ((id & 0xf0000) == CTRL_NRPN_OFFSET) {
                  int msb = id & 0xff00;
                  // int lsb = id & 0xff;
                  int nid = CTRL_NRPN_OFFSET + msb + 0xff;
                  i = controller()->find(nid);
                  if (i != controller()->end()) {
                        Ctrl* c = new Ctrl(*(i->second));
                        c->setId(id);
                        addController(c);
// printf("add pitch ctrl %x\n", id);
                        return c->add(time, val);
                        }
                  }
            printf("Track::addControllerVal(): id 0x%x not found, listsize %zd\n",
               id, controller()->size());
            return false;
            }
      return i->second->add(time, val);
      }

//---------------------------------------------------------
//   removeControllerVal
//---------------------------------------------------------

void Track::removeControllerVal(int id, unsigned time)
      {
      iCtrl i = controller()->find(id);
      if (i == controller()->end()) {
            printf("Track::removeControllerVal(): id 0x%x not found, listsize %zd\n",
               id, controller()->size());
            return;
            }
      i->second->del(time);
      }

//---------------------------------------------------------
//   getController
//---------------------------------------------------------

Ctrl* Track::getController(int id) const
      {
      ciCtrl i = controller()->find(id);
      if (i == controller()->end()) {
//            printf("%s(%s)::getController(%d) size %d: not found\n",
//               cname().toLatin1().data(), name().toLatin1().data(), id, controller()->size());
//            const CtrlList* cl = controller();
//            for (ciCtrl i = cl->begin(); i != cl->end(); ++i)
//                  printf("   Ctrl %d\n", i->first);
            return 0;
            }
      return i->second;
      }

//---------------------------------------------------------
//   controllerNames
//---------------------------------------------------------

ControllerNameList* Track::controllerNames() const
      {
      ControllerNameList* l = new ControllerNameList;
      for (ciCtrl i = controller()->begin(); i != controller()->end(); ++i)
            l->push_back(ControllerName(i->second->name(), i->second->id()));
      return l;
      }

//---------------------------------------------------------
//   setRecordFlag
//---------------------------------------------------------

void Track::setRecordFlag(bool f)
      {
      if (_recordFlag != f) {
            _recordFlag = f;
            emit recordChanged(_recordFlag);
            }
      }

//---------------------------------------------------------
//   setMonitor
//---------------------------------------------------------

void Track::setMonitor(bool f)
      {
      if (_monitor != f) {
            _monitor = f;
            emit monitorChanged(_monitor);
            }
      }

//---------------------------------------------------------
//   setSelected
//---------------------------------------------------------

void Track::setSelected(bool f)
      {
      if (f != _selected) {
            _selected = f;
            emit selectionChanged(_selected);
//            muse->selectionChanged();
            }
      }

//---------------------------------------------------------
//   setController
//    called from GUI
//---------------------------------------------------------

#if 0
void Track::setController(Pos pos, int id, int v)
      {
      CVal val;
      val.i = v;
      setController(pos, id, val);
      }

void Track::setController(Pos pos, int id, double v)
      {
      CVal val;
      val.f = v;
      setController(pos, id, val);
      }

void Track::setController(Pos pos, int id, CVal val)
      {
       Ctrl* c = getController(id);
       if (c == 0) {
            printf("no controller 0x%x %s\n", id, name().toLatin1().data());
            return;
            }
      if (isMidiTrack()) {
            int port    = ((MidiTrack*)this)->outPort();
            int channel = ((MidiTrack*)this)->outChannel();
            MidiEvent ev(0, port, channel, ME_CONTROLLER, id, val.i);
            audio->msgPlayMidiEvent(&ev);
            }
      else {
            // non midi controller are current once set
            c->setCurVal(val);
            }
      c->setSchedVal(val);
      if (autoWrite()) {
            unsigned time = _tt == AL::FRAMES ? pos.frame() : pos.tick();
            if (audio->isPlaying())
                  _recEvents.push_back(CtrlRecVal(time, id, val));
            else
                  song->addControllerVal(this, c, id, time, val);
            }
      emit controllerChanged(id);
      }
#endif

//---------------------------------------------------------
//   startAutoRecord
//    slider/knob touched
//---------------------------------------------------------

void Track::startAutoRecord(int n)
      {
      Ctrl* ctrl = getController(n);
      if (ctrl) {
            ctrl->setTouched(true);
            if (audio->isPlaying() && autoWrite())
                  _recEvents.push_back(CtrlRecVal(song->cPos().frame(), n, 1));
            }
      else
            printf("no ctrl 0x%x\n", n);
      }

//---------------------------------------------------------
//   stopAutoRecord
//    slider/knob released
//---------------------------------------------------------

void Track::stopAutoRecord(int n)
      {
      Ctrl* ctrl = getController(n);
      if (ctrl) {
            ctrl->setTouched(false);
            if (audio->isPlaying() && autoWrite())
                  _recEvents.push_back(CtrlRecVal(song->cPos().frame(), n, 2));
            }
      else
            printf("no ctrl 0x%x\n", n);
      }

//---------------------------------------------------------
//   setName
//---------------------------------------------------------

void Track::setName(const QString& s)
      {
      _name = s;
      emit nameChanged(_name);
      }

//---------------------------------------------------------
//   setAutoRead
//---------------------------------------------------------

void Track::setAutoRead(bool val)
      {
      if (_autoRead != val) {
            _autoRead = val;
            emit autoReadChanged(_autoRead);
            }
      }

//---------------------------------------------------------
//   setAutoWrite
//---------------------------------------------------------

void Track::setAutoWrite(bool val)
      {
      if (_autoWrite != val) {
            _autoWrite = val;
            emit autoWriteChanged(_autoWrite);
            }
      }

//---------------------------------------------------------
//   cpos
//---------------------------------------------------------

unsigned Track::cpos() const
      {
      return timeType() == AL::TICKS ? song->cPos().tick() : song->cPos().frame();
      }

//---------------------------------------------------------
//   updateController
//---------------------------------------------------------

void Track::updateController()
      {
      CtrlList* cl = controller();
      for (iCtrl i = cl->begin(); i != cl->end(); ++i) {
            Ctrl* c = i->second;
            if (c->changed()) {
                  c->setChanged(false);
                  emit controllerChanged(c->id());
                  }
            }
      }

//---------------------------------------------------------
//   writeRouting
//---------------------------------------------------------

void Track::writeRouting(Xml& xml) const
      {
      if (type() == AUDIO_INPUT || type() == MIDI_IN) {
            foreach(Route r, _inRoutes) {
                  xml.stag("Route");
                  r.src.write(xml, "src");
                  r.dst.write(xml, "dst");
                  xml.etag("Route");
                  }
            }
      foreach(Route r, _outRoutes) {
            xml.stag("Route");
            r.src.write(xml, "src");
            r.dst.write(xml, "dst");
            xml.etag("Route");
            }
      }

//---------------------------------------------------------
//   hwCtrlState
//---------------------------------------------------------

int Track::hwCtrlState(int ctrl) const
      {
      ciCtrl cl = _controller.find(ctrl);
      if (cl == _controller.end()) {
            if (debugMsg)
                  printf("hwCtrlState: ctrl 0x%x not found\n", ctrl);
            return CTRL_VAL_UNKNOWN;
            }
      Ctrl* vl = cl->second;
      return vl->curVal().i;
      }

//---------------------------------------------------------
//   setHwCtrlState
//---------------------------------------------------------

void Track::setHwCtrlState(int ctrl, int val)
      {
      iCtrl cl = _controller.find(ctrl);
      if (cl == _controller.end()) {
            // try to add new controller
            if (debugMsg)
                  printf("setHwCtrlState(0x%x,0x%x): not found\n", ctrl, val);
            return;
            }
      Ctrl* vl = cl->second;
// printf("setHwCtrlState ctrl %x  val %x\n", ctrl, val);
      vl->setChanged(true);
      return vl->setCurVal(val);
      }

//---------------------------------------------------------
//   getCtrl
//---------------------------------------------------------

int Track::getCtrl(int tick, int ctrl) const
      {
      ciCtrl cl = _controller.find(ctrl);
      if (cl == _controller.end()) {
            if (debugMsg)
                  printf("getCtrl: controller %d(0x%x) not found %zd\n",
                     ctrl, ctrl, _controller.size());
            return CTRL_VAL_UNKNOWN;
            }
      return cl->second->value(tick).i;
      }

//---------------------------------------------------------
//   setSolo
//---------------------------------------------------------

bool Track::setSolo(bool val)
      {
      if (_solo != val) {
            _solo = val;
            emit soloChanged(_solo);
            return true;
            }
      return false;
      }

//---------------------------------------------------------
//   setMute
//    return true if mute changed
//---------------------------------------------------------

bool Track::setMute(bool val)
      {
      if (_mute != val) {
            _mute = val;
            emit muteChanged(_mute);
            return true;
            }
      return false;
      }

//---------------------------------------------------------
//   setOff
//    return true if state changed
//---------------------------------------------------------

bool Track::setOff(bool val)
      {
      if (_off != val) {
            _off = val;
            emit offChanged(_off);
            return true;
            }
      return false;
      }

//---------------------------------------------------------
//   setChannels
//---------------------------------------------------------

void Track::setChannels(int n)
      {
      _channels = n;
      for (int i = 0; i < _channels; ++i) {
            _meter[i] = 0.0f;
            _peak[i]  = 0.0f;
            }
      }

//---------------------------------------------------------
//   resetMeter
//---------------------------------------------------------

void Track::resetMeter()
      {
      for (int i = 0; i < _channels; ++i)
            _meter[i] = 0.0f;
      }

//---------------------------------------------------------
//   resetPeaks
//---------------------------------------------------------

void Track::resetPeaks()
      {
      for (int i = 0; i < _channels; ++i)
            _peak[i] = 0;
      }

//---------------------------------------------------------
//   resetAllMeter
//---------------------------------------------------------

void Track::resetAllMeter()
      {
      TrackList* tl = song->tracks();
      for (iTrack i = tl->begin(); i != tl->end(); ++i)
            (*i)->resetMeter();
      }

//---------------------------------------------------------
//   activate1
//    register JACK and ALSA ports
//---------------------------------------------------------

void Track::activate1()
      {
      if (isMidiTrack()) {
            if (!jackPort(0).isZero())
                  printf("Track::activate1() midi: jack port already active!\n");
            if (type() == MIDI_OUT) {
                  _jackPort[0] = audioDriver->registerOutPort(_name, true);
                  }
            else if (type() == MIDI_IN) {
                  _jackPort[0] = audioDriver->registerInPort(_name, true);
                  }
            return;
            }

      for (int i = 0; i < channels(); ++i) {
            if (!jackPort(i).isZero())
                  printf("Track<%s>::activate1(): channel %d already active!\n",
                    name().toLatin1().data(), i);
            else {
                  QString s(QString("%1-%2").arg(_name).arg(i));
                  if (type() == AUDIO_OUTPUT)
                        _jackPort[i] = audioDriver->registerOutPort(s, false);
                  else if (type() == AUDIO_INPUT)
                        _jackPort[i] = audioDriver->registerInPort(s, false);
                  }
            }
      }

//---------------------------------------------------------
//   activate2
//    connect all JACK/ALSA in/out routes
//    connect to JACK only works if JACK is running
//---------------------------------------------------------

void Track::activate2()
      {
      if (audioState != AUDIO_RUNNING) {
            printf("Track::activate2(): no audio running !\n");
            abort();
            }
      foreach(Route r, _outRoutes) {
            if (r.dst.type == RouteNode::JACKMIDIPORT) {
                  audioDriver->connect(_jackPort[0], r.dst.port);
                  r.disconnected = false;
                  }
            else if (r.dst.type == RouteNode::AUDIOPORT) {
                  audioDriver->connect(_jackPort[r.src.channel], r.dst.port);
                  r.disconnected = false;
                  }
            }
      foreach(Route r, _inRoutes) {
            if (r.src.type == RouteNode::JACKMIDIPORT) {
                  audioDriver->connect(r.src.port, _jackPort[0]);
                  r.disconnected = false;
                  }
            else if (r.src.type == RouteNode::AUDIOPORT) {
                  audioDriver->connect(r.src.port, _jackPort[r.dst.channel]);
                  r.disconnected = false;
                  }
            }
      }

//---------------------------------------------------------
//   deactivate
//    disconnect and unregister JACK and ALSA ports
//---------------------------------------------------------

void Track::deactivate()
      {
// printf("deactivate<%s>\n", name().toLatin1().data());
      foreach(Route r, _outRoutes) {
            if (r.dst.type == RouteNode::JACKMIDIPORT) {
                  r.disconnected = true;
                  audioDriver->disconnect(_jackPort[0], r.dst.port);
                  }
            else if (r.dst.type == RouteNode::AUDIOPORT) {
                  audioDriver->disconnect(_jackPort[r.src.channel], r.dst.port);
                  r.disconnected = true;
                  }
            }
      foreach(Route r, _inRoutes) {
            if (r.src.type == RouteNode::JACKMIDIPORT) {
                  r.disconnected = true;
                  audioDriver->disconnect(r.src.port, _jackPort[0]);
                  }
            else if (r.src.type == RouteNode::AUDIOPORT) {
                  r.disconnected = true;
                  audioDriver->disconnect(r.src.port, _jackPort[r.dst.channel]);
                  }
            }
      for (int i = 0; i < channels(); ++i) {
            if (!_jackPort[i].isZero()) {
                  audioDriver->unregisterPort(_jackPort[i]);
                  _jackPort[i].setZero();
                  }
            }
      }

//---------------------------------------------------------
//   setSendSync
//---------------------------------------------------------

void Track::setSendSync(bool val)
      {
      _sendSync = val;
      emit sendSyncChanged(val);
      }

//---------------------------------------------------------
//   splitPart
//    split part "part" at "tick" position
//    create two new parts p1 and p2
//---------------------------------------------------------

void Track::splitPart(Part* part, int tickpos, Part*& p1, Part*& p2)
      {
      int l1 = 0;       // len of first new part (ticks or samples)
      int l2 = 0;       // len of second new part

      int samplepos = AL::tempomap.tick2frame(tickpos);

      switch (type()) {
            case WAVE:
                  l1 = samplepos - part->frame();
                  l2 = part->lenFrame() - l1;
                  break;
            case MIDI:
                  l1 = tickpos - part->tick();
                  l2 = part->lenTick() - l1;
                  break;
            default:
                  return;
            }

      if (l1 <= 0 || l2 <= 0)
            return;

      p1 = newPart(part);     // new left part
      p2 = newPart(part);     // new right part

      switch (type()) {
            case WAVE:
                  p1->setLenFrame(l1);
                  p2->setFrame(samplepos);
                  p2->setLenFrame(l2);
                  break;
            case MIDI:
                  p1->setLenTick(l1);
                  p2->setTick(tickpos);
                  p2->setLenTick(l2);
                  break;
            default:
                  break;
            }

      EventList* se  = part->events();
      EventList* de1 = p1->events();
      EventList* de2 = p2->events();

      if (type() == WAVE) {
            int ps   = part->frame();
            int d1p1 = p1->frame();
            int d2p1 = p1->endFrame();
            int d1p2 = p2->frame();
            int d2p2 = p2->endFrame();
            for (iEvent ie = se->begin(); ie != se->end(); ++ie) {
                  Event event = ie->second;
                  int s1 = event.frame() + ps;
                  int s2 = event.endFrame() + ps;

                  if ((s2 > d1p1) && (s1 < d2p1)) {
                        Event si = event.mid(d1p1 - ps, d2p1 - ps);
                        de1->add(si);
                        }
                  if ((s2 > d1p2) && (s1 < d2p2)) {
                        Event si = event.mid(d1p2 - ps, d2p2 - ps);
                        si.setFrame(si.frame() - l1);       //??
                        si.setFrame(0);                     //??
                        de2->add(si);
                        }
                  }
            }
      else {
            for (iEvent ie = se->begin(); ie != se->end(); ++ie) {
                  Event event = ie->second.clone();
                  int t = event.tick();
                  if (t >= l1) {
                        event.move(-l1);
                        de2->add(event);
                        }
                  else
                        de1->add(event);
                  }
            }
      }

//---------------------------------------------------------
//   addInRoute
//---------------------------------------------------------

void Track::addInRoute(const Route& r)
      {
      if (_inRoutes.indexOf(r) != -1) {
            printf("Track::addInRoute: route already there\n");
            return;
            }
      _inRoutes.push_back(r);
      }

//---------------------------------------------------------
//   addOutRoute
//---------------------------------------------------------

void Track::addOutRoute(const Route& r)
      {
      if (_outRoutes.indexOf(r) != -1) {
            printf("Track::addOutRoute: route already there\n");
            return;
            }
      _outRoutes.push_back(r);
      }

//---------------------------------------------------------
//   inRouteExists
//---------------------------------------------------------

bool Track::inRouteExists(const Route& r) const
      {
      return _inRoutes.indexOf(r) != -1;
      }

//---------------------------------------------------------
//   outRouteExists
//---------------------------------------------------------

bool Track::outRouteExists(const Route& r) const
      {
      return _outRoutes.indexOf(r) != -1;
      }