diff options
| author | Werner Schweer <ws.seh.de> | 2006-11-16 15:20:28 +0000 | 
|---|---|---|
| committer | Werner Schweer <ws.seh.de> | 2006-11-16 15:20:28 +0000 | 
| commit | 4c7739dc6a763f0c1ce367eb28c5dc09cf726d32 (patch) | |
| tree | 17f6533551c522d6437a9e9c3bbd107a5a25d291 | |
| parent | 2c71a89579e38cd9ff527c33b9ada07fc471b7fd (diff) | |
updates
32 files changed, 940 insertions, 725 deletions
diff --git a/muse/ChangeLog b/muse/ChangeLog index c2c1f075..a4688528 100644 --- a/muse/ChangeLog +++ b/muse/ChangeLog @@ -1,3 +1,5 @@ +16.11 (ws) +      - implemented controller editor part of new list editor  12.11 (ws)        - the internationalization files (*.qm) are now part of the          MusE image via the qt resource system. Installing them is not diff --git a/muse/doc/dimpl/dimpl.tex b/muse/doc/dimpl/dimpl.tex index 31b28470..d8c91743 100644 --- a/muse/doc/dimpl/dimpl.tex +++ b/muse/doc/dimpl/dimpl.tex @@ -174,6 +174,75 @@  %%=======================================================================  %% +\chapter{Process Structure} + +\chapter{Song} +   \section{Multithreading} + +   \section{Editing} +      \subsection{Realtime Editing} +         \M\ allows for realtime editing. That means that you can edit +         a song while the sequencer is running. +         Two threads want to access data in the main {\tt Song()} class: +         the GUI thread and the realtime sequencer thread. + +         This are the rules how \M\ handles {\tt Song()} data accesses: + +         \startitemize[packed,broad] +            \item the GUI thread can only read {\tt Song()} data +            \item the GUI thread can request the sequencer thread to +               modify Song data +            \item the sequencer thread can modify {\tt Song()} data, but only +               on request of the GUI thread +         \stopitemize + +         Interprocess communication is done via message passing.  +         Communication from the gui thread to the sequencer thread is +         done in the following steps: + +         \startitemize[packed,broad] +            \item the GUI sends a Message to the sequencer thread +                  and waits for an answer +            \item once the sequencer thread is running (the JACK +              client {\tt process()} call) it processes the message and +              sends back an answer to the GUI process. +            \item the GUI process reads the answer and continues +         \stopitemize +             +         This scheme stops the GUI process in a defined state while the +         realtime process can safely manipulate the {\tt Song()} data. + +      \subsection{User Transaction} +    +         A user transaction is a sequence of actions manipulating the +         main {\Song()} structure. Each action involves sending a +         message to the realtime thread. This means that every action +         stops the GUI thread for at most one JACK cycle. + +         If a transaction consists of lot of actions then this scheme +         would be too slow. For such cases the sequencer thread is switched +         to "idle" mode, which ensures it is not touching the {\tt Song()} +         data anymore. Then safe manipulation of the data is possible. + +      \subsection{Naming Conventions} + +         There are several methods involved in an edit transaction; +         example for adding a part: +          +         \startitemize[packed,broad] +               \item {\tt cmdAddPart()} +                     This implements a complete undoable user transaction +                     and is called from the gui thread +               \item {\tt addPart()} +                     This method is also called from the gui thread. It +                     implements part of a user transaction. It is not +                     surrounded by {\tt startUndo() endUndo()}. +               \item the realtime part (actually removing the {\tt Part} from the +                     PartList) is done inline during processing of  +                     messages in the sequencer thread. +         \stopitemize +          +  \chapter{File Formats}        \section{Instrument Definition Files} diff --git a/muse/muse/CMakeLists.txt b/muse/muse/CMakeLists.txt index 8de90026..e61c4a8f 100644 --- a/muse/muse/CMakeLists.txt +++ b/muse/muse/CMakeLists.txt @@ -64,6 +64,7 @@ QT4_WRAP_CPP ( muse_moc_headers  	cobject.h        transpose.h        track.h +      miditrackbase.h        midisynti.h        miditrack.h        wavetrack.h @@ -138,12 +139,14 @@ add_executable ( muse        midiplugin.cpp        muse.cpp        song.cpp +      songpart.cpp        transport.cpp        conf.cpp        editor.cpp  	cobject.cpp        transpose.cpp        track.cpp +      miditrackbase.cpp        midisynti.cpp        miditrack.cpp        wavetrack.cpp diff --git a/muse/muse/arranger/canvas.cpp b/muse/muse/arranger/canvas.cpp index 85e2305e..4ee07a65 100644 --- a/muse/muse/arranger/canvas.cpp +++ b/muse/muse/arranger/canvas.cpp @@ -471,7 +471,7 @@ void PartCanvas::contextMenu(const QPoint& pos)                                renamePart(part);                                break;                          case 1: -                              audio->msgRemovePart(part, true); +                              song->cmdRemovePart(part);                                track->partListChanged();                                break;                          case 2: @@ -610,7 +610,7 @@ void PartCanvas::mousePress(QMouseEvent* me)                    break;              case RubberTool:              	if (part) -                  	audio->msgRemovePart(part); +                  	song->cmdRemovePart(part);                    break;              case GlueTool:                    if (part) @@ -811,7 +811,7 @@ void PartCanvas::mouseRelease(QMouseEvent* me)                    Pos p2 = pix2pos(drag.x() + drag.width()).snaped(raster());                    part->setPos(p1);                    part->setLenTick(p2.tick() - p1.tick()); -                  song->addPart(part); +                  song->cmdAddPart(part);                    }              else                    widget()->update(); diff --git a/muse/muse/audio.cpp b/muse/muse/audio.cpp index aa58b30d..543dcdf4 100644 --- a/muse/muse/audio.cpp +++ b/muse/muse/audio.cpp @@ -588,27 +588,6 @@ void Audio::processMsg()                    midiSeq->initRealtimeTimer();                    break; -            case SEQM_ADD_TRACK: -                  song->insertTrack2(msg->track); -                  midiSeq->updatePollFd(); -                  break; - -            case SEQM_REMOVE_TRACK: -                  song->removeTrack2(msg->track); -                  midiSeq->updatePollFd(); -                  break; -            case SEQM_ADD_PART: -                  song->cmdAddPart((Part*)msg->p1); -                  break; -            case SEQM_REMOVE_PART: -                  song->cmdRemovePart((Part*)msg->p1); -                  break; -            case SEQM_CHANGE_PART: -                  song->cmdChangePart((Part*)msg->p1, (Part*)msg->p2); -                  break; -            case SEQM_MOVE_TRACK: -                  song->moveTrack((Track*)(msg->p1), (Track*)(msg->p2)); -                  break;              case AUDIO_ADDMIDIPLUGIN:                    ((MidiTrackBase*)msg->track)->addPlugin(msg->mplugin, msg->ival);                    break; diff --git a/muse/muse/audio.h b/muse/muse/audio.h index 11f30d5e..050757fd 100644 --- a/muse/muse/audio.h +++ b/muse/muse/audio.h @@ -215,8 +215,8 @@ class Audio {        void msgRemoveTrack(Track*);        void msgRemoveTracks();        void msgMoveTrack(Track*, Track*); -      void msgAddPart(Part*, bool u = true); -      void msgRemovePart(Part*, bool u = true); +//      void msgAddPart(Part*, bool u = true); +//      void msgRemovePart(Part*, bool u = true);        void msgChangePart(Part* oldPart, Part* newPart, bool u = true);        void msgAddEvent(const Event&, Part*, bool u = true);        void msgDeleteEvent(const Event&, Part*, bool u = true); diff --git a/muse/muse/importmidi.cpp b/muse/muse/importmidi.cpp index 83cae602..41a8dfd2 100644 --- a/muse/muse/importmidi.cpp +++ b/muse/muse/importmidi.cpp @@ -315,7 +315,7 @@ void MusE::addMidiFile(const QString name)        int division           = mf.division();        MidiOutPort* outPort = 0; -      MidiInPort* inPort = 0; +//      MidiInPort* inPort = 0;        if (song->midiOutPorts()->empty()) {              outPort = new MidiOutPort(); diff --git a/muse/muse/liste/ctrllistedit.cpp b/muse/muse/liste/ctrllistedit.cpp index 472e02f4..9c4a05eb 100644 --- a/muse/muse/liste/ctrllistedit.cpp +++ b/muse/muse/liste/ctrllistedit.cpp @@ -46,13 +46,17 @@ CtrlListEditor::CtrlListEditor(ListEdit* e, QWidget* parent)        le.maxValue->setSingleStep(1.0);        le.defaultValue->setSingleStep(1.0); -      le.ctrlList->setColumnWidth(TICK_COL, 60); -      le.ctrlList->setColumnWidth(TIME_COL, 120); +      QFontMetrics fm(le.ctrlList->font()); +      int zW = fm.width("0"); +      le.ctrlList->setColumnWidth(TICK_COL, zW * 8); +      le.ctrlList->setColumnWidth(TIME_COL, zW * 14);        MidiTimeDelegate* midiTimeDelegate = new MidiTimeDelegate(this);        le.ctrlList->setItemDelegate(midiTimeDelegate);        track = 0;        connect(le.ctrlList, SIGNAL(itemActivated(QTreeWidgetItem*, int)), +         SLOT(itemActivated(QTreeWidgetItem*,int))); +      connect(le.ctrlList, SIGNAL(itemDoubleClicked(QTreeWidgetItem*, int)),           SLOT(itemDoubleClicked(QTreeWidgetItem*,int)));        connect(le.ctrlList, SIGNAL(itemChanged(QTreeWidgetItem*, int)),           SLOT(itemChanged(QTreeWidgetItem*, int))); @@ -136,8 +140,7 @@ void CtrlListEditor::updateList()              QTreeWidgetItem* item = new QTreeWidgetItem;              item->setData(TICK_COL, Qt::TextAlignmentRole, int(Qt::AlignRight | Qt::AlignVCenter));              item->setData(TIME_COL, Qt::TextAlignmentRole, int(Qt::AlignRight | Qt::AlignVCenter)); -            item->setData(VAL_COL,  Qt::TextAlignmentRole, int(Qt::AlignRight | Qt::AlignVCenter)); -//            item->setItemDelegate(TIME_COL, midiTimeDelegate); +            item->setData(VAL_COL,  Qt::TextAlignmentRole, int(Qt::AlignHCenter | Qt::AlignVCenter));              item->setData(TICK_COL, Qt::DisplayRole, i.key());              item->setData(TIME_COL, Qt::DisplayRole, i.key()); @@ -165,10 +168,10 @@ void CtrlListEditor::controllerChanged(int id)        }  //--------------------------------------------------------- -//   itemDoubleClicked +//   itemActivated  //--------------------------------------------------------- -void CtrlListEditor::itemDoubleClicked(QTreeWidgetItem* item, int column) +void CtrlListEditor::itemActivated(QTreeWidgetItem* item, int column)        {        le.ctrlList->openPersistentEditor(item, column);        } @@ -179,11 +182,6 @@ void CtrlListEditor::itemDoubleClicked(QTreeWidgetItem* item, int column)  void CtrlListEditor::itemChanged(QTreeWidgetItem* item, int column)        { -      if (column != VAL_COL) { -            printf("time change not implemented\n"); -            return; -            } -              CVal val;        if (c->type() & Ctrl::INT) {              val.i = item->data(VAL_COL, Qt::DisplayRole).toInt(); @@ -217,7 +215,29 @@ void CtrlListEditor::itemChanged(QTreeWidgetItem* item, int column)        le.ctrlList->closePersistentEditor(item, TIME_COL);        le.ctrlList->closePersistentEditor(item, VAL_COL);        updateListDisabled = true; -      song->addControllerVal(track, c, listEdit->pos(), val); +      switch(column) { +            case TICK_COL: +                  { +                  int otick = item->data(TIME_COL, Qt::DisplayRole).toInt(); +                  int tick = item->data(TICK_COL, Qt::DisplayRole).toInt(); +                  item->setData(TIME_COL, Qt::DisplayRole, tick); +                  song->removeControllerVal(track, c->id(), otick); +                  song->addControllerVal(track, c, tick, val); +                  } +                  break; +            case TIME_COL: +                  { +                  int otick = item->data(TICK_COL, Qt::DisplayRole).toInt(); +                  int tick = item->data(TIME_COL, Qt::DisplayRole).toInt(); +                  item->setData(TICK_COL, Qt::DisplayRole, tick); +                  song->removeControllerVal(track, c->id(), otick); +                  song->addControllerVal(track, c, tick, val); +                  } +                  break; +            case VAL_COL: +                  song->addControllerVal(track, c, listEdit->pos(), val); +                  break; +            }        updateListDisabled = false;        } @@ -350,14 +370,31 @@ MidiTimeDelegate::MidiTimeDelegate(QObject* parent)  //   createEditor  //--------------------------------------------------------- -QWidget* MidiTimeDelegate::createEditor(QWidget* parent,  +QWidget* MidiTimeDelegate::createEditor(QWidget* pw,     const QStyleOptionViewItem& option, const QModelIndex& index) const        { -      if (index.column() != CtrlListEditor::TIME_COL) -            return QItemDelegate::createEditor(parent, option, index); - -      Awl::PosEdit* pe = new Awl::PosEdit(parent); -      return pe; +      switch(index.column()) { +            case CtrlListEditor::TICK_COL: +                  break; +            case CtrlListEditor::TIME_COL: +                  return new Awl::PosEdit(pw); +            case CtrlListEditor::VAL_COL: +                  { +                  CtrlListEditor* ce = static_cast<CtrlListEditor*>(parent()); +                  Ctrl* c = ce->ctrl(); +                  if (c->type() & Ctrl::INT) { +                        QSpinBox* w = new QSpinBox(pw); +                        w->setRange(c->minVal().i, c->maxVal().i); +                        w->installEventFilter(const_cast<MidiTimeDelegate*>(this)); +                        return w; +                        } +                  QDoubleSpinBox* w = new QDoubleSpinBox(pw); +                  w->setRange(c->minVal().f, c->maxVal().f); +                  w->installEventFilter(const_cast<MidiTimeDelegate*>(this)); +                  return w; +                  } +            } +      return QItemDelegate::createEditor(pw, option, index);        }  //--------------------------------------------------------- @@ -367,12 +404,31 @@ QWidget* MidiTimeDelegate::createEditor(QWidget* parent,  void MidiTimeDelegate::setEditorData(QWidget* editor,      const QModelIndex& index) const        { -      if (index.column() != CtrlListEditor::TIME_COL) { -            QItemDelegate::setEditorData(editor, index); -            return; +      switch(index.column()) { +            case CtrlListEditor::TICK_COL: +                  break; +            case CtrlListEditor::TIME_COL: +                  { +                  Awl::PosEdit* pe = static_cast<Awl::PosEdit*>(editor); +                  pe->setValue(AL::Pos(index.data().toInt())); +                  } +                  return; +            case CtrlListEditor::VAL_COL: +                  { +                  CtrlListEditor* ce = static_cast<CtrlListEditor*>(parent()); +                  Ctrl* c = ce->ctrl(); +                  if (c->type() & Ctrl::INT) { +                        QSpinBox* w = static_cast<QSpinBox*>(editor); +                        w->setValue(index.data().toInt()); +                        } +                  else { +                        QDoubleSpinBox* w = static_cast<QDoubleSpinBox*>(editor); +                        w->setValue(index.data().toDouble()); +                        } +                  } +                  return;              } -      Awl::PosEdit* pe = (Awl::PosEdit*)editor; -      pe->setValue(AL::Pos(index.data().toInt())); +      QItemDelegate::setEditorData(editor, index);        }  //--------------------------------------------------------- @@ -382,12 +438,31 @@ void MidiTimeDelegate::setEditorData(QWidget* editor,  void MidiTimeDelegate::setModelData(QWidget* editor, QAbstractItemModel* model,           const QModelIndex& index) const        { -      if (index.column() != CtrlListEditor::TIME_COL) { -            QItemDelegate::setModelData(editor, model, index); -            return; +      switch(index.column()) { +            case CtrlListEditor::TICK_COL: +                  break; +            case CtrlListEditor::TIME_COL: +                  { +                  Awl::PosEdit* pe = static_cast<Awl::PosEdit*>(editor); +                  model->setData(index, pe->pos().tick(), Qt::DisplayRole); +                  } +                  return; +            case CtrlListEditor::VAL_COL: +                  { +                  CtrlListEditor* ce = static_cast<CtrlListEditor*>(parent()); +                  Ctrl* c = ce->ctrl(); +                  if (c->type() & Ctrl::INT) { +                        QSpinBox* w = static_cast<QSpinBox*>(editor); +                        model->setData(index, w->value(), Qt::DisplayRole); +                        } +                  else { +                        QDoubleSpinBox* w = static_cast<QDoubleSpinBox*>(editor); +                        model->setData(index, w->value(), Qt::DisplayRole); +                        } +                  } +                  break;              } -      Awl::PosEdit* pe = (Awl::PosEdit*)editor; -      model->setData(index, pe->pos().tick(), Qt::DisplayRole); +      QItemDelegate::setModelData(editor, model, index);        }  //--------------------------------------------------------- @@ -431,3 +506,13 @@ void MidiTimeDelegate::paint(QPainter* painter,        painter->restore();        } +//--------------------------------------------------------- +//   itemDoubleClicked +//--------------------------------------------------------- + +void CtrlListEditor::itemDoubleClicked(QTreeWidgetItem* item, int column) +      { +      printf("double clicked\n"); +      } + + diff --git a/muse/muse/liste/ctrllistedit.h b/muse/muse/liste/ctrllistedit.h index 842ba7eb..a6384321 100644 --- a/muse/muse/liste/ctrllistedit.h +++ b/muse/muse/liste/ctrllistedit.h @@ -30,6 +30,7 @@  //---------------------------------------------------------  class MidiTimeDelegate : public QItemDelegate { +      Q_OBJECT        virtual QWidget* createEditor(QWidget* parent, const QStyleOptionViewItem&,           const QModelIndex& index) const; @@ -73,6 +74,7 @@ class CtrlListEditor : public ListWidget {     private slots:        void controllerChanged(int id); +      void itemActivated(QTreeWidgetItem*,int);        void itemDoubleClicked(QTreeWidgetItem*,int);        void itemChanged(QTreeWidgetItem*,int);        void currentItemChanged(QTreeWidgetItem*, QTreeWidgetItem*); @@ -87,6 +89,7 @@ class CtrlListEditor : public ListWidget {        CtrlListEditor(ListEdit*, QWidget* parent = 0);        virtual void setup(const ListType&);        void sendEscape(); +      Ctrl* ctrl() const { return c; }        enum { TICK_COL, TIME_COL, VAL_COL };        }; diff --git a/muse/muse/liste/listedit.cpp b/muse/muse/liste/listedit.cpp index 09e2ed86..df78dfe7 100644 --- a/muse/muse/liste/listedit.cpp +++ b/muse/muse/liste/listedit.cpp @@ -67,8 +67,9 @@ ListEdit::ListEdit(QWidget*)           SLOT(itemChanged(QTreeWidgetItem*,QTreeWidgetItem*)));        connect(list, SIGNAL(itemExpanded(QTreeWidgetItem*)), SLOT(itemExpanded(QTreeWidgetItem*)));        connect(list, SIGNAL(itemCollapsed(QTreeWidgetItem*)), SLOT(itemExpanded(QTreeWidgetItem*))); +      connect(song, SIGNAL(songChanged(int)), SLOT(songChanged(int)));        list->resizeColumnToContents(0); -      resize(900, 300); +      resize(900, 400);        }  //--------------------------------------------------------- @@ -153,6 +154,19 @@ void ListEdit::buildList()        }  //--------------------------------------------------------- +//   songChanged +//--------------------------------------------------------- + +void ListEdit::songChanged(int flags) +      { +      if (flags & (SC_TRACK_INSERTED | SC_TRACK_REMOVED | SC_PART_INSERTED +         | SC_PART_REMOVED)) { +            buildList(); +            selectItem(); +            }       +      } + +//---------------------------------------------------------  //   findItem  //--------------------------------------------------------- @@ -189,12 +203,11 @@ void ListEdit::selectItem(const AL::Pos& p, Track* track, Part* part, Ctrl* ctrl        lt.track = track;        lt.part  = part;        lt.ctrl  = ctrl; -      selectItem(lt); +      selectItem();        } -void ListEdit::selectItem(const ListType& l) +void ListEdit::selectItem()        { -      lt = l;        stack->setCurrentWidget(ctrlPanel);        buildList();        for (int i = 0;; ++i) { diff --git a/muse/muse/liste/listedit.h b/muse/muse/liste/listedit.h index 2c33cdab..66179cd0 100644 --- a/muse/muse/liste/listedit.h +++ b/muse/muse/liste/listedit.h @@ -75,11 +75,12 @@ class ListEdit : public TopWin {        void buildList();        QTreeWidgetItem* findItem(const ListType& lt, QTreeWidgetItem* item); -      void selectItem(const ListType& lt); +      void selectItem();     private slots:        void itemChanged(QTreeWidgetItem*, QTreeWidgetItem*);        void itemExpanded(QTreeWidgetItem*); +      void songChanged(int);     public:        ListEdit(QWidget* parent = 0); diff --git a/muse/muse/midi.cpp b/muse/muse/midi.cpp index 79eff684..37b5e5b4 100644 --- a/muse/muse/midi.cpp +++ b/muse/muse/midi.cpp @@ -568,7 +568,7 @@ void buildMidiEventList(EventList* del, const MidiEventList* el, MidiTrack* trac                    CVal val;                    val.i = ev.dataB(); -                  bool found = false; +//                  bool found = false;                    Ctrl* c = track->getController(id);                    if (c)                          c->add(tick, val); diff --git a/muse/muse/midiedit/drumedit.cpp b/muse/muse/midiedit/drumedit.cpp index 61537069..7d783245 100644 --- a/muse/muse/midiedit/drumedit.cpp +++ b/muse/muse/midiedit/drumedit.cpp @@ -123,7 +123,7 @@ DrumEdit::DrumEdit(PartList* pl, bool init)        QToolBar* transport = addToolBar(tr("Transport"));        muse->setupTransportToolbar(transport); -      // dont´how pitch value in toolbar +      // dont´ow pitch value in toolbar        addToolBarBreak();        toolbar = new Toolbar1(initRaster, initQuant, false);        addToolBar(toolbar); @@ -341,12 +341,14 @@ void DrumEdit::configChanged()        initShortcuts();        } +#if 0  static int rasterTable[] = {        //-9----8-  7    6     5     4    3(1/4)     2   1        4,  8, 16, 32,  64, 128, 256,  512, 1024,  // triple        6, 12, 24, 48,  96, 192, 384,  768, 1536,        9, 18, 36, 72, 144, 288, 576, 1152, 2304   // dot        }; +#endif  //---------------------------------------------------------  //   keyPressEvent diff --git a/muse/muse/midiinport.h b/muse/muse/midiinport.h index 4a7b8bf1..8589cf41 100644 --- a/muse/muse/midiinport.h +++ b/muse/muse/midiinport.h @@ -21,7 +21,7 @@  #ifndef __MIDIINPORT_H__  #define __MIDIINPORT_H__ -#include "track.h" +#include "miditrackbase.h"  #include "midievent.h"  static const int RECORD_FIFO_SIZE = 512; diff --git a/muse/muse/midiout.cpp b/muse/muse/midiout.cpp index 6ab98da7..31ed904d 100644 --- a/muse/muse/midiout.cpp +++ b/muse/muse/midiout.cpp @@ -20,7 +20,8 @@  #include "midiout.h"  #include "midictrl.h" -#include "track.h" +#include "miditrackbase.h" +#include "al/al.h"  #include "al/tempo.h"  #include "event.h"  #include "sync.h" diff --git a/muse/muse/midiout.h b/muse/muse/midiout.h index 9df88569..75e9cbd6 100644 --- a/muse/muse/midiout.h +++ b/muse/muse/midiout.h @@ -21,9 +21,6 @@  #ifndef __MIDIOUT_H__  #define __MIDIOUT_H__ -#include "al/al.h" -#include "globaldefs.h" -#include "midievent.h"  #include "midififo.h"  class Track; diff --git a/muse/muse/midioutport.h b/muse/muse/midioutport.h index aa056db5..e77af2e5 100644 --- a/muse/muse/midioutport.h +++ b/muse/muse/midioutport.h @@ -21,7 +21,7 @@  #ifndef __MIDIOUTPORT_H__  #define __MIDIOUTPORT_H__ -#include "track.h" +#include "miditrackbase.h"  #include "midiout.h"  //--------------------------------------------------------- diff --git a/muse/muse/midisynti.h b/muse/muse/midisynti.h index c74a0eb7..89c77764 100644 --- a/muse/muse/midisynti.h +++ b/muse/muse/midisynti.h @@ -21,7 +21,7 @@  #ifndef __MIDISYNTH_H__  #define __MIDISYNTH_H__ -#include "track.h" +#include "miditrackbase.h"  //---------------------------------------------------------  //   MidiSynti diff --git a/muse/muse/miditrack.cpp b/muse/muse/miditrack.cpp index b7e3a53d..3f380ba4 100644 --- a/muse/muse/miditrack.cpp +++ b/muse/muse/miditrack.cpp @@ -98,7 +98,7 @@ void MidiTrack::write(Xml& xml) const        xml.intTag("compression", _compression);        xml.intTag("useDrumMap", _useDrumMap); -      const PartList* pl = cparts(); +      const PartList* pl = parts();        for (ciPart p = pl->begin(); p != pl->end(); ++p)              p->second->write(xml);        xml.etag("miditrack"); @@ -195,7 +195,7 @@ void MidiTrack::startRecording()              recordPart->setTick(startTick);              recordPart->setLenTick(endTick - startTick);              recordPart->setName(name()); -            audio->msgAddPart(recordPart, false); +            song->addPart(recordPart);              partCreated = true;              }        } @@ -604,7 +604,7 @@ void MidiTrack::processMidi(unsigned from, unsigned to, unsigned, unsigned)  //    from/to - midi ticks  //--------------------------------------------------------- -void MidiTrack::getEvents(unsigned from, unsigned to, int, MidiEventList* dst) +void MidiTrack::getEvents(unsigned /*from*/, unsigned /*to*/, int, MidiEventList* dst)        {        for (iMidiEvent i = schedEvents.begin(); i != schedEvents.end(); ++i) {              dst->insert(*i); diff --git a/muse/muse/miditrack.h b/muse/muse/miditrack.h index d8b0d231..6424c1d8 100644 --- a/muse/muse/miditrack.h +++ b/muse/muse/miditrack.h @@ -21,7 +21,7 @@  #ifndef __MIDITRACK_H__  #define __MIDITRACK_H__ -#include "track.h" +#include "miditrackbase.h"  #include "midififo.h"  class Part; diff --git a/muse/muse/miditrackbase.cpp b/muse/muse/miditrackbase.cpp new file mode 100644 index 00000000..faf0ed4d --- /dev/null +++ b/muse/muse/miditrackbase.cpp @@ -0,0 +1,139 @@ +//============================================================================= +//  MusE +//  Linux Music Editor +//  $Id:$ +// +//  Copyright (C) 2002-2006 by Werner Schweer and others +// +//  This program is free software; you can redistribute it and/or modify +//  it under the terms of the GNU General Public License version 2. +// +//  This program is distributed in the hope that it will be useful, +//  but WITHOUT ANY WARRANTY; without even the implied warranty of +//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +//  GNU General Public License for more details. +// +//  You should have received a copy of the GNU General Public License +//  along with this program; if not, write to the Free Software +//  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +//============================================================================= + +#include "miditrackbase.h" +#include "midiplugin.h" + +//--------------------------------------------------------- +//   MidiTrackBase +//--------------------------------------------------------- + +MidiTrackBase::MidiTrackBase() +   : Track() +      { +      _pipeline = new MidiPipeline(); +      } + +//--------------------------------------------------------- +//   MidiTrackBase +//--------------------------------------------------------- + +MidiTrackBase::~MidiTrackBase() +      { +      foreach(MidiPluginI* plugin, *_pipeline) +            delete plugin; +      delete _pipeline; +      } + +//--------------------------------------------------------- +//   MidiTrackBase::writeProperties +//--------------------------------------------------------- + +void MidiTrackBase::writeProperties(Xml& xml) const +      { +      Track::writeProperties(xml); +      for (ciMidiPluginI ip = _pipeline->begin(); ip != _pipeline->end(); ++ip) { +            if (*ip) +                  (*ip)->writeConfiguration(xml); +            } +      } + +//--------------------------------------------------------- +//   MidiTrackBase::readProperties +//--------------------------------------------------------- + +bool MidiTrackBase::readProperties(QDomNode node) +      { +      QDomElement e = node.toElement(); +      QString tag(e.tagName()); +      if (tag == "midiPlugin") { +            MidiPluginI* pi = new MidiPluginI(this); +            if (pi->readConfiguration(node)) +                  delete pi; +            else +                  addPlugin(pi, -1); +            } +      else +            return Track::readProperties(node); +      return false; +      } + +//--------------------------------------------------------- +//   plugin +//--------------------------------------------------------- + +MidiPluginI* MidiTrackBase::plugin(int idx) const +      { +      return _pipeline->value(idx); +      } + +//--------------------------------------------------------- +//   addPlugin +//    idx    = -1     append +//    plugin = 0   remove slot +//--------------------------------------------------------- + +void MidiTrackBase::addPlugin(MidiPluginI* plugin, int idx) +      { +      if (plugin == 0) { +#if 0 +            MidiPluginI* oldPlugin = (*_pipeline)[idx]; +            if (oldPlugin) { +                  int controller = oldPlugin->plugin()->parameter(); +                  for (int i = 0; i < controller; ++i) { +                        int id = (idx + 1) * 0x1000 + i; +                        removeController(id); +                        } +                  } +#endif +            } +      if (idx == -1) +            idx = _pipeline->size(); + +      if (plugin) { +            _pipeline->insert(idx, plugin); +#if 0 +            int ncontroller = plugin->plugin()->parameter(); +            for (int i = 0; i < ncontroller; ++i) { +                  int id = (idx + 1) * 0x1000 + i; +                  QString name(plugin->getParameterName(i)); +                  double min, max; +                  plugin->range(i, &min, &max); +                  Ctrl* cl = getController(id); +                  if (cl == 0) { +                        cl = new Ctrl(id, name); +                        cl->setRange(min, max); +                        double defaultValue = plugin->defaultValue(i); +                        cl->setDefault(defaultValue); +                        cl->setCurVal(defaultValue); +                        addController(cl); +                        } +                  plugin->setParam(i, cl->schedVal().f); +                  plugin->setControllerList(cl); +                  } +#endif +            } +      else { +            _pipeline->removeAt(idx); +            } +      } + + + diff --git a/muse/muse/miditrackbase.h b/muse/muse/miditrackbase.h new file mode 100644 index 00000000..d10b2bb3 --- /dev/null +++ b/muse/muse/miditrackbase.h @@ -0,0 +1,53 @@ +//============================================================================= +//  MusE +//  Linux Music Editor +//  $Id:$ +// +//  Copyright (C) 2002-2006 by Werner Schweer and others +// +//  This program is free software; you can redistribute it and/or modify +//  it under the terms of the GNU General Public License version 2. +// +//  This program is distributed in the hope that it will be useful, +//  but WITHOUT ANY WARRANTY; without even the implied warranty of +//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +//  GNU General Public License for more details. +// +//  You should have received a copy of the GNU General Public License +//  along with this program; if not, write to the Free Software +//  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +//============================================================================= + +#ifndef __MIDITRACKBASE_H__ +#define __MIDITRACKBASE_H__ + +#include "track.h" + +//--------------------------------------------------------- +//   MidiTrackBase +//--------------------------------------------------------- + +class MidiTrackBase : public Track { +      Q_OBJECT + +      MidiPipeline* _pipeline; + +   public: +      MidiTrackBase(); +      virtual ~MidiTrackBase(); + +      bool readProperties(QDomNode); +      void writeProperties(Xml&) const; + +      MidiPipeline* pipeline()      { return _pipeline;  } +      void addPlugin(MidiPluginI* plugin, int idx); +      MidiPluginI* plugin(int idx) const; + +      virtual void processMidi(unsigned, unsigned, unsigned, unsigned) {} +      virtual void getEvents(unsigned /*from*/, unsigned /*to*/, int /*channel*/, MidiEventList* /*dst*/) {} +      }; + +#endif + + + diff --git a/muse/muse/muse.cpp b/muse/muse/muse.cpp index 31fcdc5b..f22e654b 100644 --- a/muse/muse/muse.cpp +++ b/muse/muse/muse.cpp @@ -82,7 +82,7 @@ static const char* fileSaveText =  static const char* infoLoopButton     = QT_TR_NOOP("loop between left mark and right mark");  static const char* infoPunchinButton  = QT_TR_NOOP("record starts at left mark");  static const char* infoPunchoutButton = QT_TR_NOOP("record stops at right mark"); -static const char* infoStartButton    = QT_TR_NOOP("rewind to start position"); +// static const char* infoStartButton    = QT_TR_NOOP("rewind to start position");  static const char* infoRewindButton   = QT_TR_NOOP("rewind current position");  static const char* infoForwardButton  = QT_TR_NOOP("move current position");  static const char* infoStopButton     = QT_TR_NOOP("stop sequencer"); @@ -1690,7 +1690,7 @@ void MusE::selectProject(QAction* a)  //   kbAccel  //--------------------------------------------------------- -void MusE::kbAccel(int key) +void MusE::kbAccel(int /*key*/)        {  #if 0 //TODOB        if (key == shortcuts[SHRT_TOGGLE_METRO].key) { @@ -1863,23 +1863,27 @@ void MusE::cmd(QAction* a)  //TODO1                  arranger->cmd(Arranger::CMD_PASTE_PART);                    break;              case CMD_DELETE: -                  song->startUndo(); -                  if (song->msgRemoveParts()) { -                        song->endUndo(SC_PART_REMOVED); -                        break; +                  { +                  TrackList* tl = song->tracks(); +                  bool partsMarked = false; +                  for (iTrack it = tl->begin(); it != tl->end(); ++it) { +      	            PartList* pl2 = (*it)->parts(); +                        for (iPart ip = pl2->begin(); ip != pl2->end(); ++ip) { +            	            if (ip->second->selected()) { +                  	            partsMarked = true; +                                    break; +                                    } +                              }                          } -                  else { -                  	// if there are no selected parts, delete -                  	// selected tracks -                  	// +                  if (partsMarked) +                        song->cmdRemoveParts(); +                  else                          audio->msgRemoveTracks(); -                  	} -                  song->endUndo(SC_TRACK_REMOVED); +                  }                    break; +              case CMD_DELETE_TRACK: -                  song->startUndo();                    audio->msgRemoveTracks(); -                  song->endUndo(SC_TRACK_REMOVED);                    break;              case CMD_SELECT_ALL: @@ -2193,7 +2197,7 @@ void MusE::globalCut()                    if (t + l <= lpos)                          continue;                    if ((t >= lpos) && ((t+l) <= rpos)) { -                        audio->msgRemovePart(part, false); +                        song->removePart(part);                          }                    else if ((t < lpos) && ((t+l) > lpos) && ((t+l) <= rpos)) {                          // remove part tail @@ -2337,7 +2341,7 @@ void MusE::globalSplit()                          Part* p2;                          track->splitPart(part, pos, p1, p2);                          audio->msgChangePart(part, p1, false); -                        audio->msgAddPart(p2, false); +                        song->addPart(p2);                          break;                          }                    } diff --git a/muse/muse/part.cpp b/muse/muse/part.cpp index b4a6bc86..ea8ca457 100644 --- a/muse/muse/part.cpp +++ b/muse/muse/part.cpp @@ -223,162 +223,6 @@ printf("remove part: not found\n");        }  //--------------------------------------------------------- -//   splitPart -//    split part "part" at "tick" position -//    create two new parts p1 and p2 -//--------------------------------------------------------- - -void Track::splitPart(Part* part, int tickpos, Part*& p1, Part*& p2) -      { -      int l1 = 0;       // len of first new part (ticks or samples) -      int l2 = 0;       // len of second new part - -      int samplepos = AL::tempomap.tick2frame(tickpos); - -      switch (type()) { -            case WAVE: -                  l1 = samplepos - part->frame(); -                  l2 = part->lenFrame() - l1; -                  break; -            case MIDI: -                  l1 = tickpos - part->tick(); -                  l2 = part->lenTick() - l1; -                  break; -            default: -                  return; -            } - -      if (l1 <= 0 || l2 <= 0) -            return; - -      p1 = newPart(part);     // new left part -      p2 = newPart(part);     // new right part - -      switch (type()) { -            case WAVE: -                  p1->setLenFrame(l1); -                  p2->setFrame(samplepos); -                  p2->setLenFrame(l2); -                  break; -            case MIDI: -                  p1->setLenTick(l1); -                  p2->setTick(tickpos); -                  p2->setLenTick(l2); -                  break; -            default: -                  break; -            } - -      EventList* se  = part->events(); -      EventList* de1 = p1->events(); -      EventList* de2 = p2->events(); - -      if (type() == WAVE) { -            int ps   = part->frame(); -            int d1p1 = p1->frame(); -            int d2p1 = p1->endFrame(); -            int d1p2 = p2->frame(); -            int d2p2 = p2->endFrame(); -            for (iEvent ie = se->begin(); ie != se->end(); ++ie) { -                  Event event = ie->second; -                  int s1 = event.frame() + ps; -                  int s2 = event.endFrame() + ps; - -                  if ((s2 > d1p1) && (s1 < d2p1)) { -                        Event si = event.mid(d1p1 - ps, d2p1 - ps); -                        de1->add(si); -                        } -                  if ((s2 > d1p2) && (s1 < d2p2)) { -                        Event si = event.mid(d1p2 - ps, d2p2 - ps); -                        si.setFrame(si.frame() - l1);       //?? -                        si.setFrame(0);                     //?? -                        de2->add(si); -                        } -                  } -            } -      else { -            for (iEvent ie = se->begin(); ie != se->end(); ++ie) { -                  Event event = ie->second.clone(); -                  int t = event.tick(); -                  if (t >= l1) { -                        event.move(-l1); -                        de2->add(event); -                        } -                  else -                        de1->add(event); -                  } -            } -      } - -//--------------------------------------------------------- -//   cmdSplitPart -//--------------------------------------------------------- - -void Song::cmdSplitPart(Part* part, const Pos& pos) -      { -      int tick = pos.tick(); -      int l1 = tick - part->tick(); -      int l2 = part->lenTick() - l1; -      if (l1 <= 0 || l2 <= 0) -            return; -      Part* p1; -      Part* p2; -      part->track()->splitPart(part, tick, p1, p2); - -      startUndo(); -      audio->msgChangePart(part, p1, false); -      audio->msgAddPart(p2, false); -      endUndo(SC_TRACK_MODIFIED | SC_PART_MODIFIED | SC_PART_INSERTED); -      part->track()->partListChanged(); -      } - -//--------------------------------------------------------- -//   cmdGluePart -//--------------------------------------------------------- - -void Song::cmdGluePart(Part* oPart) -      { -      Track* track   = oPart->track(); -      PartList* pl   = track->parts(); -      Part* nextPart = 0; - -      for (iPart ip = pl->begin(); ip != pl->end(); ++ip) { -            if (ip->second == oPart) { -                  ++ip; -                  if (ip == pl->end()) -                        return; -                  nextPart = ip->second; -                  break; -                  } -            } - -      Part* nPart = track->newPart(oPart); -      nPart->setLenTick(nextPart->tick() + nextPart->lenTick() - oPart->tick()); - -      // populate nPart with Events from oPart and nextPart - -      EventList* sl1 = oPart->events(); -      EventList* dl  = nPart->events(); - -      for (iEvent ie = sl1->begin(); ie != sl1->end(); ++ie) -            dl->add(ie->second); - -      EventList* sl2 = nextPart->events(); -      int tickOffset = nextPart->tick() - oPart->tick(); - -      for (iEvent ie = sl2->begin(); ie != sl2->end(); ++ie) { -            Event event = ie->second.clone(); -            event.move(tickOffset); -            dl->add(event); -            } -      startUndo(); -      audio->msgRemovePart(nextPart, false); -      audio->msgChangePart(oPart, nPart, false); -      endUndo(SC_PART_MODIFIED | SC_PART_REMOVED); -      track->partListChanged(); -      } - -//---------------------------------------------------------  //   dump  //--------------------------------------------------------- diff --git a/muse/muse/seqmsg.cpp b/muse/muse/seqmsg.cpp index 1033381a..28dfe043 100644 --- a/muse/muse/seqmsg.cpp +++ b/muse/muse/seqmsg.cpp @@ -336,7 +336,7 @@ void Audio::msgRemoveTrack(Track* track)        AudioMsg msg;        msg.id = SEQM_REMOVE_TRACK;        msg.track = track; -      sendMessage(&msg, false); +      sendMessage(&msg, true);        }  //--------------------------------------------------------- @@ -376,55 +376,6 @@ void Audio::msgMoveTrack(Track* src, Track* dst)        }  //--------------------------------------------------------- -//   msgAddPart -//--------------------------------------------------------- - -void Audio::msgAddPart(Part* part, bool doUndoFlag) -      { -      AudioMsg msg; -      msg.id = SEQM_ADD_PART; -      msg.p1 = part; -      sendMessage(&msg, doUndoFlag); -      } - -//--------------------------------------------------------- -//   msgRemovePart -//--------------------------------------------------------- - -void Audio::msgRemovePart(Part* part, bool doUndoFlag) -      { -      AudioMsg msg; -      msg.id = SEQM_REMOVE_PART; -      msg.p1 = part; -      sendMessage(&msg, doUndoFlag); -      part->track()->partListChanged();   // emit signal -      } - -//--------------------------------------------------------- -//   msgRemoveParts -//    remove selected parts; return true if any part was -//    removed -//--------------------------------------------------------- - -bool Song::msgRemoveParts() -      { -      TrackList* tl = song->tracks(); -      PartList pl; - -      for (iTrack it = tl->begin(); it != tl->end(); ++it) { -      	PartList* pl2 = (*it)->parts(); -            for (iPart ip = pl2->begin(); ip != pl2->end(); ++ip) { -            	if (ip->second->selected()) -                  	pl.add(ip->second); -                  } -            } -      for (iPart ip = pl.begin(); ip != pl.end(); ++ip) -		audio->msgRemovePart(ip->second, false); - -      return !pl.empty(); -      } - -//---------------------------------------------------------  //   msgChangePart  //--------------------------------------------------------- diff --git a/muse/muse/song.cpp b/muse/muse/song.cpp index a051d978..244b3a72 100644 --- a/muse/muse/song.cpp +++ b/muse/muse/song.cpp @@ -923,19 +923,7 @@ void Song::processMsg(AudioMsg* msg)              case SEQM_REDO:                    doRedo2();                    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_EVENT:                    updateFlags = SC_EVENT_INSERTED;                    if (addEvent(msg->ev1, (Part*)(msg->p2))) { @@ -1004,63 +992,41 @@ void Song::processMsg(AudioMsg* msg)                    msg->track->removeControllerVal(msg->a, msg->time);                    break; -            default: -                  printf("unknown seq message %d\n", msg->id); +            case SEQM_ADD_TRACK: +                  insertTrack2(msg->track);                    break; -            } -      } -//--------------------------------------------------------- -//   cmdAddPart -//	realtime context -//--------------------------------------------------------- - -void Song::cmdAddPart(Part* part) -      { -      part->track()->addPart(part); -      undoOp(UndoOp::AddPart, part); -      updateFlags = SC_PART_INSERTED; -      if (len() < part->endTick()) -            setLen(part->endTick()); -      } - -//--------------------------------------------------------- -//   cmdRemovePart -//--------------------------------------------------------- - -void Song::cmdRemovePart(Part* part) -      { -      Track* track = part->track(); -      track->parts()->remove(part); +            case SEQM_REMOVE_TRACK: +                  removeTrack2(msg->track); +                  break; -//TD      esettingsList->removeSettings(part->sn()); -      undoOp(UndoOp::DeletePart, part); -      part->events()->incARef(-1); -      updateFlags = SC_PART_MODIFIED; -      } +            case SEQM_ADD_PART: +                  { +                  Part* part = (Part*)(msg->p1); +                  part->track()->addPart(part); +                  } +                  break; -//--------------------------------------------------------- -//   changePart -//--------------------------------------------------------- +            case SEQM_REMOVE_PART: +                  { +                  Part* part = (Part*)(msg->p1); +                  Track* track = part->track(); +                  track->parts()->remove(part); +                  } +                  break; -void Song::changePart(Part* oldPart, Part* newPart) -      { -      Part part = *newPart; -      *newPart  = *oldPart; -      *oldPart  = part; -      } +            case SEQM_CHANGE_PART: +                  cmdChangePart((Part*)msg->p1, (Part*)msg->p2); +                  break; -//--------------------------------------------------------- -//   cmdChangePart -//	realtime context -//--------------------------------------------------------- +            case SEQM_MOVE_TRACK: +                  moveTrack((Track*)(msg->p1), (Track*)(msg->p2)); +                  break; -void Song::cmdChangePart(Part* oldPart, Part* newPart) -      { -      changePart(oldPart, newPart); -      undoOp(UndoOp::ModifyPart, oldPart, newPart); -      oldPart->events()->incARef(-1); -      updateFlags = SC_PART_MODIFIED; +            default: +                  printf("unknown seq message %d\n", msg->id); +                  break; +            }        }  //--------------------------------------------------------- @@ -1228,52 +1194,6 @@ void Song::seqSignal(int fd)              }        } -#if 0 -//--------------------------------------------------------- -//   recordEvent -//--------------------------------------------------------- - -void Song::recordEvent(MidiTrack* mt, Event& event) -      { -      //--------------------------------------------------- -      //    if tick points into a part, -      //          record to that part -      //    else -      //          create new part -      //--------------------------------------------------- - -      unsigned tick  = event.tick(); -      PartList* pl   = mt->parts(); -      Part* part = 0; -      iPart ip; -      for (ip = pl->begin(); ip != pl->end(); ++ip) { -            part = ip->second; -            unsigned partStart = part->tick(); -            unsigned partEnd   = partStart + part->lenTick(); -            if (tick >= partStart && tick < partEnd) -                  break; -            } -      updateFlags |= SC_EVENT_INSERTED; -      if (ip == pl->end()) { -            // create new part -            part          = new Part(mt); -            int startTick = roundDownBar(tick); -            int endTick   = roundUpBar(tick); -            part->setTick(startTick); -            part->setLenTick(endTick - startTick); -            part->setName(mt->name()); -            event.move(-startTick); -            part->events()->add(event); -            audio->msgAddPart(part); -            return; -            } -      part = ip->second; -      tick -= part->tick(); -      event.setTick(tick); -      audio->msgAddEvent(event, part); -      } -#endif -  //---------------------------------------------------------  //   stopRolling  //--------------------------------------------------------- @@ -1823,174 +1743,6 @@ std::vector<QString>* Song::synthesizer() const        }  //--------------------------------------------------------- -//   changePart -//    extend/shrink part in front or at end -//--------------------------------------------------------- - -void Song::changePart(Part* oPart, unsigned pos, unsigned len) -      { -      startUndo(); -      // -      // move events so they stay at same position in song -      // -      int delta    = oPart->tick() - pos; -      EventList* d = new EventList(); -      EventList* s = oPart->events(); -      for (iEvent ie = s->begin(); ie != s->end(); ++ie) { -            int tick = ie->first + delta; -            if (tick >= 0 && tick < int(len)) { -                  Event ev = ie->second.clone(); -                  ev.move(delta); -                  d->add(ev, unsigned(tick)); -                  } -            } -      if (oPart->fillLen() > 0 && len < (unsigned)oPart->fillLen()) -            oPart->setFillLen(len); -      if (oPart->lenTick() < len && oPart->fillLen() > 0) { -            unsigned loop = oPart->fillLen(); -            unsigned fillLen = len - oPart->lenTick(); -            for (unsigned i = 0; i < fillLen / loop; ++i) { -                  int start = oPart->lenTick() + loop * i; -                  for (iEvent ie = s->begin(); ie != s->end(); ++ie) { -                        if (ie->first >= loop) -                              break; -                  	Event ev = ie->second.clone(); -                        ev.move(start); -                        d->add(ev, ie->first + start); -                        } -                  } -            } -      Part* nPart = new Part(*oPart, d); -      nPart->setLenTick(len); -      nPart->setTick(pos); -      audio->msgChangePart(oPart, nPart, false); -      endUndo(SC_PART_MODIFIED); -      oPart->track()->partListChanged(); -      if (unsigned(_len) < oPart->endTick())  // update song len -            setLen(oPart->endTick()); -      } - -//--------------------------------------------------------- -//   movePart -//--------------------------------------------------------- - -void Song::movePart(Part* oPart, unsigned pos, Track* track) -      { -      Track* oTrack = oPart->track(); -      Part* nPart   = new Part(*oPart); -      nPart->setTrack(track); -      nPart->setTick(pos); -      startUndo(); -      if (oPart->track() != track) { -	      audio->msgRemovePart(oPart, false); -	      audio->msgAddPart(nPart, false); -            } -      else { -	      audio->msgChangePart(oPart, nPart, false); -            } -      endUndo(0); -      oTrack->partListChanged(); -      if (len() < nPart->endTick()) -            setLen(nPart->endTick()); -      } - -//--------------------------------------------------------- -//   linkPart -//--------------------------------------------------------- - -void Song::linkPart(Part* sPart, unsigned pos, Track* track) -      { -      Part* dPart = track->newPart(sPart, true); -      dPart->setTick(pos); -      audio->msgAddPart(dPart); -      sPart->track()->partListChanged(); -      dPart->track()->partListChanged(); -      } - -//--------------------------------------------------------- -//   copyPart -//--------------------------------------------------------- - -void Song::copyPart(Part* sPart, unsigned pos, Track* track) -      { -      bool clone = sPart->events()->arefCount() > 1; -      Part* dPart = track->newPart(sPart, clone); -      dPart->setTick(pos); -      if (!clone) { -            // -            // Copy Events -            // -            EventList* se = sPart->events(); -            EventList* de = dPart->events(); -            for (iEvent i = se->begin(); i != se->end(); ++i) { -                  Event oldEvent = i->second; -                  Event ev = oldEvent.clone(); -                  de->add(ev); -                  } -            } -      audio->msgAddPart(dPart); -      sPart->track()->partListChanged(); -      dPart->track()->partListChanged(); -      } - -//--------------------------------------------------------- -//   createLRPart -//--------------------------------------------------------- - -void Song::createLRPart(Track* track) -      { -      Part* part = track->newPart(); -      if (part) { -            part->setTick(pos[1].tick()); -            part->setLenTick(pos[2].tick()-pos[1].tick()); -            part->setSelected(true); -            addPart(part); -            } -      } - -//--------------------------------------------------------- -//   addPart -//--------------------------------------------------------- - -void Song::addPart(Part* part) -      { -      audio->msgAddPart(part); -      // adjust song len: -      unsigned epos = part->tick() + part->lenTick(); - -      if (epos > len()) -            setLen(epos); -      part->track()->partListChanged(); -      } - -//--------------------------------------------------------- -//   selectPart -//--------------------------------------------------------- - -void Song::selectPart(Part* part, bool add) -      { -      if (add) { -            part->setSelected(!part->selected()); -            part->track()->partListChanged(); -            return; -            } -      for (iTrack it = _tracks.begin(); it != _tracks.end(); ++it) { -            PartList* pl = (*it)->parts(); -            bool changed = false; -            for (iPart ip = pl->begin(); ip != pl->end(); ++ip) { -                  bool f = part == ip->second; -                  if (ip->second->selected() != f) { -                        ip->second->setSelected(f); -                        changed = true; -                        } -                  } -            if (changed) -                  (*it)->partListChanged(); -            } -      } - - -//---------------------------------------------------------  //   setRecordFlag  //--------------------------------------------------------- diff --git a/muse/muse/song.h b/muse/muse/song.h index 610b0eec..8e011fb9 100644 --- a/muse/muse/song.h +++ b/muse/muse/song.h @@ -235,8 +235,6 @@ class Song : public QObject {        const Pos& lPos() const       { return pos[1]; }        const Pos& rPos() const       { return pos[2]; }        unsigned cpos() const         { return pos[0].tick(); } -//      unsigned vcpos() const        { return _vcpos.tick(); } -//      const Pos& vcPos() const      { return _vcpos; }        unsigned lpos() const         { return pos[1].tick(); }        unsigned rpos() const         { return pos[2].tick(); } @@ -276,18 +274,22 @@ class Song : public QObject {        //   part manipulations        //----------------------------------------- +      void cmdAddPart(Part* part); +      void addPart(Part* part); + +      void cmdRemoveParts(); +      void cmdRemovePart(Part* part); +      void removePart(Part* part); + +      void cmdChangePart(Part* oldPart, Part* newPart); +      void changePart(Part*, Part*); +        void cmdSplitPart(Part* p, const Pos&);        void cmdGluePart(Part* p); -      void changePart(Part*, Part*); -      void addPart(Part* part);        PartList* getSelectedMidiParts() const;        PartList* getSelectedWaveParts() const; -      bool msgRemoveParts(); -      void cmdChangePart(Part* oldPart, Part* newPart); -      void cmdRemovePart(Part* part); -      void cmdAddPart(Part* part);        void movePart(Part*, unsigned, Track*);        void linkPart(Part*, unsigned, Track*);        void copyPart(Part*, unsigned, Track*); @@ -324,7 +326,6 @@ class Song : public QObject {        void insertTrack1(Track*, int idx);        void insertTrack2(Track*);        void readRoute(QDomNode); -//      void recordEvent(MidiTrack*, Event&);        std::vector<QString>* synthesizer() const;        void deselectTracks(); diff --git a/muse/muse/songpart.cpp b/muse/muse/songpart.cpp new file mode 100644 index 00000000..7b49cdfa --- /dev/null +++ b/muse/muse/songpart.cpp @@ -0,0 +1,366 @@ +//============================================================================= +//  MusE +//  Linux Music Editor +//  $Id:$ +// +//  Copyright (C) 2002-2006 by Werner Schweer and others +// +//  This program is free software; you can redistribute it and/or modify +//  it under the terms of the GNU General Public License version 2. +// +//  This program is distributed in the hope that it will be useful, +//  but WITHOUT ANY WARRANTY; without even the implied warranty of +//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +//  GNU General Public License for more details. +// +//  You should have received a copy of the GNU General Public License +//  along with this program; if not, write to the Free Software +//  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +//============================================================================= + +#include "song.h" +#include "part.h" +#include "audio.h" + +//--------------------------------------------------------- +//   cmdAddPart +//    GUI context + startUndo/endUndo +//--------------------------------------------------------- + +void Song::cmdAddPart(Part* part) +      { +      Track* track = part->track(); +      // +      // create default name: +      // +      for (int i = 1;;++i) { +            PartList* pl = track->parts(); +            bool found = false; +            QString name = QString("Part-%1").arg(i); +            for (iPart ip = pl->begin(); ip != pl->end(); ++ip) { +                  if (name == ip->second->name()) { +                        found = true; +                        break; +                        } +                  } +            if (!found) { +                  part->setName(name); +                  break; +                  } +            } +      startUndo(); +      addPart(part); +      endUndo(0); +      track->partListChanged(); +      } + +//--------------------------------------------------------- +//   addPart +//	GUI context +//--------------------------------------------------------- + +void Song::addPart(Part* part) +      { +      AudioMsg msg; +      msg.id = SEQM_ADD_PART; +      msg.p1 = part; +      audio->sendMessage(&msg, false); +      undoOp(UndoOp::AddPart, part); +      updateFlags |= SC_PART_INSERTED; +      if (len() < part->endTick()) +            setLen(part->endTick()); +      } + +//--------------------------------------------------------- +//   cmdRemovePart +//--------------------------------------------------------- + +void Song::cmdRemovePart(Part* part) +      { +      startUndo(); +      removePart(part); +      endUndo(0); +      part->track()->partListChanged(); +      } + +//--------------------------------------------------------- +//   removePart +//--------------------------------------------------------- + +void Song::removePart(Part* part) +      { +      AudioMsg msg; +      msg.id = SEQM_REMOVE_PART; +      msg.p1 = part; +      audio->sendMessage(&msg, false); +      undoOp(UndoOp::DeletePart, part); +      part->events()->incARef(-1); +      updateFlags |= SC_PART_REMOVED; +      } + +//--------------------------------------------------------- +//   cmdRemoveParts +//    remove selected parts +//--------------------------------------------------------- + +void Song::cmdRemoveParts() +      { +      TrackList* tl = song->tracks(); +      PartList pl; + +      for (iTrack it = tl->begin(); it != tl->end(); ++it) { +      	PartList* pl2 = (*it)->parts(); +            for (iPart ip = pl2->begin(); ip != pl2->end(); ++ip) { +            	if (ip->second->selected()) +                  	pl.add(ip->second); +                  } +            } +      for (iPart ip = pl.begin(); ip != pl.end(); ++ip) +		removePart(ip->second); +      } + +//--------------------------------------------------------- +//   cmdChangePart +//	realtime context +//--------------------------------------------------------- + +void Song::cmdChangePart(Part* oldPart, Part* newPart) +      { +      changePart(oldPart, newPart); +      undoOp(UndoOp::ModifyPart, oldPart, newPart); +      oldPart->events()->incARef(-1); +      updateFlags = SC_PART_MODIFIED; +      } + +//--------------------------------------------------------- +//   changePart +//--------------------------------------------------------- + +void Song::changePart(Part* oldPart, Part* newPart) +      { +      Part part = *newPart; +      *newPart  = *oldPart; +      *oldPart  = part; +      } + +//--------------------------------------------------------- +//   changePart +//    extend/shrink part in front or at end +//--------------------------------------------------------- + +void Song::changePart(Part* oPart, unsigned pos, unsigned len) +      { +      startUndo(); +      // +      // move events so they stay at same position in song +      // +      int delta    = oPart->tick() - pos; +      EventList* d = new EventList(); +      EventList* s = oPart->events(); +      for (iEvent ie = s->begin(); ie != s->end(); ++ie) { +            int tick = ie->first + delta; +            if (tick >= 0 && tick < int(len)) { +                  Event ev = ie->second.clone(); +                  ev.move(delta); +                  d->add(ev, unsigned(tick)); +                  } +            } +      if (oPart->fillLen() > 0 && len < (unsigned)oPart->fillLen()) +            oPart->setFillLen(len); +      if (oPart->lenTick() < len && oPart->fillLen() > 0) { +            unsigned loop = oPart->fillLen(); +            unsigned fillLen = len - oPart->lenTick(); +            for (unsigned i = 0; i < fillLen / loop; ++i) { +                  int start = oPart->lenTick() + loop * i; +                  for (iEvent ie = s->begin(); ie != s->end(); ++ie) { +                        if (ie->first >= loop) +                              break; +                  	Event ev = ie->second.clone(); +                        ev.move(start); +                        d->add(ev, ie->first + start); +                        } +                  } +            } +      Part* nPart = new Part(*oPart, d); +      nPart->setLenTick(len); +      nPart->setTick(pos); +      audio->msgChangePart(oPart, nPart, false); +      endUndo(SC_PART_MODIFIED); +      oPart->track()->partListChanged(); +      if (unsigned(_len) < oPart->endTick())  // update song len +            setLen(oPart->endTick()); +      } + +//--------------------------------------------------------- +//   movePart +//--------------------------------------------------------- + +void Song::movePart(Part* oPart, unsigned pos, Track* track) +      { +      Track* oTrack = oPart->track(); +      Part* nPart   = new Part(*oPart); +      nPart->setTrack(track); +      nPart->setTick(pos); +      startUndo(); +      if (oPart->track() != track) { +	      removePart(oPart); +	      addPart(nPart); +            } +      else { +	      audio->msgChangePart(oPart, nPart, false); +            } +      endUndo(0); +      oTrack->partListChanged(); +      if (len() < nPart->endTick()) +            setLen(nPart->endTick()); +      } + +//--------------------------------------------------------- +//   linkPart +//--------------------------------------------------------- + +void Song::linkPart(Part* sPart, unsigned pos, Track* track) +      { +      Part* dPart = track->newPart(sPart, true); +      dPart->setTick(pos); +      cmdAddPart(dPart); +      sPart->track()->partListChanged(); +      dPart->track()->partListChanged(); +      } + +//--------------------------------------------------------- +//   copyPart +//--------------------------------------------------------- + +void Song::copyPart(Part* sPart, unsigned pos, Track* track) +      { +      bool clone = sPart->events()->arefCount() > 1; +      Part* dPart = track->newPart(sPart, clone); +      dPart->setTick(pos); +      if (!clone) { +            // +            // Copy Events +            // +            EventList* se = sPart->events(); +            EventList* de = dPart->events(); +            for (iEvent i = se->begin(); i != se->end(); ++i) { +                  Event oldEvent = i->second; +                  Event ev = oldEvent.clone(); +                  de->add(ev); +                  } +            } +      cmdAddPart(dPart); +      sPart->track()->partListChanged(); +      dPart->track()->partListChanged(); +      } + +//--------------------------------------------------------- +//   createLRPart +//--------------------------------------------------------- + +void Song::createLRPart(Track* track) +      { +      Part* part = track->newPart(); +      if (part) { +            part->setTick(pos[1].tick()); +            part->setLenTick(pos[2].tick()-pos[1].tick()); +            part->setSelected(true); +            cmdAddPart(part); +            } +      } + +//--------------------------------------------------------- +//   selectPart +//--------------------------------------------------------- + +void Song::selectPart(Part* part, bool add) +      { +      if (add) { +            part->setSelected(!part->selected()); +            part->track()->partListChanged(); +            return; +            } +      for (iTrack it = _tracks.begin(); it != _tracks.end(); ++it) { +            PartList* pl = (*it)->parts(); +            bool changed = false; +            for (iPart ip = pl->begin(); ip != pl->end(); ++ip) { +                  bool f = part == ip->second; +                  if (ip->second->selected() != f) { +                        ip->second->setSelected(f); +                        changed = true; +                        } +                  } +            if (changed) +                  (*it)->partListChanged(); +            } +      } + +//--------------------------------------------------------- +//   cmdSplitPart +//--------------------------------------------------------- + +void Song::cmdSplitPart(Part* part, const Pos& pos) +      { +      int tick = pos.tick(); +      int l1 = tick - part->tick(); +      int l2 = part->lenTick() - l1; +      if (l1 <= 0 || l2 <= 0) +            return; +      Part* p1; +      Part* p2; +      part->track()->splitPart(part, tick, p1, p2); + +      startUndo(); +      audio->msgChangePart(part, p1, false); +      addPart(p2); +      endUndo(SC_TRACK_MODIFIED | SC_PART_MODIFIED | SC_PART_INSERTED); +      part->track()->partListChanged(); +      } + +//--------------------------------------------------------- +//   cmdGluePart +//--------------------------------------------------------- + +void Song::cmdGluePart(Part* oPart) +      { +      Track* track   = oPart->track(); +      PartList* pl   = track->parts(); +      Part* nextPart = 0; + +      for (iPart ip = pl->begin(); ip != pl->end(); ++ip) { +            if (ip->second == oPart) { +                  ++ip; +                  if (ip == pl->end()) +                        return; +                  nextPart = ip->second; +                  break; +                  } +            } + +      Part* nPart = track->newPart(oPart); +      nPart->setLenTick(nextPart->tick() + nextPart->lenTick() - oPart->tick()); + +      // populate nPart with Events from oPart and nextPart + +      EventList* sl1 = oPart->events(); +      EventList* dl  = nPart->events(); + +      for (iEvent ie = sl1->begin(); ie != sl1->end(); ++ie) +            dl->add(ie->second); + +      EventList* sl2 = nextPart->events(); +      int tickOffset = nextPart->tick() - oPart->tick(); + +      for (iEvent ie = sl2->begin(); ie != sl2->end(); ++ie) { +            Event event = ie->second.clone(); +            event.move(tickOffset); +            dl->add(event); +            } +      startUndo(); +      removePart(nextPart); +      audio->msgChangePart(oPart, nPart, false); +      endUndo(SC_PART_MODIFIED | SC_PART_REMOVED); +      track->partListChanged(); +      } + + diff --git a/muse/muse/track.cpp b/muse/muse/track.cpp index 14189c61..ca457ad6 100644 --- a/muse/muse/track.cpp +++ b/muse/muse/track.cpp @@ -21,6 +21,7 @@  #include "track.h"  #include "midiplugin.h"  #include "song.h" +#include "al/tempo.h"  #include "al/xml.h"  #include "icons.h"  #include "muse.h" @@ -682,121 +683,6 @@ void Track::writeRouting(Xml& xml) const        }  //--------------------------------------------------------- -//   MidiTrackBase -//--------------------------------------------------------- - -MidiTrackBase::MidiTrackBase() -   : Track() -      { -      _pipeline = new MidiPipeline(); -      } - -//--------------------------------------------------------- -//   MidiTrackBase -//--------------------------------------------------------- - -MidiTrackBase::~MidiTrackBase() -      { -      foreach(MidiPluginI* plugin, *_pipeline) -            delete plugin; -      delete _pipeline; -      } - -//--------------------------------------------------------- -//   MidiTrackBase::writeProperties -//--------------------------------------------------------- - -void MidiTrackBase::writeProperties(Xml& xml) const -      { -      Track::writeProperties(xml); -      for (ciMidiPluginI ip = _pipeline->begin(); ip != _pipeline->end(); ++ip) { -            if (*ip) -                  (*ip)->writeConfiguration(xml); -            } -      } - -//--------------------------------------------------------- -//   MidiTrackBase::readProperties -//--------------------------------------------------------- - -bool MidiTrackBase::readProperties(QDomNode node) -      { -      QDomElement e = node.toElement(); -      QString tag(e.tagName()); -      if (tag == "midiPlugin") { -            MidiPluginI* pi = new MidiPluginI(this); -            if (pi->readConfiguration(node)) -                  delete pi; -            else -                  addPlugin(pi, -1); -            } -      else -            return Track::readProperties(node); -      return false; -      } - -//--------------------------------------------------------- -//   plugin -//--------------------------------------------------------- - -MidiPluginI* MidiTrackBase::plugin(int idx) const -      { -      return _pipeline->value(idx); -      } - -//--------------------------------------------------------- -//   addPlugin -//    idx    = -1     append -//    plugin = 0   remove slot -//--------------------------------------------------------- - -void MidiTrackBase::addPlugin(MidiPluginI* plugin, int idx) -      { -      if (plugin == 0) { -#if 0 -            MidiPluginI* oldPlugin = (*_pipeline)[idx]; -            if (oldPlugin) { -                  int controller = oldPlugin->plugin()->parameter(); -                  for (int i = 0; i < controller; ++i) { -                        int id = (idx + 1) * 0x1000 + i; -                        removeController(id); -                        } -                  } -#endif -            } -      if (idx == -1) -            idx = _pipeline->size(); - -      if (plugin) { -            _pipeline->insert(idx, plugin); -#if 0 -            int ncontroller = plugin->plugin()->parameter(); -            for (int i = 0; i < ncontroller; ++i) { -                  int id = (idx + 1) * 0x1000 + i; -                  QString name(plugin->getParameterName(i)); -                  double min, max; -                  plugin->range(i, &min, &max); -                  Ctrl* cl = getController(id); -                  if (cl == 0) { -                        cl = new Ctrl(id, name); -                        cl->setRange(min, max); -                        double defaultValue = plugin->defaultValue(i); -                        cl->setDefault(defaultValue); -                        cl->setCurVal(defaultValue); -                        addController(cl); -                        } -                  plugin->setParam(i, cl->schedVal().f); -                  plugin->setControllerList(cl); -                  } -#endif -            } -      else { -            _pipeline->removeAt(idx); -            } -      } - - -//---------------------------------------------------------  //   hwCtrlState  //--------------------------------------------------------- @@ -1072,4 +958,92 @@ void Track::setSendSync(bool val)        emit sendSyncChanged(val);        } +//--------------------------------------------------------- +//   splitPart +//    split part "part" at "tick" position +//    create two new parts p1 and p2 +//--------------------------------------------------------- + +void Track::splitPart(Part* part, int tickpos, Part*& p1, Part*& p2) +      { +      int l1 = 0;       // len of first new part (ticks or samples) +      int l2 = 0;       // len of second new part + +      int samplepos = AL::tempomap.tick2frame(tickpos); + +      switch (type()) { +            case WAVE: +                  l1 = samplepos - part->frame(); +                  l2 = part->lenFrame() - l1; +                  break; +            case MIDI: +                  l1 = tickpos - part->tick(); +                  l2 = part->lenTick() - l1; +                  break; +            default: +                  return; +            } + +      if (l1 <= 0 || l2 <= 0) +            return; + +      p1 = newPart(part);     // new left part +      p2 = newPart(part);     // new right part + +      switch (type()) { +            case WAVE: +                  p1->setLenFrame(l1); +                  p2->setFrame(samplepos); +                  p2->setLenFrame(l2); +                  break; +            case MIDI: +                  p1->setLenTick(l1); +                  p2->setTick(tickpos); +                  p2->setLenTick(l2); +                  break; +            default: +                  break; +            } + +      EventList* se  = part->events(); +      EventList* de1 = p1->events(); +      EventList* de2 = p2->events(); + +      if (type() == WAVE) { +            int ps   = part->frame(); +            int d1p1 = p1->frame(); +            int d2p1 = p1->endFrame(); +            int d1p2 = p2->frame(); +            int d2p2 = p2->endFrame(); +            for (iEvent ie = se->begin(); ie != se->end(); ++ie) { +                  Event event = ie->second; +                  int s1 = event.frame() + ps; +                  int s2 = event.endFrame() + ps; + +                  if ((s2 > d1p1) && (s1 < d2p1)) { +                        Event si = event.mid(d1p1 - ps, d2p1 - ps); +                        de1->add(si); +                        } +                  if ((s2 > d1p2) && (s1 < d2p2)) { +                        Event si = event.mid(d1p2 - ps, d2p2 - ps); +                        si.setFrame(si.frame() - l1);       //?? +                        si.setFrame(0);                     //?? +                        de2->add(si); +                        } +                  } +            } +      else { +            for (iEvent ie = se->begin(); ie != se->end(); ++ie) { +                  Event event = ie->second.clone(); +                  int t = event.tick(); +                  if (t >= l1) { +                        event.move(-l1); +                        de2->add(event); +                        } +                  else +                        de1->add(event); +                  } +            } +      } + diff --git a/muse/muse/track.h b/muse/muse/track.h index 7ffb76a6..c7a68d8e 100644 --- a/muse/muse/track.h +++ b/muse/muse/track.h @@ -233,8 +233,8 @@ class Track : public QObject {        virtual TrackType type() const = 0; -      PartList* parts()               { return _parts; } -      const PartList* cparts() const  { return _parts; } +      PartList* parts() const         { return _parts; } +        Part* findPart(unsigned tick);  	void addPart(Part* p); @@ -332,30 +332,6 @@ class Track : public QObject {  Q_DECLARE_METATYPE(class Track*); -//--------------------------------------------------------- -//   MidiTrackBase -//--------------------------------------------------------- - -class MidiTrackBase : public Track { -      Q_OBJECT - -      MidiPipeline* _pipeline; - -   public: -      MidiTrackBase(); -      virtual ~MidiTrackBase(); - -      bool readProperties(QDomNode); -      void writeProperties(Xml&) const; - -      MidiPipeline* pipeline()      { return _pipeline;  } -      void addPlugin(MidiPluginI* plugin, int idx); -      MidiPluginI* plugin(int idx) const; - -      virtual void processMidi(unsigned, unsigned, unsigned, unsigned) {} -      virtual void getEvents(unsigned /*from*/, unsigned /*to*/, int /*channel*/, MidiEventList* /*dst*/) {} -      }; -  typedef QList<Track*> TrackList;  typedef TrackList::iterator iTrack;  typedef TrackList::const_iterator ciTrack; diff --git a/muse/muse/wave.cpp b/muse/muse/wave.cpp index 7ef0a665..934b8b9d 100644 --- a/muse/muse/wave.cpp +++ b/muse/muse/wave.cpp @@ -910,7 +910,7 @@ bool MusE::importWaveToTrack(const QString& wave, Track* track, const Pos& pos)        part->addEvent(event);        part->setName(srcInfo.baseName()); -      audio->msgAddPart(part); +      song->cmdAddPart(part);        unsigned endTick = part->tick() + part->lenTick();        if (song->len() < endTick)              song->setLen(endTick); diff --git a/muse/muse/wavetrack.cpp b/muse/muse/wavetrack.cpp index 8504d35c..c022c3c1 100644 --- a/muse/muse/wavetrack.cpp +++ b/muse/muse/wavetrack.cpp @@ -140,7 +140,7 @@ void WaveTrack::write(Xml& xml) const        {        xml.tag("wavetrack");        AudioTrack::writeProperties(xml); -      const PartList* pl = cparts(); +      const PartList* pl = parts();        for (ciPart p = pl->begin(); p != pl->end(); ++p)              p->second->write(xml);        xml.etag("wavetrack"); @@ -232,7 +232,7 @@ void WaveTrack::startRecording()              recordPart->setPos(spos);              recordPart->setLenTick(epos.tick() - spos.tick());              recordPart->setName(name()); -            audio->msgAddPart(recordPart, false); +            song->addPart(recordPart);              partCreated = true;              emit partsChanged();              }  | 
