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

#include "song.h"
#include "track.h"
#include "undo.h"
#include "seq.h"
#include "key.h"
#include "globals.h"
#include "event.h"
#include "drummap.h"
#include "marker/marker.h"
#include "synth.h"
#include "audio.h"
#include "midithread.h"
#include "mididev.h"
#include "amixer.h"

#include <unistd.h>
#include <stdio.h>
#include <errno.h>
#include <qapplication.h>
#include <qmessagebox.h>

Song* song;

//---------------------------------------------------------
//   Song
//---------------------------------------------------------

Song::Song(const char* name)
   :QObject(0, name)
      {
      undoList     = new UndoList;
      redoList     = new UndoList;
      _markerList  = new MarkerList;
      _karaoke     = new EventList;
      _globalPitchShift = 0;
      clear(false);
      }

//---------------------------------------------------------
//   Song
//---------------------------------------------------------

Song::~Song()
      {
      delete undoList;
      delete redoList;
      delete _karaoke;
      delete _markerList;
      }

//---------------------------------------------------------
//   setTempo
//    public slot
//---------------------------------------------------------

void Song::setTempo(int newTempo)
      {
      midiThread->msgSetTempo(pos[0], newTempo, true);
      }

//---------------------------------------------------------
//   setSig
//    called from transport window
//---------------------------------------------------------

void Song::setSig(int z, int n)
      {
      if (_masterFlag) {
            midiThread->msgAddSig(pos[0], z, n);
            }
      }

//---------------------------------------------------------
//    Track Manipulationen
//---------------------------------------------------------

Track* Song::addTrack(Track::TrackType type, Track* t)
      {
      Track* track = 0;
      switch(type) {
            case Track::MIDI:
                  track = new MidiTrack();
                  track->setType(Track::MIDI);
                  break;
            case Track::DRUM:
                  track = new MidiTrack();
                  track->setType(Track::DRUM);
                  break;
            case Track::WAVE:
                  track = new WaveTrack();
                  if (t) {
                        WaveTrack* st = (WaveTrack*) t;
                        WaveTrack* dt = (WaveTrack*) track;
                        dt->setPorts(st->ports());
                        //TODO
//                        track->setOutPort(st->outPort());
//                        track->setInPort(st->inPort());
//                        track->setOutChannel(st->Track::outChannel());
//                        ((WaveTrack*)track)->setRoute(st->route());
//                        ((WaveTrack*)track)->setPan(st->pan());
//                        ((WaveTrack*)track)->setVolume(st->volume());
                        }
                  break;
            }
      midiThread->msgAddTrack(track);
      return track;
      }

void Song::addTrack(Track* track)
      {
      track->newSn();
      _tracks.add(track);
      undoOp(UndoOp::AddTrack, _tracks.size()-1, track);
      updateFlags |= SC_TRACK_INSERTED;
      }

//---------------------------------------------------------
//   removeMarkedTracks
//---------------------------------------------------------

void Song::removeMarkedTracks()
      {
      bool loop;
      do {
            loop = false;
            for (iTrack t = _tracks.begin(); t != _tracks.end(); ++t) {
                  if ((*t)->selected()) {
                        int idx = _tracks.index(*t);
                        undoOp(UndoOp::DeleteTrack, idx, *t);
                        _tracks.erase(t);
                        loop = true;
                        break;
                        }
                  }
            } while (loop);
      updateFlags |= SC_TRACK_REMOVED;
      }

//---------------------------------------------------------
//   changeTrack
//    oldTrack  -  original track
//    newTrack  -  replace Track
//---------------------------------------------------------

void Song::changeTrack(Track* oldTrack, Track* newTrack)
      {
      oldTrack->setSelected(false);
      int trackno = _tracks.index(oldTrack);
      iTrack it   = _tracks.find(trackno);
      *it         = newTrack;
      newTrack->setSn(oldTrack->sn());
      undoOp(UndoOp::ModifyTrack, trackno, oldTrack);
      updateFlags |= SC_TRACK_MODIFIED;
      }

//---------------------------------------------------------
//    Event Manipulationen
//---------------------------------------------------------

void Song::addEvent(MidiEvent* event, MidiPart* part)
      {
      part->events()->add(event);
      }

//---------------------------------------------------------
//   changeEvent
//---------------------------------------------------------

void Song::changeEvent(MidiEvent* oldEvent, MidiEvent* newEvent, MidiPart* part)
      {
      iEvent i = part->events()->find(oldEvent);
      if (i != part->events()->end()) {
            oldEvent->incRefs(1);         // dont delete event
            part->events()->erase(i);
            oldEvent->incRefs(-1);
            part->events()->add(newEvent);
            }
      else {
            printf("Song::changeEvent(): EVENT %p not found !!\n", oldEvent);
            abort();
            }
      }

//---------------------------------------------------------
//   deleteEvent
//---------------------------------------------------------

void Song::deleteEvent(MidiEvent* event, MidiPart* part)
      {
      iEvent ev = part->events()->find(event);
      part->events()->erase(ev);
      }

//---------------------------------------------------------
//   cmdAddRecordedEvents
//    add recorded Events into part
//---------------------------------------------------------

void Song::cmdAddRecordedEvents(MidiTrack* mt, EventList* events, int startTick)
      {
// printf("cmdAddRecordedEvents\n");
      if (events->empty()) {
            if (debugMsg)
                  printf("no events recorded\n");
            return;
            }

      iEvent s;
      iEvent e;
      int endTick;

      if (punchin()) {
            s = events->lower_bound(lpos());
            startTick = lpos();
            }
      else {
            s = events->begin();
            startTick = s->first;
            }
      if (punchout()) {
            e = events->lower_bound(rpos());
            endTick = rpos();
            }
      else {
            // search for last noteOff:
            endTick = 0;
            for (iEvent i = events->begin(); i != events->end(); ++i) {
                  Event* ev = i->second;
                  int l     = ev->endTick();
                  if (l > endTick)
                        endTick = l;
                  }
            e = events->end();
            }
      if (startTick > endTick) {
            if (debugMsg)
                  printf("no events in record area\n");
            return;
            }

      //---------------------------------------------------
      //    if startTick points into a part,
      //          record to that part
      //    else
      //          create new part
      //---------------------------------------------------

      PartList* pl = mt->parts();
      MidiPart* part = 0;
      iPart ip;
      for (ip = pl->begin(); ip != pl->end(); ++ip) {
            part = (MidiPart*)(ip->second);
            int partStart = part->posTick();
            int partEnd   = partStart + part->lenTick();
            if (startTick >= partStart && startTick < partEnd)
                  break;
            }
      if (ip == pl->end()) {
            if (debugMsg)
                  printf("create new part for recorded events\n");
            // create new part
            part      = new MidiPart(mt);
            startTick = roundDownBar(startTick);
            endTick   = roundUpBar(endTick);
            part->setPosTick(startTick);
            part->setLenTick(endTick - startTick);
            part->setName(mt->name());
            // copy events
            EventList* de = part->events();
            for (iEvent i = s; i != e; ++i) {
                  const MidiEvent* old = (MidiEvent*)(i->second);
                  MidiEvent* event = new MidiEvent(*old);
                  event->move(-startTick);
                  de->add(event);
                  }
            startUndo();
            midiThread->msgAddPart(part);
            endUndo(SC_PART_INSERTED);
            return;
            }

      startUndo();
      int flags = SC_EVENT_INSERTED;
      int partTick = part->posTick();
      if (endTick > partTick + part->lenTick()) {
            Part* newPart = part->clone();
            if (_recMode == REC_REPLACE) {
                  // TODO: delete Events
                  }
            endTick = 0;
            for (iEvent i = s; i != e; ++i) {
                  MidiEvent* event = (MidiEvent*)i->second;
                  int tick = event->posTick() - partTick;
                  event->setPosTick(tick);
                  midiThread->msgAddEvent(event, (MidiPart*)newPart, false);
                  if (endTick < event->posTick() + event->lenTick())
                        endTick = event->posTick() + event->lenTick();
                  }
            newPart->setLenTick(endTick - part->posTick());
            midiThread->msgChangePart(part, newPart, false);
            flags |= SC_PART_MODIFIED;
            }
      else {
            if (_recMode == REC_REPLACE) {
                  // TODO: delete Events
                  }
            for (iEvent i = s; i != e; ++i) {
                  MidiEvent* event = (MidiEvent*)i->second;
                  int tick = event->posTick() - partTick;
                  event->setPosTick(tick);
                  midiThread->msgAddEvent(event, part, false);
                  }
            }
      endUndo(flags);
      }

//---------------------------------------------------------
//   findTrack
//---------------------------------------------------------

MidiTrack* Song::findTrack(const Part* part) const
      {
      for (ciTrack t = _tracks.begin(); t != _tracks.end(); ++t) {
            MidiTrack* track = dynamic_cast<MidiTrack*>(*t);
            if (track == 0)
                  continue;
            PartList* pl = track->parts();
            for (iPart p = pl->begin(); p != pl->end(); ++p) {
                  if (part == p->second)
                        return track;
                  }
            }
      return 0;
      }

//---------------------------------------------------------
//   setLoop
//    set transport loop flag
//---------------------------------------------------------

void Song::setLoop(bool f)
      {
      if (loopFlag != f) {
            loopFlag = f;
            loopAction->setOn(loopFlag);
            emit loopChanged(loopFlag);
            }
      }

//---------------------------------------------------------
//   setRecord
//    set transport loop flag
//---------------------------------------------------------

void Song::setRecord(bool f)
      {
      if (recordFlag != f) {
            if (f) {
#if 0
                  // check for midi devices suitable for recording
                  bool portFound = false;
                  for (int i = 0; i < MIDI_PORTS; ++i) {
                        MidiDevice* dev = midiPorts[i].device();
                        if (dev && (dev->rwFlags() & 0x2))
                              portFound = true;
                        }
                  if (!portFound) {
                        QMessageBox::critical(qApp->mainWidget(), "MusE: Record",
                           "There are no midi devices configured for recording");
                        f = false;
                        }
#endif
                  }
            else {
                  bounceTrack = 0;
                  }
            if (playFlag && f)
                  f = false;
            recordFlag = f;
            recordAction->setOn(recordFlag);
            emit recordChanged(recordFlag);
            }
      }

//---------------------------------------------------------
//   setPunchin
//    set punchin flag
//---------------------------------------------------------

void Song::setPunchin(bool f)
      {
      if (punchinFlag != f) {
            punchinFlag = f;
            punchinAction->setOn(punchinFlag);
            emit punchinChanged(punchinFlag);
            }
      }

//---------------------------------------------------------
//   setPunchout
//    set punchout flag
//---------------------------------------------------------

void Song::setPunchout(bool f)
      {
      if (punchoutFlag != f) {
            punchoutFlag = f;
            punchoutAction->setOn(punchoutFlag);
            emit punchoutChanged(punchoutFlag);
            }
      }

//---------------------------------------------------------
//   setClick
//---------------------------------------------------------

void Song::setClick(bool val)
      {
      if (_click != val) {
            _click = val;
            emit clickChanged(_click);
            }
      }

//---------------------------------------------------------
//   setQuantize
//---------------------------------------------------------

void Song::setQuantize(bool val)
      {
      if (_quantize != val) {
            _quantize = val;
            emit quantizeChanged(_quantize);
            }
      }

//---------------------------------------------------------
//   setMasterFlag
//---------------------------------------------------------

void Song::setMasterFlag(bool val)
      {
      _masterFlag = val;
      if (tempomap.setMasterFlag(cpos(), val))
            emit songChanged(SC_MASTER);
      }

//---------------------------------------------------------
//   setPlay
//    set transport play flag
//---------------------------------------------------------

void Song::setPlay(bool f)
      {
      setStopPlay(f ? true : playFlag);
      }

void Song::setStop(bool f)
      {
      setStopPlay(f ? false : playFlag);
      }

void Song::setStopPlay(bool f)
      {
      playAction->blockSignals(true);
      stopAction->blockSignals(true);

      if (playFlag != f) {
            playFlag = f;
            midiThread->msgPlay(playFlag);
            emit playChanged(playFlag);   // signal transport window
            }

      playAction->setOn(playFlag);
      stopAction->setOn(!playFlag);

      stopAction->blockSignals(false);
      playAction->blockSignals(false);
      }

//---------------------------------------------------------
//   swapTracks
//---------------------------------------------------------

void Song::swapTracks(int i1, int i2)
      {
      undoOp(UndoOp::SwapTrack, i1, i2);
      iTrack it1 = _tracks.find(i1);
      iTrack it2 = _tracks.find(i2);
      Track* track = *it1;
      *it1 = *it2;
      *it2 = track;
      }

//---------------------------------------------------------
//   setPos
//---------------------------------------------------------

void Song::setPos(int idx, int val, bool sig,
   bool isSeek, bool adjustScrollbar)
      {
//      printf("setPos(%d) %d sig=%d,seek=%d,scroll=%d\n",
//         idx, val, sig, isSeek, adjustScrollbar);

      if (pos[idx] == val)
            return;
      pos[idx]  = val;
      bool swap = pos[LPOS] > pos[RPOS];
      if (swap) {        // swap lpos/rpos if lpos > rpos
            int tmp = pos[LPOS];
            pos[LPOS] = pos[RPOS];
            pos[RPOS] = tmp;
            }
      if (sig) {
            if (swap) {
                  emit posChanged(LPOS, pos[LPOS], adjustScrollbar);
                  emit posChanged(RPOS, pos[RPOS], adjustScrollbar);
                  if (idx != LPOS && idx != RPOS)
                        emit posChanged(idx, pos[idx], adjustScrollbar);
                  }
            else
                  emit posChanged(idx, pos[idx], adjustScrollbar);
            }

      if (idx == CPOS) {
            if (isSeek)
                  midiThread->msgSeek(val);
            iMarker i1 = _markerList->begin();
            iMarker i2 = i1;
            bool currentChanged = false;
            for (; i1 != _markerList->end(); ++i1) {
                  ++i2;
                  if (val >= i1->first && (i2==_markerList->end() || val < i2->first)) {
                        if (i1->second.current())
                              return;
                        i1->second.setCurrent(true);
                        if (currentChanged) {
                              emit markerChanged(MARKER_CUR);
                              return;
                              }
                        ++i1;
                        for (; i1 != _markerList->end(); ++i1) {
                              if (i1->second.current())
                                    i1->second.setCurrent(false);
                              }
                        emit markerChanged(MARKER_CUR);
                        return;
                        }
                  else {
                        if (i1->second.current()) {
                              currentChanged = true;
                              i1->second.setCurrent(false);
                              }
                        }
                  }
            if (currentChanged)
                  emit markerChanged(MARKER_CUR);
            }
      }

//---------------------------------------------------------
//   forward
//---------------------------------------------------------

void Song::forward()
      {
      pos[0] += division;
      emit posChanged(0, pos[0], true);
      midiThread->msgSeek(pos[0]);
      }

//---------------------------------------------------------
//   rewind
//---------------------------------------------------------

void Song::rewind()
      {
      int newPos = pos[0] - division;
      if (newPos < 0)
            newPos = 0;
      pos[0] = newPos;
      emit posChanged(0, newPos, true);
      midiThread->msgSeek(newPos);
      }

//---------------------------------------------------------
//   rewindStart
//---------------------------------------------------------

void Song::rewindStart()
      {
      int newPos = 0;
      pos[0] = newPos;
      emit posChanged(0, newPos, true);
      midiThread->msgSeek(newPos);
      }

//---------------------------------------------------------
//   update
//---------------------------------------------------------

void Song::update(int flags)
      {
      static int level = 0;
      if (level) {
            printf("Song::update %08x, level %d\n", flags, level);
            return;
            }
      ++level;
      emit songChanged(flags);
      --level;
      }

//---------------------------------------------------------
//   updatePos
//---------------------------------------------------------

void Song::updatePos()
      {
      emit posChanged(0, pos[0], false);
      emit posChanged(1, pos[1], false);
      emit posChanged(2, pos[2], false);
      }

//---------------------------------------------------------
//   setChannelMute
//    mute all midi tracks associated with channel
//---------------------------------------------------------

void Song::setChannelMute(int channel, bool val)
      {
      for (iTrack i = _tracks.begin(); i != _tracks.end(); ++i) {
            MidiTrack* track = dynamic_cast<MidiTrack*>(*i);
            if (track == 0)
                  continue;
            if (track->outChannel() == channel)
                  track->setMute(val);
            }
      emit songChanged(SC_MUTE);
      }

//---------------------------------------------------------
//   len
//---------------------------------------------------------

void Song::initLen()
      {
      _len = sigmap.bar2tick(40, 0, 0);    // default song len
      for (iTrack t = _tracks.begin(); t != _tracks.end(); ++t) {
            MidiTrack* track = dynamic_cast<MidiTrack*>(*t);
            if (track == 0)
                  continue;
            PartList* parts = track->parts();
            for (iPart p = parts->begin(); p != parts->end(); ++p) {
                  int last = p->second->posTick()+p->second->lenTick();
                  if (last > _len)
                        _len = last;
                  }
            }
      _len = roundUpBar(_len);
      }

//---------------------------------------------------------
//   roundUpBar
//---------------------------------------------------------

int Song::roundUpBar(int t) const
      {
      int bar, beat, tick;
      sigmap.tickValues(t, &bar, &beat, &tick);
      if (beat || tick)
            return sigmap.bar2tick(bar+1, 0, 0);
      return t;
      }

//---------------------------------------------------------
//   roundUpBeat
//---------------------------------------------------------

int Song::roundUpBeat(int t) const
      {
      int bar, beat, tick;
      sigmap.tickValues(t, &bar, &beat, &tick);
      if (tick)
            return sigmap.bar2tick(bar, beat+1, 0);
      return t;
      }

//---------------------------------------------------------
//   roundDownBar
//---------------------------------------------------------

int Song::roundDownBar(int t) const
      {
      int bar, beat, tick;
      sigmap.tickValues(t, &bar, &beat, &tick);
      return sigmap.bar2tick(bar, 0, 0);
      }

//---------------------------------------------------------
//   dumpMaster
//---------------------------------------------------------

void Song::dumpMaster()
      {
      tempomap.dump();
      sigmap.dump();
      }

//---------------------------------------------------------
//   getSelectedParts
//---------------------------------------------------------

PartList* Song::getSelectedMidiParts() const
      {
      PartList* parts = new PartList();

      //------------------------------------------------------
      //    wenn ein Part selektiert ist, diesen editieren
      //    wenn ein Track selektiert ist, den Ersten
      //       Part des Tracks editieren, die restlichen sind
      //       'ghostparts'
      //    wenn mehrere Parts selektiert sind, dann Ersten
      //       editieren, die restlichen sind 'ghostparts'
      //

      // markierte Parts sammeln
      for (ciTrack t = _tracks.begin(); t != _tracks.end(); ++t) {
            MidiTrack* track = dynamic_cast<MidiTrack*>(*t);
            if (track == 0)
                  continue;
            PartList* pl = track->parts();
            for (iPart p = pl->begin(); p != pl->end(); ++p) {
                  if (p->second->selected()) {
                        parts->add(p->second);
                        }
                  }
            }
      // wenn keine Parts selektiert, dann markierten Track suchen
      // und alle Parts dieses Tracks zusammensuchen

      if (parts->empty()) {
            for (ciTrack t = _tracks.begin(); t != _tracks.end(); ++t) {
                  if ((*t)->selected()) {
                        MidiTrack* track = dynamic_cast<MidiTrack*>(*t);
                        if (track == 0)
                              continue;
                        PartList* pl = track->parts();
                        for (iPart p = pl->begin(); p != pl->end(); ++p)
                              parts->add(p->second);
                        break;
                        }
                  }
            }
      return parts;
      }

PartList* Song::getSelectedWaveParts() const
      {
      PartList* parts = new PartList();

      //------------------------------------------------------
      //    wenn ein Part selektiert ist, diesen editieren
      //    wenn ein Track selektiert ist, den Ersten
      //       Part des Tracks editieren, die restlichen sind
      //       'ghostparts'
      //    wenn mehrere Parts selektiert sind, dann Ersten
      //       editieren, die restlichen sind 'ghostparts'
      //

      // markierte Parts sammeln
      for (ciTrack t = _tracks.begin(); t != _tracks.end(); ++t) {
            WaveTrack* track = dynamic_cast<WaveTrack*>(*t);
            if (track == 0)
                  continue;
            PartList* pl = track->parts();
            for (iPart p = pl->begin(); p != pl->end(); ++p) {
                  if (p->second->selected()) {
                        parts->add(p->second);
                        }
                  }
            }
      // wenn keine Parts selektiert, dann markierten Track suchen
      // und alle Parts dieses Tracks zusammensuchen

      if (parts->empty()) {
            for (ciTrack t = _tracks.begin(); t != _tracks.end(); ++t) {
                  if ((*t)->selected()) {
                        WaveTrack* track =  dynamic_cast<WaveTrack*>(*t);
                        if (track == 0)
                              continue;
                        PartList* pl = track->parts();
                        for (iPart p = pl->begin(); p != pl->end(); ++p)
                              parts->add(p->second);
                        break;
                        }
                  }
            }
      return parts;
      }

void Song::setMType(MType t)
      {
//   printf("set MType %d\n", t);
      _mtype = t;
      }

//---------------------------------------------------------
//   beat
//---------------------------------------------------------

void Song::beat(int tick)
      {
      if (midiThread->isPlaying())
            setPos(0, tick, true, false, true);
      emit heartBeat();
      }

//---------------------------------------------------------
//   setLen
//---------------------------------------------------------

void Song::setLen(int l)
      {
      _len = l;
      update();
      }

//---------------------------------------------------------
//   addMarker
//---------------------------------------------------------

Marker* Song::addMarker(const QString& s, int t, bool lck)
      {
      Marker* marker = _markerList->add(s, t, lck);
      emit markerChanged(MARKER_ADD);
      return marker;
      }

//---------------------------------------------------------
//   removeMarker
//---------------------------------------------------------

void Song::removeMarker(Marker* marker)
      {
      _markerList->remove(marker);
      emit markerChanged(MARKER_REMOVE);
      }

Marker* Song::setMarkerName(Marker* m, const QString& s)
      {
      m->setName(s);
      emit markerChanged(MARKER_NAME);
      return m;
      }

Marker* Song::setMarkerTick(Marker* m, int t)
      {
      Marker mm(*m);
      _markerList->remove(m);
      mm.setPosTick(t);
      m = _markerList->add(mm);
      emit markerChanged(MARKER_TICK);
      return m;
      }

Marker* Song::setMarkerLock(Marker* m, bool f)
      {
      m->setType(f ? Pos::TIME : Pos::TICKS);
      emit markerChanged(MARKER_LOCK);
      return m;
      }

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

void Song::setRecordFlag(Track* track, bool val)
      {
      if (track->type() == Track::WAVE) {
            WaveTrack* audioTrack = (WaveTrack*)track;
            audioTrack->setRecordFlag1(val);
            audio->msgSetRecord(audioTrack, val);
            }
      else {
            track->setRecordFlag1(val);
            track->setRecordFlag2(val);
            }
      emit recordFlagChanged(track);
      }

//---------------------------------------------------------
//   rescanAlsaPorts
//---------------------------------------------------------

void Song::rescanAlsaPorts()
      {
      emit midiPortsChanged();
      }

//---------------------------------------------------------
//   endMsgCmd
//---------------------------------------------------------

void Song::endMsgCmd()
      {
      if (updateFlags) {
            redoList->clear();
            undoAction->setEnabled(true);
            redoAction->setEnabled(false);
            emit songChanged(updateFlags);
            }
      }

//---------------------------------------------------------
//   updateAudioMixer
//---------------------------------------------------------

void Song::updateAudioMixer()
      {
      if (audioMixer)
            audioMixer->updateMixer();
      }

//---------------------------------------------------------
//   undo
//---------------------------------------------------------

void Song::undo()
      {
      updateFlags = 0;
      midiThread->undo();
      redoAction->setEnabled(true);
      undoAction->setEnabled(!undoList->empty());
      if (updateFlags & SC_AUDIO_MIXER) {
            updateFlags &= ~SC_AUDIO_MIXER;
            updateAudioMixer();
            }
      emit songChanged(updateFlags);
      }

//---------------------------------------------------------
//   redo
//---------------------------------------------------------

void Song::redo()
      {
      updateFlags = 0;
      midiThread->redo();
      undoAction->setEnabled(true);
      redoAction->setEnabled(!redoList->empty());
      if (updateFlags & SC_AUDIO_MIXER) {
            updateFlags &= ~SC_AUDIO_MIXER;
            updateAudioMixer();
            }
      emit songChanged(updateFlags);
      }

//---------------------------------------------------------
//   processAudioMsg
//    executed in midiThread context
//---------------------------------------------------------

void Song::processAudioMsg(const AudioMsg* msg)
      {
      switch(msg->id) {
            case AUDIO_ADD_PART:
                  addPart(msg->spart);
                  undoOp(UndoOp::AddPart, msg->spart);
                  updateFlags = SC_PART_INSERTED;
                  break;
            case AUDIO_REMOVE_PART:
                  {
                  Part* part = msg->spart;
                  undoOp(UndoOp::DeletePart, part);
                  part->track()->parts()->remove(part);
                  part->events()->incARef(-1);
                  updateFlags = SC_PART_REMOVED;
                  }
                  break;
            case AUDIO_CHANGE_PART:
                  changePart(msg->spart, msg->dpart);
                  undoOp(UndoOp::ModifyPart, msg->spart, msg->dpart);
                  msg->spart->events()->incARef(-1);
                  updateFlags = SC_PART_MODIFIED;
                  break;
            case AUDIO_REMOVE_TRACK:
                  {
                  WaveTrack* t = msg->track;
                  int idx = _tracks.index(t);
                  undoOp(UndoOp::DeleteTrack, idx, t);
                  _tracks.erase(t);
                  }
                  break;
            case AUDIO_UNDO:
                  doUndo();
                  break;
            case AUDIO_REDO:
                  doRedo();
                  break;
            }
      }

//---------------------------------------------------------
//   processMidiMsg
//    executed in midiThread context
//---------------------------------------------------------

void Song::processMidiMsg(const MidiMsg* msg)
      {
      switch(msg->id) {
            case SEQM_ADD_TRACK:
                  addTrack((Track*)(msg->p1));
                  break;
            case SEQM_REMOVE_TRACK:
                  {
                  Track* t = (Track*)(msg->p1);
                  if (t->type() == Track::WAVE)
                        audio->msgRemoveTrack((WaveTrack*)t);
                  else {
                        int idx = _tracks.index(t);
                        undoOp(UndoOp::DeleteTrack, idx, t);
                        _tracks.erase(t);
                        }
                  }
                  break;
            case SEQM_CHANGE_TRACK:
                  changeTrack((Track*)msg->p1, (Track*)msg->p2);
                  break;
            case SEQM_MOVE_TRACK:
                  if (msg->a > msg->b) {
                        for (int i = msg->a; i > msg->b; --i) {
                              swapTracks(i, i-1);
                              }
                        }
                  else {
                        for (int i = msg->a; i < msg->b; ++i) {
                              swapTracks(i, i+1);
                              }
                        }
                  updateFlags = SC_TRACK_MODIFIED;
                  break;
            case SEQM_ADD_PART:
                  addPart((Part*)msg->p1);
                  undoOp(UndoOp::AddPart, (Part*)msg->p1);
                  updateFlags = SC_PART_INSERTED;
                  break;
            case SEQM_REMOVE_PART:
                  {
                  Part* part = (Part*)msg->p1;
                  undoOp(UndoOp::DeletePart, part);
                  part->track()->parts()->remove(part);
                  part->events()->incARef(-1);
                  updateFlags = SC_PART_REMOVED;
                  }
                  break;
            case SEQM_CHANGE_PART:
                  changePart((Part*)msg->p1, (Part*)msg->p2);
                  undoOp(UndoOp::ModifyPart, (Part*)msg->p1, (Part*)msg->p2);
                  ((Part*)msg->p1)->events()->incARef(-1);
                  updateFlags = SC_PART_MODIFIED;
                  break;
            case SEQM_ADD_EVENT:
                  undoOp(UndoOp::AddEvent, 0, (Event*)msg->p1,
                     (Part*)msg->p2);
                  addEvent((MidiEvent*)msg->p1, (MidiPart*)msg->p2);
                  updateFlags = SC_EVENT_INSERTED;
                  break;
            case SEQM_REMOVE_EVENT:
                  {
                  MidiEvent* event = (MidiEvent*)msg->p1;
                  MidiPart* part   = (MidiPart*)msg->p2;
                  undoOp(UndoOp::DeleteEvent, (Event*)0, (Event*)event, (Part*)part);
                  deleteEvent(event, part);
                  updateFlags = SC_EVENT_REMOVED;
                  }
                  break;
            case SEQM_CHANGE_EVENT:
                  changeEvent((MidiEvent*)msg->p1, (MidiEvent*)msg->p2, (MidiPart*)msg->p3);
                  undoOp(UndoOp::ModifyEvent, (Event*)msg->p2, (Event*)msg->p1, (Part*)msg->p3);
                  updateFlags = SC_EVENT_MODIFIED;
                  break;

            case SEQM_ADD_TEMPO:
                  undoOp(UndoOp::AddTempo, msg->a, msg->b);
                  tempomap.addTempo(msg->a, msg->b);
                  updateFlags = SC_TEMPO;
                  break;

            case SEQM_SET_TEMPO:
                  undoOp(UndoOp::AddTempo, msg->a, msg->b);
                  tempomap.setTempo(msg->a, msg->b);
                  updateFlags = SC_TEMPO;
                  break;

            case SEQM_SET_GLOBAL_TEMPO:
                  tempomap.setGlobalTempo(msg->a);
                  break;

            case SEQM_REMOVE_TEMPO:
                  undoOp(UndoOp::DeleteTempo, msg->a, msg->b);
                  tempomap.delTempo(msg->a);
                  updateFlags = SC_TEMPO;
                  break;

            case SEQM_ADD_SIG:
                  undoOp(UndoOp::AddSig, msg->a, msg->b, msg->c);
                  sigmap.add(msg->a, msg->b, msg->c);
                  updateFlags = SC_SIG;
                  break;

            case SEQM_UNDO:
                  audio->msgUndo();
                  break;
            case SEQM_REDO:
                  audio->msgRedo();
                  break;
            default:
printf("unknown seq message %d\n", msg->id);
                  break;
            }
      }

//---------------------------------------------------------
//   panic
//---------------------------------------------------------

void Song::panic()
      {
      midiThread->msgPanic();
      }

//---------------------------------------------------------
//   masterVolChanged
//---------------------------------------------------------

void Song::masterVolChanged(int a)
      {
      midiThread->masterVolChanged(a);
      }

//---------------------------------------------------------
//   ctrlChanged
//---------------------------------------------------------

void Song::ctrlChanged(int a, int b, int c, int d)
      {
      midiThread->ctrlChanged(a, b, c, d);
      }

//---------------------------------------------------------
//   appearanceChanged
//---------------------------------------------------------

void Song::appearanceChanged()
      {
      update();
      }

//---------------------------------------------------------
//   clear
//    signal - emit signals for changes if true
//    called from constructor as clear(false) and
//    from MusE::clearSong() as clear(true)
//---------------------------------------------------------

void Song::clear(bool signal)
      {
      bounceTrack    = 0;
      _name          = "";
      _komponist1    = "";
      _komponist2    = "";
      _tracks.clearDelete();
      tempomap.clear();
      sigmap.clear();
      undoList->clear();
      redoList->clear();
      _markerList->clear();
      pos[0]         = 0;
      pos[1]         = 0;
      pos[2]         = 0;
      _showPageNo    = true;
      _showMeasureNo = true;
      _showTrackname = true;
      _masterFlag    = true;
      loopFlag       = false;
      loopFlag       = false;
      punchinFlag    = false;
      punchoutFlag   = false;
      playFlag       = false;
      recordFlag     = false;
      soloFlag       = false;
      // seq
      _mtype         = MT_UNKNOWN;
      _karaokeFlag   = false;
      _karaoke->clear();
      _recMode       = REC_OVERDUP;
      _cycleMode     = CYCLE_NORMAL;
      _click         = false;
      _quantize      = false;
      _len           = 0;           // song len in ticks
      _follow        = JUMP;
      // _tempo         = 500000;      // default tempo 120
      dirty          = false;
      initDrumMap();
      if (signal) {
            emit loopChanged(false);
            recordChanged(false);
            }
      }


