//=========================================================
//  MusE
//  Linux Music Editor
//  $Id: audiotrack.cpp,v 1.14 2004/07/14 15:27:26 wschweer Exp $
//
//  (C) Copyright 2004 Werner Schweer (ws@seh.de)
//=========================================================

#include <values.h>

#include "track.h"
#include "event.h"
#include "song.h"
#include "audio.h"
#include "wave.h"
#include "xml.h"
#include "plugin.h"
#include "audiodev.h"

//---------------------------------------------------------
//   AudioTrack
//---------------------------------------------------------

AudioTrack::AudioTrack(TrackType t)
   : Track(t)
      {
      _prefader = false;
      _efxPipe  = new Pipeline();
      _recFile  = 0;
      _channels = 0;
      _automationType = AUTO_READ;
      setChannels(1);
      addController(new CtrlList(AC_VOLUME));
      addController(new CtrlList(AC_PAN));
      outBuffers = new float*[2];
      for (int i = 0; i < MAX_CHANNELS; ++i)
            outBuffers[i] = new float[segmentSize];
      bufferPos = MAXINT;
      }

AudioTrack::AudioTrack(const AudioTrack& t)
  : Track(t)
      {
      _controller     = t._controller;
      _prefader       = t._prefader;
      _auxSend        = t._auxSend;
      _efxPipe        = new Pipeline(*(t._efxPipe));
      _automationType = t._automationType;
      _inRoutes       = t._inRoutes;
      _outRoutes      = t._outRoutes;
      outBuffers = new float*[MAX_CHANNELS];
      for (int i = 0; i < MAX_CHANNELS; ++i)
            outBuffers[i] = new float[segmentSize];
      bufferPos = MAXINT;
      _recFile  = t._recFile;
      }

AudioTrack::~AudioTrack()
      {
      delete _efxPipe;
      for (int i = 0; i < MAX_CHANNELS; ++i)
            delete[] outBuffers[i];
      delete[] outBuffers;
      }

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

Part* AudioTrack::newPart(Part*, bool /*clone*/)
      {
      return 0;
      }

//---------------------------------------------------------
//   addPlugin
//---------------------------------------------------------

void AudioTrack::addPlugin(PluginI* plugin, int idx)
      {
      if (plugin == 0) {
            PluginI* oldPlugin = (*_efxPipe)[idx];
            if (oldPlugin) {
                  int controller = oldPlugin->parameters();
                  for (int i = 0; i < controller; ++i) {
                        int id = (idx + 1) * 0x1000 + i;
                        removeController(id);
                        }
                  }
            }
      efxPipe()->insert(plugin, idx);
      if (plugin) {
            int controller = plugin->parameters();
            for (int i = 0; i < controller; ++i) {
                  int id = (idx + 1) * 0x1000 + i;
                  const char* name = plugin->paramName(i);
                  float min, max;
                  plugin->range(i, &min, &max);
                  CtrlValueType t = plugin->valueType();
                  CtrlList* cl = new CtrlList(id);
                  cl->setRange(min, max);
                  cl->setName(QString(name));
                  cl->setValueType(t);
                  addController(cl);
                  }
            }
      }

//---------------------------------------------------------
//   addAuxSend
//---------------------------------------------------------

void AudioTrack::addAuxSend(int n)
      {
      int nn = _auxSend.size();
      for (int i = nn; i < n; ++i) {
            _auxSend.push_back(0.0);
            _auxSend[i] = 0.0;  //??
            }
      }

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

void AudioTrack::addController(CtrlList* list)
      {
      _controller.add(list);
      }

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

void AudioTrack::removeController(int id)
      {
      iCtrlList i = _controller.find(id);
      if (i == _controller.end()) {
            printf("AudioTrack::removeController id %d not found\n", id);
            return;
            }
      _controller.erase(i);
      }

//---------------------------------------------------------
//   volume
//---------------------------------------------------------

double AudioTrack::volume() const
      {
      ciCtrlList cl = _controller.find(AC_VOLUME);
      if (cl == _controller.end())
            return 0.0;
      if (automation && (automationType() == AUTO_READ
         || automationType() == AUTO_TOUCH))
            return cl->second->value(song->cPos().frame());
      else
            return cl->second->curVal();
      }

//---------------------------------------------------------
//   setVolume
//---------------------------------------------------------

void AudioTrack::setVolume(double val)
      {
      iCtrlList cl = _controller.find(AC_VOLUME);
      if (cl == _controller.end()) {
            printf("no volume controller %s %d\n",
               name().latin1(), _controller.size());
            return;
            }
      cl->second->setCurVal(val);
      }

//---------------------------------------------------------
//   pan
//---------------------------------------------------------

double AudioTrack::pan() const
      {
      ciCtrlList cl = _controller.find(AC_PAN);
      if (cl == _controller.end())
            return 0.0;
      return cl->second->value(song->cPos().frame());
      }

//---------------------------------------------------------
//   setPan
//---------------------------------------------------------

void AudioTrack::setPan(double val)
      {
      iCtrlList cl = _controller.find(AC_PAN);
      if (cl == _controller.end()) {
            printf("no pan controller\n");
            return;
            }
      cl->second->setCurVal(val);
      }

void AudioTrack::recordAutomation(int n, float v)
      {
      if (audio->isPlaying() && automation && automationType() == AUTO_TOUCH || automationType() == AUTO_WRITE)
            _recEvents.push_back(CtrlRecVal(song->cPos().frame(), n, v));
      }

void AudioTrack::startAutoRecord(int n)
      {
      if (audio->isPlaying() && automation && automationType() == AUTO_TOUCH)
            _recEvents.push_back(CtrlRecVal(song->cPos().frame(), n, 1));
      }

void AudioTrack::stopAutoRecord(int n)
      {
      if (audio->isPlaying() && automation && automationType() == AUTO_TOUCH)
            _recEvents.push_back(CtrlRecVal(song->cPos().frame(), n, 2));
      }

//---------------------------------------------------------
//   AudioTrack::writeProperties
//---------------------------------------------------------

void AudioTrack::writeProperties(int level, Xml& xml) const
      {
      Track::writeProperties(level, xml);
      xml.intTag(level, "prefader", prefader());
      xml.intTag(level, "automation", int(automationType()));
      if (hasAuxSend()) {
            int naux = song->auxs()->size();
            for (int idx = 0; idx < naux; ++idx) {
                  QString s("<auxSend idx=%1>%2</auxSend>\n");
                  xml.nput(level, s.arg(idx).arg(_auxSend[idx]));
                  }
            }
      for (ciPluginI ip = _efxPipe->begin(); ip != _efxPipe->end(); ++ip) {
            if (*ip)
                  (*ip)->writeConfiguration(level, xml);
            }
      for (ciCtrlList icl = _controller.begin(); icl != _controller.end(); ++icl) {
            const CtrlList* cl = icl->second;
            QString s("controller id=\"%1\" cur=\"%2\"");
            xml.tag(level++, s.arg(cl->id()).arg(cl->curVal()));
            int i = 0;
            for (ciCtrl ic = cl->begin(); ic != cl->end(); ++ic) {
                  QString s("%1 %2, ");
                  xml.nput(level, s.arg(ic->second.frame).arg(ic->second.val));
                  ++i;
                  if (i >= 4) {
                        xml.put(level, "");
                        i = 0;
                        }
                  }
            if (i)
                  xml.put(level, "");
            xml.etag(level--, "controller");
            }
      }

//---------------------------------------------------------
//   readAuxSend
//---------------------------------------------------------

void AudioTrack::readAuxSend(Xml& xml)
      {
      unsigned idx = 0;
      double val;
      for (;;) {
            Xml::Token token = xml.parse();
            const QString& tag = xml.s1();
            switch (token) {
                  case Xml::Error:
                  case Xml::End:
                        return;
                  case Xml::Attribut:
                        if (tag == "idx")
                              idx = xml.s2().toInt();
                        break;
                  case Xml::Text:
                        val = tag.toDouble();
                        break;
                  case Xml::TagEnd:
                        if (xml.s1() == "auxSend") {
                              if (_auxSend.size() < idx+1)
                                    _auxSend.push_back(val);
                              else
                                    _auxSend[idx] = val;
                              return;
                              }
                  default:
                        break;
                  }
            }
      }

//---------------------------------------------------------
//   AudioTrack::readProperties
//---------------------------------------------------------

bool AudioTrack::readProperties(Xml& xml, const QString& tag)
      {
      if (tag == "plugin") {
            PluginI* pi = new PluginI;
            if (pi->readConfiguration(xml, false)) {
                  delete pi;
                  }
            else {
                  // insert plugin into first free slot
                  // of plugin rack
                  int i = 0;
                  for (i = 0; i < 4; ++i) {
                        if ((*_efxPipe)[i] == 0) {
                              (*_efxPipe)[i] = pi;
                              break;
                              }
                        }
                  if (i == 4) {
                        printf("internal error: too many plugins\n");
                        }
                  }
            }
      else if (tag == "auxSend")
            readAuxSend(xml);
      else if (tag == "prefader")
            _prefader = xml.parseInt();
      else if (tag == "automation")
            setAutomationType(AutomationType(xml.parseInt()));
      else if (tag == "recfile")
            readRecfile(xml);
      else if (tag == "controller") {
            CtrlList* l = new CtrlList();
            l->read(xml);
            iCtrlList icl = _controller.find(l->id());
            if (icl == _controller.end())
                  _controller.add(l);
            else {
                  CtrlList* d = icl->second;
                  for (iCtrl i = l->begin(); i != l->end(); ++i)
                        d->insert(std::pair<const int, CtrlVal> (i->first, i->second));
                  d->setCurVal(l->curVal());
                  d->setDefault(l->getDefault());
                  delete l;
                  }
            }
      else
            return Track::readProperties(xml, tag);
      return false;
      }

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

void AudioTrack::writeRouting(int level, Xml& xml) const
      {
      if (type() == Track::AUDIO_INPUT) {
            const RouteList* rl = &_inRoutes;
            for (ciRoute r = rl->begin(); r != rl->end(); ++r) {
                  Route dst(name(), true, r->channel);
                  xml.tag(level++, "Route");
                  xml.strTag(level, "srcNode", r->name());
                  xml.strTag(level, "dstNode", dst.name());
                  xml.etag(level--, "Route");
                  }
            }
      const RouteList* rl = &_outRoutes;
      for (ciRoute r = rl->begin(); r != rl->end(); ++r) {
            QString src(name());
            if (type() == Track::AUDIO_OUTPUT) {
                  Route s(src, false, r->channel);
                  src = s.name();
                  }
            xml.tag(level++, "Route");
            xml.strTag(level, "srcNode", src);
            xml.strTag(level, "dstNode", r->name());
            xml.etag(level--, "Route");
            }
      }

//---------------------------------------------------------
//   AudioInput
//---------------------------------------------------------

AudioInput::AudioInput()
   : AudioTrack(AUDIO_INPUT)
      {
      // set Default for Input Ports:
      _mute = true;
      setVolume(0.0);
      for (int i = 0; i < MAX_CHANNELS; ++i)
            jackPorts[i] = 0;
      _channels = 0;
      setChannels(2);
      }

AudioInput::AudioInput(const AudioInput& t)
  : AudioTrack(t)
      {
      for (int i = 0; i < MAX_CHANNELS; ++i)
            jackPorts[i] = t.jackPorts[i];
      }

//---------------------------------------------------------
//   ~AudioInput
//---------------------------------------------------------

AudioInput::~AudioInput()
      {
      for (int i = 0; i < _channels; ++i)
            audioDevice->unregisterPort(jackPorts[i]);
      }

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

void AudioInput::write(int level, Xml& xml) const
      {
      xml.tag(level++, "AudioInput");
      AudioTrack::writeProperties(level, xml);
      xml.etag(level, "AudioInput");
      }

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

void AudioInput::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 (AudioTrack::readProperties(xml, tag))
                              xml.unknown("AudioInput");
                        break;
                  case Xml::Attribut:
                        break;
                  case Xml::TagEnd:
                        if (tag == "AudioInput") {
                              setName(name());  // allocate jack ports
                              return;
                              }
                  default:
                        break;
                  }
            }
      }

//---------------------------------------------------------
//   AudioOutput
//---------------------------------------------------------

AudioOutput::AudioOutput()
   : AudioTrack(AUDIO_OUTPUT)
      {
      for (int i = 0; i < MAX_CHANNELS; ++i)
            jackPorts[i] = 0;
      _channels = 0;
      setChannels(2);
      }

AudioOutput::AudioOutput(const AudioOutput& t)
  : AudioTrack(t)
      {
      for (int i = 0; i < MAX_CHANNELS; ++i)
            jackPorts[i] = t.jackPorts[i];
      _nframes = t._nframes;
      }

//---------------------------------------------------------
//   ~AudioOutput
//---------------------------------------------------------

AudioOutput::~AudioOutput()
      {
      for (int i = 0; i < _channels; ++i)
            audioDevice->unregisterPort(jackPorts[i]);
      }

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

void AudioOutput::write(int level, Xml& xml) const
      {
      xml.tag(level++, "AudioOutput");
      AudioTrack::writeProperties(level, xml);
      xml.etag(level, "AudioOutput");
      }

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

void AudioOutput::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 (AudioTrack::readProperties(xml, tag))
                              xml.unknown("AudioOutput");
                        break;
                  case Xml::Attribut:
                        break;
                  case Xml::TagEnd:
                        if (tag == "AudioOutput") {
                              setName(name());  // allocate jack ports
                              return;
                              }
                  default:
                        break;
                  }
            }
      }

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

void AudioGroup::write(int level, Xml& xml) const
      {
      xml.tag(level++, "AudioGroup");
      AudioTrack::writeProperties(level, xml);
      xml.etag(level, "AudioGroup");
      }

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

void AudioGroup::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 (AudioTrack::readProperties(xml, tag))
                              xml.unknown("AudioGroup");
                        break;
                  case Xml::Attribut:
                        break;
                  case Xml::TagEnd:
                        if (tag == "AudioGroup")
                              return;
                  default:
                        break;
                  }
            }
      }

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

void AudioAux::write(int level, Xml& xml) const
      {
      xml.tag(level++, "AudioAux");
      AudioTrack::writeProperties(level, xml);
      xml.etag(level, "AudioAux");
      }

//---------------------------------------------------------
//   AudioAux
//---------------------------------------------------------

AudioAux::AudioAux()
   : AudioTrack(AUDIO_AUX)
      {
      _channels = 0;
      setChannels(2);
      for (int i = 0; i < MAX_CHANNELS; ++i)
            buffer[i] = (i < channels()) ? new float[segmentSize] : 0;
      }

//---------------------------------------------------------
//   AudioAux
//---------------------------------------------------------

AudioAux::~AudioAux()
      {
      for (int i = 0; i < channels(); ++i)
            delete[] buffer[i];
      }

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

void AudioAux::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 (AudioTrack::readProperties(xml, tag))
                              xml.unknown("AudioAux");
                        break;
                  case Xml::Attribut:
                        break;
                  case Xml::TagEnd:
                        if (tag == "AudioAux")
                              return;
                  default:
                        break;
                  }
            }
      }

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

bool AudioAux::getData(unsigned /*pos*/, int ch, unsigned /*samples*/, float** data)
      {
      for (int i = 0; i < ch; ++i)
            data[i] = buffer[i % channels()];
      return true;
      }

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

void AudioAux::setChannels(int n)
      {
      if (n > channels()) {
            for (int i = channels(); i < n; ++i)
                  buffer[i] = new float[segmentSize];
            }
      else if (n < channels()) {
            for (int i = n; i < channels(); ++i)
                  delete[] buffer[i];
            }
      AudioTrack::setChannels(n);
      }

//---------------------------------------------------------
//   setRecordFlag1
//    gui part (executed in gui thread)
//---------------------------------------------------------

void AudioTrack::setRecordFlag1(bool f)
      {
      if (f == _recordFlag)
            return;
      if (f) {
            if (_recFile == 0) {
                  //
                  // create soundfile for recording
                  //
                  char buffer[128];
                  QFile fil;
                  for (;;++recFileNumber) {
                     sprintf(buffer, "%s/rec%d.wav",
                        museProject.latin1(),
                        recFileNumber);
                     fil.setName(QString(buffer));
                     if (!fil.exists())
                        break;
                        }
                  _recFile = new SndFile(QString(buffer));
                  _recFile->setFormat(
                     SF_FORMAT_WAV | SF_FORMAT_FLOAT,
                     _channels, sampleRate);
                  }
            _recFile->openWrite();
            if (debugMsg)
                  printf("AudioNode::setRecordFlag1: create internal file %s\n",
                     _recFile->path().latin1());
            }
      else {
            if (_recFile) {
                  // this file has not been processed and can be
                  // deleted
	            QString s = _recFile->path();
                  remove(s.latin1());
                  if (debugMsg)
                        printf("AudioNode::setRecordFlag1: remove file %s\n", s.latin1());
                  _recFile = 0;
                  }
            }
      }
double AudioTrack::auxSend(int idx) const
      {
      if (unsigned(idx) >= _auxSend.size()) {
            printf("%s auxSend: bad index: %d >= %d\n",
               name().latin1(), idx, _auxSend.size());
            return 0.0;
            }
      return _auxSend[idx];
      }

void AudioTrack::setAuxSend(int idx, double v)
      {
      if (unsigned(idx) >= _auxSend.size()) {
            printf("%s setAuxSend: bad index: %d >= %d\n",
               name().latin1(), idx, _auxSend.size());
            return;
            }
      _auxSend[idx] = v;
      }

