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

#include <cmath>
#include <assert.h>
#include <sndfile.h>

#include "node.h"
#include "globals.h"
#include "song.h"
#include "xml.h"
#include "plugins/plugin.h"
#include "synth.h"
#include "audiodev.h"
#include "audio.h"
#include "wave.h"
#include "utils.h" //debug

bool AudioNode::_soloMode = false;
bool MidiNode::_soloMode = false;
std::list<MidiNode*> MidiNode::nodeList;
std::list<AudioNode*> AudioNode::nodeList;

AudioOutputPort audioOutput;  // output device node ("Master" strip)
AudioInputPort audioInput;    // output device node
AudioNode audioGroups[AUDIO_GROUPS];
int mixerGroups = AUDIO_GROUPS;

//---------------------------------------------------------
//   MidiNode
//---------------------------------------------------------

MidiNode::MidiNode()
   : SNode()
      {
      nodeList.push_back(this);
      }

MidiNode::~MidiNode()
      {
      for (std::list<MidiNode*>::iterator i = nodeList.begin();
         i != nodeList.end(); ++i) {
            if (*i == this) {
                  nodeList.erase(i);
                  break;
                  }
            }
      }

//---------------------------------------------------------
//   connect
//---------------------------------------------------------

void connectNodes(AudioNode* out, AudioNode* in)
      {
      out->connectOut(in);
      in->connectIn(out);
      }

//---------------------------------------------------------
//   disconnect
//---------------------------------------------------------

void disconnectNodes(AudioNode* out, AudioNode* in)
      {
      out->disconnectOut(in);
      in->disconnectIn(out);
      }

//---------------------------------------------------------
//   node2Name
//    create string name representation for audio node
//---------------------------------------------------------

QString node2Name(const AudioNode* n)
      {
      if (n == 0)
            return QWidget::tr("None");
      if (n == &audioOutput)
            return QWidget::tr("Master");
      if (n == &audioInput)
            return QWidget::tr("Input");
      for (int i = 0; i < mixerGroups; ++i) {
            if (n == &audioGroups[i]) {
                  QString s;
                  s.sprintf(QWidget::tr("Group %c"), i + 'A');
                  return s;
                  }
            }
      TrackList* tracks = song->tracks();
      int trackno = 0;
      for (iTrack i = tracks->begin(); i != tracks->end(); ++i, ++trackno) {
            WaveTrack* track = dynamic_cast<WaveTrack*>(*i);
            if (track == n)
                  return track->name();
            }
      int synthno = 0;
      for (iSynthI i = synthiInstances.begin(); i != synthiInstances.end();
         ++i, ++synthno) {
            AudioNode* node = *i;
            if (node == n) {
                  char buffer[32];
                  sprintf(buffer, "Synth %d", synthno);
                  return QString(buffer);
                  }
            }
//      printf("unknown audio node %p\n", n);
      return QWidget::tr("Unknown");
      }

//---------------------------------------------------------
//   name2Node
//---------------------------------------------------------

AudioNode* name2Node(const QString& s)
      {
      if (s == QWidget::tr("Master"))
            return &audioOutput;
      if (s == QWidget::tr("Input"))
            return &audioInput;
      QString group(QWidget::tr("Group"));
      if (s.startsWith(group)) {
            int n = group.length();
            int i = s.at(n+1).latin1() - 'A';
            return &audioGroups[i];
            }
      TrackList* tracks = song->tracks();
      for (iTrack i = tracks->begin(); i != tracks->end(); ++i) {
            WaveTrack* track = dynamic_cast<WaveTrack*>(*i);
            if (track) {
                  if (track->name() == s)
                        return track;
                  }
            }
printf("name2Node failed <%s>\n", s.latin1());
      return 0;
      }

//---------------------------------------------------------
//   SNode
//---------------------------------------------------------

SNode::SNode()
      {
      _activity     = 0;
      _lastActivity = 0;
      _recordFlag   = false;
      _mute         = false;
      _solo         = false;
      _off          = false;
      }

SNode::SNode(const SNode& s)
      {
      _activity     = 0;
      _lastActivity = 0;
      _recordFlag   = s._recordFlag;
      _mute         = s._mute;
      _solo         = s._solo;
      _off          = s._off;
      }

//---------------------------------------------------------
//   readProperty
//---------------------------------------------------------

bool SNode::readProperty(Xml& xml, const QString& tag)
      {
      if (tag == "mute")
            _mute = xml.parseInt();
      else if (tag == "off")
            _off = xml.parseInt();
      else if (tag == "solomute")
            xml.parseInt();   // obsolete
      else if (tag == "solo")
            _solo = xml.parseInt();
      else if (tag == "record")
            _recordFlag = xml.parseInt();
      else
            return false;
      return true;
      }

//---------------------------------------------------------
//   isMute
//---------------------------------------------------------

bool MidiNode::isMute() const
      {
      if (_solo)
            return false;
      if (_soloMode)
            return true;
      return _mute;
      }

bool AudioNode::isMute() const
      {
      if (_solo)
            return false;
      if (_soloMode)
            return true;
      return _mute;
      }

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

void MidiNode::setSolo(bool val)
      {
      for (std::list<MidiNode*>::iterator i = nodeList.begin();
         i != nodeList.end(); ++i) {
            (*i)->_solo = false;
            }
      _solo    = val;
      _soloMode = _solo;
      song->update(SC_SOLO);
      }

void AudioNode::setSolo(bool val)
      {
      for (std::list<AudioNode*>::iterator i = nodeList.begin();
         i != nodeList.end(); ++i) {
            (*i)->_solo = false;
            }
      _solo    = val;
      _soloMode = _solo;
      if (mute())
            resetMeter();
      song->update(SC_SOLO);
      }

//---------------------------------------------------------
//   addSolo
//---------------------------------------------------------

void MidiNode::addSolo()
      {
      _solo    = true;
      _soloMode = true;
      song->update(SC_SOLO);
      }

void AudioNode::addSolo()
      {
      _solo    = true;
      _soloMode = true;
      song->update(SC_SOLO);
      }

//---------------------------------------------------------
//   setMute
//---------------------------------------------------------

void SNode::setMute(bool val)
      {
      _mute = val;
      song->update(SC_MUTE);
      }

//---------------------------------------------------------
//   setOff
//---------------------------------------------------------

void SNode::setOff(bool val)
      {
      _off = val;
      song->update(SC_MUTE);
      }

//---------------------------------------------------------
//   segmentSizeChanged
//---------------------------------------------------------

void AudioNode::segmentSizeChanged()
      {
      }

//---------------------------------------------------------
//   copyData
//---------------------------------------------------------

void AudioNode::copyData(int dstChannels, unsigned long nframes, float** dstBuffer)
      {
      int srcChannels = ports();
      float* buffer[srcChannels];
      float data[nframes * srcChannels];

      for (int i = 0; i < srcChannels; ++i)
            buffer[i] = data + i * nframes;

      if (off() || (isMute() && !_prefader) || !getData(srcChannels, nframes, buffer)) {
            for (int i = 0; i < dstChannels; ++i) {
                  memset(dstBuffer[i], 0, sizeof(float) * nframes);
                  _meter[i] = 0;
                  }
            return;
            }

      //---------------------------------------------------
      // apply plugin chain
      //---------------------------------------------------

      _efxPipe->apply(srcChannels, nframes, buffer);

      //---------------------------------------------------
      //    prefader metering
      //---------------------------------------------------

      float meter[srcChannels];
      if (_prefader) {
            for (int i = 0; i < srcChannels; ++i) {
                  float* p = buffer[i];
                  meter[i] = 0.0;
                  for (unsigned k = 0; k < nframes; ++k) {
                        double f = fabs(*p);
                        if (f > meter[i])
                              meter[i] = f;
                        ++p;
                        }
                  _meter[i] = lrint(meter[i] * 32767.0);
                  if (_meter[i] > _peak[i])
                        _peak[i] = _meter[i];
                  }
            }
      if (isMute()) {
            if (_prefader) {
                  for (int i = 0; i < dstChannels; ++i) {
                        memset(dstBuffer[i], 0, sizeof(float) * nframes);
                        }
                  }
            return;
            }

      //---------------------------------------------------
      // apply volume
      //    postfader metering
      //---------------------------------------------------

      double vol[2];
      vol[0] = _volume * (1.0 - _pan);
      vol[1] = _volume * (1.0 + _pan);

      if (srcChannels == dstChannels) {
            if (_prefader) {
                  for (int c = 0; c < dstChannels; ++c) {
                        float* sp = buffer[c];
                        float* dp = dstBuffer[c];
                        for (unsigned k = 0; k < nframes; ++k)
                              *dp++ = (*sp++ * vol[c]);
                        }
                  }
            else {
                  for (int c = 0; c < dstChannels; ++c) {
                        meter[c] = 0.0;
                        float* sp = buffer[c];
                        float* dp = dstBuffer[c];
                        for (unsigned k = 0; k < nframes; ++k) {
                              float val = *sp++ * vol[c];
                              *dp++ = val;
                              double f = fabs(val);
                              if (f > meter[c])
                                    meter[c] = f;
                              }
                        _meter[c] = lrint(meter[c] * 32767.0);
                        if (_meter[c] > _peak[c])
                              _peak[c] = _meter[c];
                        }
                  }
            }
      else if (srcChannels == 1 && dstChannels == 2) {
            if (_prefader) {
                  for (int c = 0; c < dstChannels; ++c) {
                        float* dp = dstBuffer[c];
                        float* sp = buffer[0];
                        for (unsigned k = 0; k < nframes; ++k)
                              *dp++ = (*sp++ * vol[c]);
                        }
                  }
            else {
                  float* sp = buffer[0];
                  meter[0]  = 0.0;
                  for (unsigned k = 0; k < nframes; ++k) {
                        float val = *sp++;
                        double f = fabs(val);
                        if (f > meter[0])
                              meter[0] = f;
                        *(dstBuffer[0] + k) = val * vol[0];
                        *(dstBuffer[1] + k) = val * vol[1];
                        }
                  _meter[0] = lrint(meter[0] * 32767.0);
                  if (_meter[0] > _peak[0])
                        _peak[0] = _meter[0];
                  }
            }
      else if (srcChannels == 2 && dstChannels == 1) {
            float* sp1 = buffer[0];
            float* sp2 = buffer[1];
            if (_prefader) {
                  float* dp = dstBuffer[0];
                  for (unsigned k = 0; k < nframes; ++k)
                        *dp++ = (*sp1++ * vol[0] + *sp2++ * vol[1]);
                  }
            else {
                  float* dp = dstBuffer[0];
                  meter[0] = 0.0;
                  meter[1] = 0.0;
                  for (unsigned k = 0; k < nframes; ++k) {
                        float val1 = *sp1++ * vol[0];
                        float val2 = *sp2++ * vol[1];
                        double f1 = fabs(val1);
                        if (f1 > meter[0])
                              meter[0] = f1;
                        double f2 = fabs(val2);
                        if (f2 > meter[1])
                              meter[1] = f2;
                        *dp++ = (val1 + val2);
                        }
                  _meter[0] = lrint(meter[0] * 32767.0);
                  if (_meter[0] > _peak[0])
                        _peak[0] = _meter[0];
                  _meter[1] = lrint(meter[1] * 32767.0);
                  if (_meter[1] > _peak[1])
                        _peak[1] = _meter[1];
                  }
            }
      }

//---------------------------------------------------------
//   addData
//---------------------------------------------------------

void AudioNode::addData(int dstChannels, unsigned long nframes, float** dstBuffer)
      {
      if (off())
            return;
      int srcChannels = ports();
      float* buffer[srcChannels];
      float data[nframes * srcChannels];

      for (int i = 0; i < srcChannels; ++i)
            buffer[i] = data + i * nframes;
      // getData can use the supplied buffers or change buffer to point
      // to local buffers

      if (!getData(srcChannels, nframes, buffer)) {
            for (int i = 0; i < srcChannels; ++i)
                  _meter[i] = 0;
            return;
            }

      //---------------------------------------------------
      // apply plugin chain
      //---------------------------------------------------

      _efxPipe->apply(srcChannels, nframes, buffer);

      //---------------------------------------------------
      //    prefader metering
      //---------------------------------------------------

      float meter[srcChannels];
      if (_prefader) {
            for (int i = 0; i < srcChannels; ++i) {
                  float* p = buffer[i];
                  meter[i] = 0.0;
                  for (unsigned k = 0; k < nframes; ++k) {
                        double f = fabs(*p);
                        if (f > meter[i])
                              meter[i] = f;
                        ++p;
                        }
                  _meter[i] = lrint(meter[i] * 32767.0);
                  if (_meter[i] > _peak[i])
                        _peak[i] = _meter[i];
                  }
            }
      if (isMute())
            return;

      //---------------------------------------------------
      // apply volume
      //    postfader metering
      //---------------------------------------------------

      double vol[2];
      vol[0] = _volume * (1.0 - _pan);
      vol[1] = _volume * (1.0 + _pan);

      if (srcChannels == dstChannels) {
            if (_prefader) {
                  for (int c = 0; c < dstChannels; ++c) {
                        float* sp = buffer[c];
                        float* dp = dstBuffer[c];
                        for (unsigned k = 0; k < nframes; ++k)
                              *dp++ += (*sp++ * vol[c]);
                        }
                  }
            else {
                  for (int c = 0; c < dstChannels; ++c) {
                        meter[c] = 0.0;
                        float* sp = buffer[c];
                        float* dp = dstBuffer[c];
                        for (unsigned k = 0; k < nframes; ++k) {
                              float val = *sp++ * vol[c];
                              *dp++ += val;
                              double f = fabs(val);
                              if (f > meter[c])
                                    meter[c] = f;
                              }
                        _meter[c] = lrint(meter[c] * 32767.0);
                        if (_meter[c] > _peak[c])
                              _peak[c] = _meter[c];
                        }
                  }
            }
      else if (srcChannels == 1 && dstChannels == 2) {
            if (_prefader) {
                  for (int c = 0; c < dstChannels; ++c) {
                        float* dp = dstBuffer[c];
                        float* sp = buffer[0];
                        for (unsigned k = 0; k < nframes; ++k)
                              *dp++ += (*sp++ * vol[c]);
                        }
                  }
            else {
                  float* sp = buffer[0];
                  meter[0]  = 0.0;
                  for (unsigned k = 0; k < nframes; ++k) {
                        float val = *sp++;
                        double f = fabs(val);
                        if (f > meter[0])
                              meter[0] = f;
                        *(dstBuffer[0] + k) += val * vol[0];
                        *(dstBuffer[1] + k) += val * vol[1];
                        }
                  _meter[0] = lrint(meter[0] * 32767.0);
                  if (_meter[0] > _peak[0])
                        _peak[0] = _meter[0];
                  }
            }
      else if (srcChannels == 2 && dstChannels == 1) {
            float* sp1 = buffer[0];
            float* sp2 = buffer[1];
            if (_prefader) {
                  float* dp = dstBuffer[0];
                  for (unsigned k = 0; k < nframes; ++k)
                        *dp++ += (*sp1++ * vol[0] + *sp2++ * vol[1]);
                  }
            else {
                  float* dp = dstBuffer[0];
                  meter[0] = 0.0;
                  meter[1] = 0.0;
                  for (unsigned k = 0; k < nframes; ++k) {
                        float val1 = *sp1++ * vol[0];
                        float val2 = *sp2++ * vol[1];
                        double f1 = fabs(val1);
                        if (f1 > meter[0])
                              meter[0] = f1;
                        double f2 = fabs(val2);
                        if (f2 > meter[1])
                              meter[1] = f2;
                        *dp++ += (val1 + val2);
                        }
                  _meter[0] = lrint(meter[0] * 32767.0);
                  if (_meter[0] > _peak[0])
                        _peak[0] = _meter[0];
                  _meter[1] = lrint(meter[1] * 32767.0);
                  if (_meter[1] > _peak[1])
                        _peak[1] = _meter[1];
                  }
            }
      }

//---------------------------------------------------------
//   writeConfiguration
//---------------------------------------------------------

void AudioNode::writeConfiguration(int level, Xml& xml) const
      {
      xml.tag(level++, "audionode");
      xml.intTag(level, "channels", ports());

      for (ciAudioNode i = _outRoute.begin(); i != _outRoute.end(); ++i) {
            QString s = node2Name(*i);
            xml.strTag(level, "connect", s);
            }
      xml.put(level, "<volume>%f</volume>", _volume);
      xml.doubleTag(level, "pan", _pan);
      xml.intTag(level, "mute", mute());
      xml.intTag(level, "solo", solo());
      xml.intTag(level, "prefader", prefader());
      xml.intTag(level, "off", off());
      for (ciPluginI ip = _efxPipe->begin(); ip != _efxPipe->end(); ++ip) {
            if (*ip)
                  (*ip)->writeConfiguration(level, xml);
            }
      xml.etag(level, "audionode");
      }

//---------------------------------------------------------
//   readVolume
//---------------------------------------------------------

void AudioNode::readVolume(Xml& xml)
      {
      int ch = 0;
      for (;;) {
            Xml::Token token = xml.parse();
            switch (token) {
                  case Xml::Error:
                  case Xml::End:
                        return;
                  case Xml::TagStart:
                        xml.unknown("readVolume");
                        break;
                  case Xml::Text:
                        setVolume(xml.s1().toDouble());
                        break;
                  case Xml::Attribut:
                        if (xml.s1() == "ch")
                              ch = xml.s2().toInt();
                        break;
                  case Xml::TagEnd:
                        if (xml.s1() == "volume")
                              return;
                  default:
                        break;
                  }
            }
      }

//---------------------------------------------------------
//   readRecfile
//---------------------------------------------------------

void AudioNode::readRecfile(Xml& xml)
      {
      QString path;
      int channels = 2;
      int format   = SF_FORMAT_WAV | SF_FORMAT_PCM_16;

      for (;;) {
            Xml::Token token = xml.parse();
            if (token == Xml::Error || token == Xml::End)
                  break;
            const QString& tag = xml.s1();
            switch (token) {
                  case Xml::TagStart:
                        if (tag == "path")
                              path = xml.parse1();
                        else if (tag == "channels")
                              channels = xml.parseInt();
                        else if (tag == "format")
                              format = xml.parseInt();
                        else if (tag == "samplebits")
                              ;
                        else
                              xml.unknown("recfile");
                        break;
                  case Xml::TagEnd:
                        if (tag == "recfile") {
                              if (QFile::exists(path)) {
                                    _recFile = getWave(path, true);
                                    }
                              else {
                                    _recFile = new SndFile(path);
                                    _recFile->setFormat(format, channels, sampleRate);
                                    if (_recFile->openWrite()) {
                                          fprintf(stderr, "create wave file(%s) failed: %s\n",
                                             path.latin1(), _recFile->strerror().latin1());
                                          delete _recFile;
                                          _recFile = 0;
                                          }
                                    }
                              return;
                              }
                  default:
                        break;
                  }
            }
      }

//---------------------------------------------------------
//   readConfiguration
//---------------------------------------------------------

void AudioNode::readConfiguration(Xml& xml)
      {
      // remove old routes
      disconnectClear();
      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 == "connect") {
                              QString s = xml.parse1();
                              AudioNode* node = name2Node(s);
                              if (node)
                                    connectOut(node);
                              }
                        else if (tag == "channels") {
                              int ports = xml.parseInt();
                              setPorts(ports);
                              }
                        else if (tag == "volume")
                              readVolume(xml);
                        else if (tag == "pan")
                              setPan(xml.parseDouble());
                        else if (tag == "plugin") {
                              PluginI* pi = new PluginI;
                              pi->readConfiguration(xml, false);
                              // 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 (SNode::readProperty(xml, tag))
                              ;
                        else if (tag == "prefader")
                              _prefader = xml.parseInt();
                        else if (tag == "recfile")
                              readRecfile(xml);
                        else
                              xml.unknown("audionode");
                        break;
                  case Xml::Attribut:
                        break;
                  case Xml::TagEnd:
                        if (tag == "audionode")
                              return;
                  default:
                        break;
                  }
            }
      }

//---------------------------------------------------------
//   AudioNode
//---------------------------------------------------------

AudioNode::AudioNode()
   : SNode()
      {
      _ports    = 0;                   // 1 - mono, 2 - stereo
      _volume   = 1.0;
      _pan      = 0.0;
      _prefader = false;
      _efxPipe  = new Pipeline();
      _recFile  = 0;
      nodeList.push_back(this);
      }

AudioNode::AudioNode(const AudioNode& s)
   : SNode(s)
      {
      _inRoute  = s._inRoute;
      _outRoute = s._outRoute;
      _ports    = s._ports;
      _volume   = s._volume;
      _pan      = s._pan;
      _prefader = s._prefader;
      _efxPipe  = new Pipeline(*(s._efxPipe));
      _meter[0] = 0;
      _meter[1] = 0;
      _peak[0]  = 0;
      _peak[1]  = 0;
      _recFile  = s._recFile;
      fifo      = s.fifo;
      }

//---------------------------------------------------------
//   ~AudioNode
//---------------------------------------------------------

AudioNode::~AudioNode()
      {
      for (std::list<AudioNode*>::iterator i = nodeList.begin();
         i != nodeList.end(); ++i) {
            if (*i == this) {
                  nodeList.erase(i);
                  break;
                  }
            }
      if (_recFile)
            delete _recFile;
      delete _efxPipe;
      }

//---------------------------------------------------------
//   setPorts
//---------------------------------------------------------

void AudioNode::setPorts(int n)
      {
      if (n == _ports)
            return;
      _ports = n;
      if (_recFile) {
            if (_recFile->samples() == 0) {
                  _recFile->remove();
                  _recFile->setFormat(_recFile->format(), _ports,
                     _recFile->samplerate());
                  _recFile->openWrite();
                  }
            }
      for (int i = 0; i < _ports; ++i) {
            _meter[i] = 0;
            _peak[i]  = 0;
            }
      }

//---------------------------------------------------------
//   putFifo
//---------------------------------------------------------

void AudioNode::putFifo(int channels, unsigned long n, float** bp)
      {
      if (fifo.put(channels, n, bp, audio->pos())) {
            if (this == &audioOutput)
                  printf("  overrun audio output\n");
            else
                  printf("   overrun ???\n");
            }
      }

//---------------------------------------------------------
//   removeRoute
//---------------------------------------------------------

void AudioNode::disconnectOut(AudioNode* p)
      {
      for (iAudioNode i = _outRoute.begin(); i != _outRoute.end(); ++i) {
            if (*i == p) {
                  _outRoute.erase(i);
                  break;
                  }
            }
//      resetMeter();
      }

void AudioNode::disconnectIn(AudioNode* p)
      {
      for (iAudioNode i = _inRoute.begin(); i != _inRoute.end(); ++i) {
            if (*i == p) {
                  _inRoute.erase(i);
                  break;
                  }
            }
      resetMeter();
      }

//---------------------------------------------------------
//   connect
//---------------------------------------------------------

void AudioNode::connectIn(AudioNode* p)
      {
      for (iAudioNode n = _inRoute.begin(); n != _inRoute.end(); ++n)
            if (p == *n)
                  return;
      _inRoute.push_back(p);
      }

void AudioNode::connectOut(AudioNode* p)
      {
      for (iAudioNode n = _outRoute.begin(); n != _outRoute.end(); ++n)
            if (p == *n)
                  return;
      _outRoute.push_back(p);
      }

void AudioNode::connect()
      {
      for (iAudioNode n = _outRoute.begin(); n != _outRoute.end(); ++n) {
            (*n)->connectIn(this);
            }
      }

void AudioNode::disconnect()
      {
      for (iAudioNode n = _outRoute.begin(); n != _outRoute.end(); ++n)
            (*n)->disconnectIn(this);
      }

void AudioNode::disconnectClear()
      {
      disconnect();
      _outRoute.clear();
      }

//---------------------------------------------------------
//   route
//    return first output route
//---------------------------------------------------------

AudioNode* AudioNode::route() const
      {
      if (_outRoute.empty())
            return 0;
      return _outRoute.front();
      }

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

bool AudioNode::getData(int ports, unsigned long nframes, float** buffer)
      {
      // use supplied buffers

      iAudioNode i = _inRoute.begin();
      if (i == _inRoute.end())
            return false;

      (*i)->copyData(ports, nframes, buffer);
      ++i;
      for (; i != _inRoute.end(); ++i)
            (*i)->addData(ports, nframes, buffer);
      return true;
      }

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

bool AudioInputPort::getData(int ports, unsigned long nframes, float** buffer)
      {
      if (audioDevice) {
            audioDevice->read(ports, nframes, buffer);
            return true;
            }
      return false;
      }

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

void AudioNode::resetMeter()
      {
      for (int i = 0; i < _ports; ++i)
            _meter[i] = 0;
      }

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

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

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

void AudioNode::resetAllMeter()
      {
      for (std::list<AudioNode*>::iterator i = nodeList.begin();
         i != nodeList.end(); ++i) {
            (*i)->resetMeter();
            }
      }

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

void AudioNode::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,
                     _ports,
                     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;
                  }
            }
      }

//---------------------------------------------------------
//   setRecordFlag
//    real time part (executed in audio thread)
//---------------------------------------------------------

void AudioNode::setRecordFlag2(bool f)
      {
      if (f == _recordFlag)
            return;
      _recordFlag = f;
      if (!_recordFlag)
            resetMeter();
      }

//---------------------------------------------------------
//   setMute
//---------------------------------------------------------

void AudioNode::setMute(bool f)
      {
      _mute = f;
      if (_mute)
            resetAllMeter();
      song->update(SC_MUTE);
      }

//---------------------------------------------------------
//   setOff
//---------------------------------------------------------

void AudioNode::setOff(bool val)
      {
      _off = val;
      if (val)
            resetAllMeter();
      song->update(SC_MUTE);
      }

//---------------------------------------------------------
//   setPrefader
//---------------------------------------------------------

void AudioNode::setPrefader(bool val)
      {
      _prefader = val;
      if (!_prefader && isMute())
            resetAllMeter();
      }

//---------------------------------------------------------
//   canRecord
//---------------------------------------------------------

bool AudioNode::canRecord() const
      {
      return  (!_inRoute.empty() || (this == song->bounceTrack));
      }

//---------------------------------------------------------
//   AudioInputPort
//---------------------------------------------------------

AudioInputPort::AudioInputPort()
   : AudioPort()
      {
      // set Default for Input Ports:
      _mute = true;
      _volume = 0.0;
      }

//---------------------------------------------------------
//   AudioOutputPort
//---------------------------------------------------------

AudioOutputPort::AudioOutputPort()
   : AudioPort()
      {
      }

//---------------------------------------------------------
//   AudioOutputPort
//---------------------------------------------------------

AudioOutputPort::~AudioOutputPort()
      {
      }

//---------------------------------------------------------
//   record
//---------------------------------------------------------

void AudioNode::record()
      {
      int ports;
      unsigned long nframes;
      unsigned long pos;
      float* buffer[_ports];

      if (fifo.get(ports, nframes, buffer, pos)) {
            printf("AudioNode::record(): fifo underrun\n");
            return;
            }
      if (_recFile) {
            _recFile->seek(pos, 0);
            _recFile->write(ports, buffer, nframes);
            }
      else {
            printf("AudioNode::record(): no recFile\n");
            }
      }

//---------------------------------------------------------
//   process
//---------------------------------------------------------

void AudioOutputPort::process(unsigned long nframes)
      {
      float* buffer[_ports];
      float data[nframes * _ports];

      if (audioDevice->getWriteBuffer(_ports, nframes, buffer)) {
            copyData(_ports, nframes, buffer);
            }
      else {
            for (int i = 0; i < _ports; ++i)
                  buffer[i] = data + i * nframes;

            copyData(_ports, nframes, buffer);
            audioDevice->write(_ports, nframes, buffer);
            }
      if (audio->isPlaying() && song->record()) {
            WaveTrack* track = song->bounceTrack;
            if (track && track->recordFlag() && track->recFile()) {
                  track->putFifo(_ports, nframes, buffer);
                  }
            if (recordFlag() && recFile()) {
                  putFifo(_ports, nframes, buffer);
                  }
            }
      }

//---------------------------------------------------------
//   Fifo
//---------------------------------------------------------

Fifo::Fifo()
      {
      nbuffer = FIFO_BUFFER;
      buffer  = new FifoBuffer*[nbuffer];
      for (int i = 0; i < nbuffer; ++i)
            buffer[i]  = new FifoBuffer;
      clear();
      }

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

//---------------------------------------------------------
//   put
//    return true if fifo full
//---------------------------------------------------------

bool Fifo::put(int segs, unsigned long samples, float** src, unsigned long pos)
      {
      if (atomic_read(&count) == nbuffer) {
            printf("FIFO overrun...\n");
            return true;
            }
      FifoBuffer* b = buffer[widx];
      widx          = (widx + 1) % nbuffer;
      int n         = segs * samples;
      if (b->maxSize < n) {
            if (b->buffer)
                 delete[] b->buffer;
            b->buffer = new float[n];
            b->maxSize = n;
            }
      b->size = samples;
      b->segs = segs;
      b->pos  = pos;
      for (int i = 0; i < segs; ++i)
            memcpy(b->buffer + i * samples, src[i], samples * sizeof(float));
      atomic_inc(&count);
      return false;
      }

//---------------------------------------------------------
//   get
//    return true if fifo empty
//---------------------------------------------------------

bool Fifo::get(int& segs, unsigned long& samples, float** dst, unsigned long& pos)
      {
      if (atomic_read(&count) == 0) {
            printf("FIFO underrun...\n");
            return true;
            }
      FifoBuffer* b = buffer[ridx];
      segs    = b->segs;
      samples = b->size;
      pos     = b->pos;
      for (int i = 0; i < segs; ++i)
            dst[i] = b->buffer + samples * i;
      ridx = (ridx + 1) % nbuffer;
      atomic_dec(&count);
      return false;
      }

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

void Fifo::remove()
      {
      ridx = (ridx + 1) % nbuffer;
      atomic_dec(&count);
      }

//---------------------------------------------------------
//   getWriteBuffer
//---------------------------------------------------------

bool Fifo::getWriteBuffer(int segs, unsigned long samples, float** buf, unsigned long pos)
      {
      if (atomic_read(&count) == nbuffer)
            return true;
      FifoBuffer* b = buffer[widx];
      int n = segs * samples;
      if (b->maxSize < n) {
            if (b->buffer)
                 delete[] b->buffer;
            b->buffer = new float[n];
            b->maxSize = n;
            }
      for (int i = 0; i < segs; ++i)
            buf[i] = b->buffer + i * samples;
      b->size = samples;
      b->segs = segs;
      b->pos  = pos;
      return false;
      }

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

void Fifo::add()
      {
      widx = (widx + 1) % nbuffer;
      atomic_inc(&count);
      }

