//=========================================================
//  MusE
//  Linux Music Editor
//  $Id: track.cpp,v 1.2 2003/10/29 22:14:12 spamatica Exp $
//
//  (C) Copyright 2000 Werner Schweer (ws@seh.de)
//=========================================================

#include "track.h"
#include "event.h"
#include "driver/mididev.h"
#include "driver/audiodev.h"
#include "midiport.h"
#include "song.h"
#include "midithread.h"
#include "globals.h"
#include "audio.h"
#include "wave.h"

int Track::snGen=100;
bool WaveTrack::firstWaveTrack = true;

//---------------------------------------------------------
//   y
//---------------------------------------------------------

int Track::y() const
      {
      TrackList* tl = song->tracks();
      int yy = 0;
      for (ciTrack it = tl->begin(); it != tl->end(); ++it) {
            if (this == *it)
                  return yy;
            yy += (*it)->height();
            }
      printf("Track::y(): track not in tracklist\n");
      return -1;
      }

//---------------------------------------------------------
//   Track::init
//---------------------------------------------------------

void Track::init()
      {
      _inPortMask  = 0xffff;
      _outPort     = 0;
      _outChannel  = 0;
      _inChannelMask   = 0xffff;      // "ALL"
      _locked      = false;
      _selected    = false;
      _sn          = newSn();
      _height      = 20;
      }

Track::Track(Track::TrackType t)
      {
      init();
      _type = t;
      }

Track::Track(const QString& name, TrackType t)
   : _name(name)
      {
      init();
      _type = t;
      }

Track::Track(const Track& t)
      {
      _comment     = t.comment();
      _outPort     = t.outPort();
      _outChannel  = t.outChannel();
      _inPortMask      = t.inPortMask();
      _inChannelMask   = t.inChannelMask();
      _name        = t.name();
      _type        = t.type();
      _locked      = t.locked();
      _selected    = t.selected();
      _sn          = newSn();
      _y           = t._y;
      _height      = t._height;

      const PartList* pl = t.cparts();
      for (ciPart ip = pl->begin(); ip != pl->end(); ++ip) {
            Part* newPart = ip->second->clone();
            newPart->setTrack(this);
            _parts.add(newPart);
            }
      }

Track::~Track()
      {
      }

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

void Track::setSn(int n)
      {
//      printf("set sn %d\n", n);
      _sn = n;
      }

//---------------------------------------------------------
//   MidiTrack
//---------------------------------------------------------

MidiTrack::MidiTrack()
   : Track(MIDI), key(0), scale(0)
      {
      init();
      _events = new EventList;
      }
MidiTrack::MidiTrack(const QString& name)
   : Track(name, Track::MIDI), key(0), scale(0)
      {
      init();
      _events = new EventList;
      }

MidiTrack::MidiTrack(const MidiTrack& mt)
   : Track(mt), MidiNode(mt)
      {
      _events       = new EventList;
      key           = mt.key;
      scale         = mt.scale;
      keyL          = mt.keyL;
      scaleL        = mt.scaleL;
      noteQuant     = mt.noteQuant;
      restQuant     = mt.restQuant;
      mode          = mt.mode;
      splitpoint    = mt.splitpoint;
      transposition = mt.transposition;
      velocity      = mt.velocity;
      delay         = mt.delay;
      len           = mt.len;
      compression   = mt.compression;
      _midiThruFlag = mt._midiThruFlag;
      }

MidiTrack::~MidiTrack()
      {
      delete _events;
      }

void MidiTrack::init()
      {
      noteQuant     = 96*4;     // 4'tel Noten Darstellungs-Quantisierung
      restQuant     = 96*4;     // Pausen Darstellungs-Quantisierung
      transposition = 0;
      velocity      = 0;
      delay         = 0;
      len           = 100;          // percent
      compression   = 100;          // percent
      mode          = Single;
      splitpoint    = 60;
      _midiThruFlag = true;
      keyL.set(4);
      scaleL.set(0);
      }

MidiTrack* MidiTrack::clone() const
      {
      return new MidiTrack(*this);
      }

void MidiTrack::copy(const Track* /*t*/)
      {
      printf("COPY MIDITRACK not implemented\n");  // TODO
      }

//---------------------------------------------------------
//   WaveTrack
//---------------------------------------------------------

void WaveTrack::init()
      {
      _recFile = 0;
      setPorts(1);
      audio->msgAddRoute(this, &audioOutput);
      }

WaveTrack::WaveTrack(const QString& str)
   : Track(str, Track::WAVE)
      {
			firstWaveTrack=false;
	    init();
      }

WaveTrack::WaveTrack()
   : Track(Track::WAVE)
      {
      init();
      }

WaveTrack::WaveTrack(const WaveTrack& wt)
   : Track(wt), AudioNode(wt)
      {
      audio->msgAddRoute(this, &audioOutput);
      }

WaveTrack* WaveTrack::clone() const
      {
      return new WaveTrack(*this);
      }

void WaveTrack::copy(const Track* /*t*/)
      {
      printf("COPY WAVE TRACK not impl.\n");   // TODO
      }

WaveTrack::~WaveTrack()
      {
      }

void WaveTrack::setSn(int n)
      {
      Track::setSn(n);
      }

//---------------------------------------------------------
//   add
//---------------------------------------------------------

Track* TrackList::add(Track* track)
      {
      if (track->name().isEmpty()) {
            QString s("Track ");
            QString k;
            k.setNum(size()+1);
            track->setName(s+k);
            }
      push_back(track);
      return track;
      }

//---------------------------------------------------------
//   index
//---------------------------------------------------------

unsigned TrackList::index(Track* track)
      {
      unsigned index = 0;
      for (iTrack i = begin(); i != end(); ++i, ++index)
            if (*i == track)
                  return index;
      printf("TrackList::index(): not found!\n");
      return 0;
      }

//---------------------------------------------------------
//   sfind
//    search track with specified serial number
//---------------------------------------------------------

Track* TrackList::sfind(int sno)
      {
      for (iTrack i = begin(); i != end(); ++i) {
            if ((*i)->sn() == sno) {
                  return *i;
                  }
            }
      int index = 0;
      for (iTrack i = begin(); i != end(); ++i) {
            printf("search %d  in %d, index %d %p\n", sno, (*i)->sn(), index, *i);
            ++index;
            }
printf("sfind: track sno %d not found\n", sno);
      return 0;
      }

//---------------------------------------------------------
//   findIdx
//    search track with specified index
//---------------------------------------------------------

Track* TrackList::findIdx(int idx)
      {
      int index = 0;
      for (iTrack i = begin(); i != end(); ++i, ++index) {
            if (index == idx)
                  return *i;
            }
printf("findIdx: track %d not found\n", idx);
      return 0;
      }

//---------------------------------------------------------
//   find
//    search track with specified serial number
//---------------------------------------------------------

iTrack TrackList::find(int idx)
      {
      int index = 0;
      for (iTrack i = begin(); i != end(); ++i, ++index) {
            if (index == idx)
                  return i;
            }
      return end();
      }

//---------------------------------------------------------
//   remove
//---------------------------------------------------------

void TrackList::erase(Track* t)
      {
      for (iTrack i = begin(); i != end(); ++i) {
            if (*i  == t) {
                  erase(i);
                  return;
                  }
            }
      }

//---------------------------------------------------------
//   deselect
//---------------------------------------------------------

void TrackList::deselect()
      {
      for (iTrack i = begin(); i != end(); ++i)
            (*i)->setSelected(false);
      }

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

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

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

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

//---------------------------------------------------------
//   device
//---------------------------------------------------------

MidiDevice* MidiTrack::outDevice() const
      {
      return midiPorts[outPort()].device();
      }

MidiDevice* MidiTrack::inDevice() const
      {
      return 0;
      }

//---------------------------------------------------------
//   setInChannel
//---------------------------------------------------------

void Track::setInChannelMask(int c)
      {
      _inChannelMask = c;
      }

//---------------------------------------------------------
//   setInPort
//---------------------------------------------------------

void Track::setInPortMask(int i)
      {
      _inPortMask = i;
      }

//---------------------------------------------------------
//   fetchData
//    called from prefetch thread
//---------------------------------------------------------

void WaveTrack::fetchData(int pos, unsigned long samples)
      {
      float* bp[ports()];
      if (prefetchFifo.getWriteBuffer(ports(), samples, bp, pos))
            return;
      for (int i = 0; i < ports(); ++i)
            memset(bp[i], 0, samples * sizeof(float));
      PartList* pl = parts();

      int n = samples;
      for (iPart ip = pl->begin(); ip != pl->end(); ++ip) {
            WavePart*  part   = (WavePart*)(ip->second);
            if (part->mute())
                  continue;
            EventList* events = part->events();
            int p_spos        = part->posSample();
            int p_epos        = p_spos + part->lenSample();

            if (pos + n < p_spos)
                  break;
            if (pos >= p_epos)
                  continue;
            for (iEvent ie = events->begin(); ie != events->end(); ++ie) {
                  WaveEvent* event = (WaveEvent*)ie->second;
                  int e_spos = event->posSample() + p_spos;
                  int e_epos = p_spos + event->lenSample();
                  int nn     = e_epos - e_spos;
                  if (pos + n < e_spos)
                        break;
                  if (pos >= e_epos)
                        continue;
                  int offset  = e_spos - pos;

                  unsigned srcOffset, dstOffset;
                  if (offset > 0) {
                        nn = n - offset;
                        srcOffset = 0;
                        dstOffset = offset;
                        }
                  else {
                        srcOffset = -offset;
                        dstOffset = 0;
                        nn -= offset;
                        if (nn > n)
                              nn = n;
                        }
                  float* bpp[ports()];
                  for (int i = 0; i < ports(); ++i)
                        bpp[i] = bp[i] + dstOffset;

                  event->clip()->read(srcOffset, bpp, ports(), nn);
                  }
            }
      prefetchFifo.add();
      }

//---------------------------------------------------------
//   newPart
//---------------------------------------------------------

Part* MidiTrack::newPart(Part*p, bool clone)
      {
      MidiPart* part = clone ? new MidiPart(this, p->events()) : new MidiPart(this);
      if (p) {
            part->setName(p->name());
            part->setColorIndex(p->colorIndex());

            *(PosLen*)part = *(PosLen*)p;
            part->setMute(p->mute());
            }
      return part;
      }

//---------------------------------------------------------
//   newPart
//---------------------------------------------------------

Part* WaveTrack::newPart(Part*p, bool clone)
      {
      WavePart* part = clone ? new WavePart(this, p->events()) : new WavePart(this);
      if (p) {
            part->setName(p->name());
            part->setColorIndex(p->colorIndex());

            *(PosLen*)part = *(PosLen*)p;
            part->setMute(p->mute());
            }
      return part;
      }

//---------------------------------------------------------
//   getData
//---------------------------------------------------------

bool WaveTrack::getData(int channels, unsigned long nframe, float** bp)
      {
      if (!_inRoute.empty()) {
            iAudioNode i = _inRoute.begin();
            (*i)->copyData(channels, nframe, bp);
            ++i;
            for (; i != _inRoute.end(); ++i)
                  (*i)->addData(channels, nframe, bp);
            if (recordFlag()) {
                  if (audio->isPlaying() && song->record() && recFile()) {
                        fifo.put(channels, nframe, bp, audio->pos());
                        }
                  return true;
                  }
            }
      if (!audio->isPlaying())
            return false;
      long unsigned pos;
      if (prefetchFifo.get(channels, nframe, bp, pos))
            return false;
      return true;
      }

//---------------------------------------------------------
//   clearDelete
//    delete tracks and clear tracklist
//---------------------------------------------------------

void TrackList::clearDelete()
      {
      for (iTrack i = begin(); i != end(); ++i)
            delete *i;
      std::list<Track*>::clear();
      }

