//=========================================================
//  MusE
//  Linux Music Editor
//  $Id: wavetrack.cpp,v 1.15 2004/07/18 10:26:19 wschweer Exp $
//
//  (C) Copyright 2003 Werner Schweer (ws@seh.de)
//=========================================================

#include "track.h"
#include "event.h"
#include "audio.h"
#include "wave.h"
#include "xml.h"
#include "song.h"
#include "globals.h"

bool WaveTrack::firstWaveTrack = true;

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

void WaveTrack::fetchData(unsigned pos, unsigned samples, float** bp)
      {
      for (int i = 0; i < channels(); ++i)
            memset(bp[i], 0, samples * sizeof(float));
      PartList* pl = parts();

      unsigned n = samples;
      for (iPart ip = pl->begin(); ip != pl->end(); ++ip) {
            WavePart* part = (WavePart*)(ip->second);
            if (part->mute())
                  continue;
            unsigned p_spos = part->frame();
            unsigned p_epos = p_spos + part->lenFrame();

            if (pos + n < p_spos)
                  break;
            if (pos >= p_epos)
                  continue;

            EventList* events = part->events();
            for (iEvent ie = events->begin(); ie != events->end(); ++ie) {
                  Event& event = ie->second;
                  unsigned e_spos  = event.frame() + p_spos;
                  unsigned nn      = event.lenFrame();
                  unsigned e_epos  = e_spos + nn;
                  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[channels()];
                  for (int i = 0; i < channels(); ++i)
                        bpp[i] = bp[i] + dstOffset;

                  event.read(srcOffset, bpp, channels(), nn);
                  }
            }
      _prefetchFifo.add();
      }

//---------------------------------------------------------
//   write
//---------------------------------------------------------

void WaveTrack::write(int level, Xml& xml) const
      {
      xml.tag(level++, "wavetrack");
      AudioTrack::writeProperties(level, xml);
      const PartList* pl = cparts();
      for (ciPart p = pl->begin(); p != pl->end(); ++p)
            p->second->write(level, xml);
      xml.etag(level, "wavetrack");
      }

//---------------------------------------------------------
//   read
//---------------------------------------------------------

void WaveTrack::read(Xml& xml)
      {
      for (;;) {
            Xml::Token token = xml.parse();
            const QString& tag = xml.s1();
            switch (token) {
                  case Xml::Error:
                  case Xml::End:
                        return;
                  case Xml::TagStart:
                        if (tag == "part") {
                              Part* p = newPart();
                              p->read(xml);
                              parts()->add(p);
                              }
                        else if (AudioTrack::readProperties(xml, tag))
                              xml.unknown("WaveTrack");
                        break;
                  case Xml::Attribut:
                        break;
                  case Xml::TagEnd:
                        if (tag == "wavetrack") {
                              return;
                              }
                  default:
                        break;
                  }
            }
      }

//---------------------------------------------------------
//   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(unsigned framePos, int channels, unsigned nframe, float** bp)
      {
      if ((song->bounceTrack != this) && !noInRoute()) {
            RouteList* irl = inRoutes();
            iRoute i = irl->begin();
            i->track->copyData(framePos, channels, nframe, bp);
            ++i;
            for (; i != irl->end(); ++i)
                  i->track->addData(framePos, channels, nframe, bp);
            if (recordFlag()) {
                  if (audio->isRecording() && recFile()) {
                        if (audio->freewheel()) {
                              }
                        else {
                              if (fifo.put(channels, nframe, bp, audio->pos().frame()))
                                    printf("WaveTrack::getData(%d, %d, %d): fifo overrun\n",
                                       framePos, channels, nframe);
                              }
                        }
                  return true;
                  }
            }
      if (!audio->isPlaying())
            return false;
      if (outRoutes()->size() > 1) {
            if (bufferPos != framePos) {
                  bufferPos = framePos;
                  if (audio->freewheel()) {
                        // when freewheeling, read data direct from file:
                        fetchData(bufferPos, nframe, outBuffers);
                        }
                  else {
                        unsigned pos;
                        if (_prefetchFifo.get(channels, nframe, outBuffers, &pos)) {
                              printf("WaveTrack::getData(%s) fifo underrun\n",
                                 name().latin1());
                              return false;
                              }
                        if (pos != framePos) {
                              printf("fifo get error expected %d, got %d\n",
                                 framePos, pos);
                              if (debugMsg)
                                    printf("fifo get error expected %d, got %d\n",
                                       framePos, pos);
                              while (pos < framePos) {
                                    if (_prefetchFifo.get(channels, nframe, bp, &pos)) {
                                          printf("WaveTrack::getData(%s) fifo underrun\n",
                                             name().latin1());
                                          return false;
                                          }
                                    }
                              }
                        }
                  }
            for (int i = 0; i < channels; ++i)
                  memcpy(bp[i], outBuffers[i], nframe * sizeof(float));
            }
      else {
            if (audio->freewheel()) {
                  // when freewheeling, read data direct from file:
                  fetchData(framePos, nframe, bp);
                  }
            else {
                  unsigned pos;
                  if (_prefetchFifo.get(channels, nframe, bp, &pos)) {
                        printf("WaveTrack::getData(%s) fifo underrun\n",
                           name().latin1());
                        return false;
                        }
                  if (pos != framePos) {
                        if (debugMsg)
                              printf("fifo get error expected %d, got %d\n",
                                 framePos, pos);
                        while (pos < framePos) {
                              if (_prefetchFifo.get(channels, nframe, bp, &pos)) {
                                    printf("WaveTrack::getData(%s) fifo underrun\n",
                                       name().latin1());
                                    return false;
                                    }
                              }
                        }
                  }
            }
      return true;
      }

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

void WaveTrack::setChannels(int n)
      {
      AudioTrack::setChannels(n);
      SndFile* sf = recFile();
      if (sf) {
            if (sf->samples() == 0) {
                  sf->remove();
                  sf->setFormat(sf->format(), _channels,
                     sf->samplerate());
                  sf->openWrite();
                  }
            }
      }
