diff options
| author | Florian Jung <flo@windfisch.org> | 2012-07-01 16:42:16 +0000 | 
|---|---|---|
| committer | Florian Jung <flo@windfisch.org> | 2012-07-01 16:42:16 +0000 | 
| commit | 9c4664d162c537ba4dd4fd8220971c0fb727103a (patch) | |
| tree | 37a28b7cd4e4d8984ad4934a4884cd7b4da0505c /muse2/muse | |
| parent | e87fedf1be804f7ec774071d844b1f163be30b96 (diff) | |
final merge
Diffstat (limited to 'muse2/muse')
65 files changed, 3690 insertions, 1085 deletions
| diff --git a/muse2/muse/arranger/arranger.cpp b/muse2/muse/arranger/arranger.cpp index 29e69582..8d786311 100644 --- a/muse2/muse/arranger/arranger.cpp +++ b/muse2/muse/arranger/arranger.cpp @@ -531,7 +531,7 @@ Arranger::Arranger(ArrangerView* parent, const char* name)        connect(canvas, SIGNAL(dropMidiFile(const QString&)), SIGNAL(dropMidiFile(const QString&)));        connect(canvas, SIGNAL(toolChanged(int)), SIGNAL(toolChanged(int))); -      connect(MusEGlobal::song,   SIGNAL(controllerChanged(MusECore::Track*)), SLOT(controllerChanged(MusECore::Track*))); +      connect(MusEGlobal::song,   SIGNAL(controllerChanged(MusECore::Track*, int)), SLOT(controllerChanged(MusECore::Track*, int)));        configChanged();  // set configuration values        if(canvas->part()) @@ -677,7 +677,7 @@ void Arranger::songChanged(int type)          // Keep this light, partsChanged is a heavy move!       TEST p4.0.36 Try these, may need more.          if(type & (SC_TRACK_INSERTED | SC_TRACK_REMOVED | SC_TRACK_MODIFIED |                      SC_PART_INSERTED | SC_PART_REMOVED | SC_PART_MODIFIED |  -                   SC_SIG | SC_TEMPO)) // Maybe sig. Requires tempo. +                   SC_SIG | SC_TEMPO | SC_MASTER)) // Maybe sig. Requires tempo.            canvas->partsChanged();          if (type & SC_SIG) @@ -1110,9 +1110,9 @@ void Arranger::clear()  //      emit redirectWheelEvent(ev);  //      } -void Arranger::controllerChanged(MusECore::Track *t) +void Arranger::controllerChanged(MusECore::Track *t, int ctrlId)  { -      canvas->controllerChanged(t); +      canvas->controllerChanged(t, ctrlId);  }  //--------------------------------------------------------- diff --git a/muse2/muse/arranger/arranger.h b/muse2/muse/arranger/arranger.h index e51ec068..60390a8f 100644 --- a/muse2/muse/arranger/arranger.h +++ b/muse2/muse/arranger/arranger.h @@ -186,7 +186,7 @@ class Arranger : public QWidget {        void setTool(int);        void updateTrackInfo(int flags);        void configChanged(); -      void controllerChanged(MusECore::Track *t); +      void controllerChanged(MusECore::Track *t, int ctrlId);        void focusCanvas();     public: diff --git a/muse2/muse/arranger/pcanvas.cpp b/muse2/muse/arranger/pcanvas.cpp index cc23b59b..c5c3ca6d 100644 --- a/muse2/muse/arranger/pcanvas.cpp +++ b/muse2/muse/arranger/pcanvas.cpp @@ -904,12 +904,25 @@ bool PartCanvas::mousePress(QMouseEvent* event)                        }                    }              case AutomationTool: -                  if (event->button() & Qt::RightButton) { -                      QMenu *automationMenu = new QMenu(this); -                      QAction* act; -                      act = automationMenu->addAction(tr("Remove selected")); -                      act = automationMenu->exec(event->globalPos()); -                      if (act && automation.currentTrack) { +                  if (event->button() & Qt::RightButton  ||  +                      event->button() & Qt::MidButton) { +                       +                      bool do_delete; +                       +                      if (event->button() & Qt::MidButton) // mid-click +                        do_delete=true; +                      else // right-click +                      { +                        QMenu *automationMenu = new QMenu(this); +                        QAction* act; +                        act = automationMenu->addAction(tr("Remove selected")); +                        act = automationMenu->exec(event->globalPos()); +                        if (act) +                          do_delete=true; +                        else +                          do_delete=false; +                      } +                      if (do_delete && automation.currentTrack) {                            foreach(int frame, automation.currentCtrlFrameList)                                MusEGlobal::audio->msgEraseACEvent((MusECore::AudioTrack*)automation.currentTrack,                                         automation.currentCtrlList->id(), frame); @@ -2683,6 +2696,7 @@ void PartCanvas::cmd(int cmd)                        case 0: paste_mode=PASTEMODE_MIX; break;                        case 1: paste_mode=PASTEMODE_MOVEALL; break;                        case 2: paste_mode=PASTEMODE_MOVESOME; break; +                      default: paste_mode=PASTEMODE_MIX; // shall never be executed                      }                      paste(paste_dialog->clone, paste_mode, paste_dialog->all_in_one_track, @@ -3411,7 +3425,7 @@ void PartCanvas::drawTopItem(QPainter& p, const QRect& rect)            yy += th;            } -    unsigned int startPos = MusEGlobal::audio->getStartRecordPos().tick(); +    unsigned int startPos = MusEGlobal::extSyncFlag.value() ? MusEGlobal::audio->getStartExternalRecTick() : MusEGlobal::audio->getStartRecordPos().tick();       if (MusEGlobal::song->punchin())        startPos=MusEGlobal::song->lpos();      int startx = mapx(startPos); @@ -3663,19 +3677,36 @@ void PartCanvas::drawAutomation(QPainter& p, const QRect& rr, MusECore::AudioTra  bool checkIfOnLine(double mouseX, double mouseY, double firstX, double lastX, double firstY, double lastY, int circumference)  { -  double proportion = (mouseX-firstX)/(lastX-firstX); - -  // 10   X(15)   20 -  // proportion = 0.5 -  //              10 -  //          / -  //      Y(5) -  //   / -  // 1 -  double calcY = (lastY-firstY)*proportion+firstY; -  if(ABS(calcY-mouseY) < circumference || (lastX == firstX && ABS(mouseX-lastX) < circumference)) -    return true; -  return false; +  if (lastX==firstX)   +    return (ABS(mouseX-lastX) < circumference); +  else if (mouseX < firstX || mouseX > lastX+circumference) // (*) +    return false; +  else +  { +    double proportion = (mouseX-firstX)/(lastX-firstX); // a value between 0 and 1, where firstX->0 and lastX->1 +    double calcY = (lastY-firstY)*proportion+firstY;    // where the drawn line's y-coord is at mouseX +    double slope = (lastY-firstY)/(lastX-firstX); +     +    return (ABS(calcY-mouseY) < (circumference * sqrt(1+slope*slope))); +    // this is equivalent to circumference / cos( atan(slope) ). to +    // verify, draw a sloped line (the graph), a 90°-line to it with +    // length "circumference". from the (unconnected) endpoint of that +    // line, draw a vertical line down to the sloped line. +    // use slope=tan(alpha) <==> alpha=atan(slope) and +    // cos(alpha) = adjacent side / hypothenuse (hypothenuse is what we +    // want, and adjacent = circumference). +    // to optimize: this looks similar to abs(slope)+1 +     +    //return (ABS(calcY-mouseY) < circumference); +  } +   +  /* without the +circumference in the above if statement (*), moving +   * the mouse towards a control point from the right would result in +   * the line segment from the targeted point to the next to be con- +   * sidered, but not the segment from the previous to the targeted. +   * however, only points for which the line segment they _end_ is +   * under the cursor are considered, so we need to enlengthen this +   * a bit  (flo93)*/  }  //--------------------------------------------------------- @@ -3684,12 +3715,7 @@ bool checkIfOnLine(double mouseX, double mouseY, double firstX, double lastX, do  bool checkIfNearPoint(int mouseX, int mouseY, int eventX, int eventY, int circumference)  { -  int x1 = ABS(mouseX - eventX) ; -  int y1 = ABS(mouseY - eventY); -  if (x1 < circumference &&  y1 < circumference) { -    return true; -  } -  return false; +  return (ABS(mouseX - eventX) < circumference &&  ABS(mouseY - eventY) < circumference);  }  //--------------------------------------------------------- @@ -3703,7 +3729,7 @@ bool checkIfNearPoint(int mouseX, int mouseY, int eventX, int eventY, int circum  //    controller added.  //--------------------------------------------------------- -void PartCanvas::checkAutomation(MusECore::Track * t, const QPoint &pointer, bool NOTaddNewCtrl) +void PartCanvas::checkAutomation(MusECore::Track * t, const QPoint &pointer, bool /*NOTaddNewCtrl*/)  {      if (t->isMidiTrack())        return; @@ -3751,7 +3777,7 @@ void PartCanvas::checkAutomation(MusECore::Track * t, const QPoint &pointer, boo          }          else // we have automation, loop through it          {   -          for (; ic !=cl->end(); ic++) +          for (; ic!=cl->end(); ic++)            {               double y = ic->second.val;               if (cl->valueType() == MusECore::VAL_LOG ) { // use db scale for volume @@ -3774,7 +3800,7 @@ void PartCanvas::checkAutomation(MusECore::Track * t, const QPoint &pointer, boo               eventOldX = eventX;               eventOldY = eventY; - +                            if (onLine) {                 if (!onPoint) {                   QWidget::setCursor(Qt::CrossCursor); @@ -3799,7 +3825,7 @@ void PartCanvas::checkAutomation(MusECore::Track * t, const QPoint &pointer, boo          // check if we are reasonably close to a line, we only need to check Y          // as the line is straight after the last controller          //printf("post oldX:%d oldY:%d xpixel:%d ypixel:%d currX:%d currY:%d\n", oldX, oldY, xpixel, ypixel, currX, currY); -        if(mouseX >= eventX && eventY == eventOldY && ABS(mouseY-eventY) < circumference) { +        if(mouseX >= eventX && ABS(mouseY-eventY) < circumference) {            QWidget::setCursor(Qt::CrossCursor);            automation.controllerState = addNewController;            automation.currentCtrlList = cl; @@ -3817,7 +3843,7 @@ void PartCanvas::checkAutomation(MusECore::Track * t, const QPoint &pointer, boo        setCursor();  } -void PartCanvas::controllerChanged(MusECore::Track* t) +void PartCanvas::controllerChanged(MusECore::Track* t, int)  {    redraw((QRect(0, mapy(t->y()), width(), rmapy(t->height()))));  // TODO Check this - correct?  } diff --git a/muse2/muse/arranger/pcanvas.h b/muse2/muse/arranger/pcanvas.h index ab227eb2..1b766c5d 100644 --- a/muse2/muse/arranger/pcanvas.h +++ b/muse2/muse/arranger/pcanvas.h @@ -184,7 +184,7 @@ class PartCanvas : public Canvas {     public slots:        void redirKeypress(QKeyEvent* e) { keyPress(e); } -      void controllerChanged(MusECore::Track *t); +      void controllerChanged(MusECore::Track *t, int CtrlId);  };  } // namespace MusEGui diff --git a/muse2/muse/arranger/tlist.cpp b/muse2/muse/arranger/tlist.cpp index 3d831ba9..05e23321 100644 --- a/muse2/muse/arranger/tlist.cpp +++ b/muse2/muse/arranger/tlist.cpp @@ -22,6 +22,8 @@  #include <cmath> +#include <QAction> +#include <QActionGroup>  #include <QKeyEvent>  #include <QLineEdit>  #include <QMessageBox> @@ -35,6 +37,7 @@  #include <QIcon>  #include <QSpinBox>  #include <QToolTip> +#include <QList>  #include "popupmenu.h"  #include "globals.h" @@ -64,6 +67,8 @@  #include "menutitleitem.h"  #include "arranger.h"  #include "undo.h" +#include "midi_audio_control.h" +#include "ctrl.h"  #ifdef DSSI_SUPPORT  #include "dssihost.h" @@ -1409,18 +1414,21 @@ MusECore::TrackList TList::getRecEnabledTracks()  void TList::changeAutomation(QAction* act)  { -  if ( (editAutomation->type() == MusECore::Track::MIDI) || (editAutomation->type() == MusECore::Track::DRUM) || (editAutomation->type() == MusECore::Track::NEW_DRUM) ) { -    printf("this is wrong, we can't edit automation for midi tracks from arranger yet!\n"); +  if(!editAutomation || editAutomation->isMidiTrack()) +    return; +  if(act->data().toInt() == -1)      return; -  }    int colindex = act->data().toInt() & 0xff; -  int id = (act->data().toInt() & 0x00ffffff) / 256; +  int id = (act->data().toInt() & 0x00ffffff) >> 8; +  // Is it the midi control action or clear action item? +  if (colindex == 254 || colindex == 255) +    return; +      if (colindex < 100)        return; // this was meant for changeAutomationColor                // one of these days I'll rewrite this so it's understandable                // this is just to get it up and running... -    MusECore::CtrlListList* cll = ((MusECore::AudioTrack*)editAutomation)->controller();    for(MusECore::CtrlListList::iterator icll =cll->begin();icll!=cll->end();++icll) {      MusECore::CtrlList *cl = icll->second; @@ -1435,13 +1443,81 @@ void TList::changeAutomation(QAction* act)  //---------------------------------------------------------  void TList::changeAutomationColor(QAction* act)  { -  if ( (editAutomation->type() == MusECore::Track::MIDI) || (editAutomation->type() == MusECore::Track::DRUM) || (editAutomation->type() == MusECore::Track::NEW_DRUM) ) { -    printf("this is wrong, we can't edit automation for midi tracks from arranger yet!\n"); +  if(!editAutomation || editAutomation->isMidiTrack()) +    return; +  if(act->data().toInt() == -1)      return; -  }    int colindex = act->data().toInt() & 0xff; -  int id = (act->data().toInt() & 0x00ffffff) / 256; +  int id = (act->data().toInt() & 0x00ffffff) >> 8; +  // Is it the clear midi control action item? +  if(colindex == 254)   +  { +    MusECore::AudioTrack* track = static_cast<MusECore::AudioTrack*>(editAutomation); +    MusECore::MidiAudioCtrlMap* macp = track->controller()->midiControls(); +    MusECore::AudioMidiCtrlStructMap amcs; +    macp->find_audio_ctrl_structs(id, &amcs); +    if(!amcs.empty()) +      MusEGlobal::audio->msgIdle(true);  // Gain access to structures, and sync with audio +    for(MusECore::iAudioMidiCtrlStructMap iamcs = amcs.begin(); iamcs != amcs.end(); ++iamcs) +      macp->erase(*iamcs); +    if(!amcs.empty()) +      MusEGlobal::audio->msgIdle(false); +     +    // Hm, need to remove the 'clear' item, and the status lines below it. Try this: +    QActionGroup* midi_actgrp = act->actionGroup(); +    if(midi_actgrp) +    { +      QList<QAction*> act_list = midi_actgrp->actions(); +      int sz = act_list.size(); +      for(int i = 0; i < sz; ++i) +      { +        QAction* list_act = act_list.at(i); +        ///midi_actgrp->removeAction(list_act); +        // list_act has no parent now. +        ///delete list_act; +        list_act->setVisible(false); // HACK Cannot delete any actions! Causes crash with our PopupMenu due to recent fixes. +      } +    } +    return; +  } +   +  // Is it the midi control action item? +  if(colindex == 255)   +  { +    MusECore::AudioTrack* track = static_cast<MusECore::AudioTrack*>(editAutomation); +    MusECore::MidiAudioCtrlMap* macm = track->controller()->midiControls(); +    MusECore::AudioMidiCtrlStructMap amcs; +    macm->find_audio_ctrl_structs(id, &amcs); +     +    int port = -1, chan = 0, ctrl = 0; +    for(MusECore::iAudioMidiCtrlStructMap iamcs = amcs.begin(); iamcs != amcs.end(); ++iamcs) +    { +      macm->hash_values((*iamcs)->first, &port, &chan, &ctrl); +      break; // Only a single item for now, thanks! +    } +     +    MidiAudioControl* pup = new MidiAudioControl(port, chan, ctrl); +     +    if(pup->exec() == QDialog::Accepted) +    { +      MusEGlobal::audio->msgIdle(true);  // Gain access to structures, and sync with audio +      // Erase all for now. +      for(MusECore::iAudioMidiCtrlStructMap iamcs = amcs.begin(); iamcs != amcs.end(); ++iamcs) +        macm->erase(*iamcs); +       +      port = pup->port(); chan = pup->chan(); ctrl = pup->ctrl(); +      if(port >= 0 && chan >=0 && ctrl >= 0) +        // Add will replace if found. +        macm->add_ctrl_struct(port, chan, ctrl, MusECore::MidiAudioCtrlStruct(id)); +       +      MusEGlobal::audio->msgIdle(false); +    } +     +    delete pup; +    return; +  } +      if (colindex > 100)        return; // this was meant for changeAutomation                // one of these days I'll rewrite this so it's understandable @@ -1461,7 +1537,10 @@ void TList::changeAutomationColor(QAction* act)  //---------------------------------------------------------  PopupMenu* TList::colorMenu(QColor c, int id, QWidget* parent)  { -  PopupMenu * m = new PopupMenu(parent);  //, true);  //TODO +  PopupMenu * m = new PopupMenu(parent, true);   + +  QActionGroup* col_actgrp = new QActionGroup(m); +  col_actgrp->setExclusive(true);    for (int i = 0; i< 6; i++) {      QPixmap pix(10,10);      QPainter p(&pix); @@ -1469,14 +1548,52 @@ PopupMenu* TList::colorMenu(QColor c, int id, QWidget* parent)      p.setPen(Qt::black);      p.drawRect(0,0,10,10);      QIcon icon(pix); -    QAction *act = m->addAction(icon,""); +    QAction *act = col_actgrp->addAction(icon,"");      act->setCheckable(true);      if (c == collist[i])          act->setChecked(true); -    int data = id * 256; // shift 8 bits -    data += i; // color in the bottom 8 bits -    act->setData(data); +    act->setData((id<<8) + i); // Shift 8 bits. Color in the bottom 8 bits.     } +  m->addActions(col_actgrp->actions()); +   +  //m->addSeparator(); +  m->addAction(new MenuTitleItem(tr("Midi control"), m)); +   +  if(editAutomation && !editAutomation->isMidiTrack())  +  { +    QAction *act = m->addAction(tr("Assign")); +    act->setCheckable(false); +    act->setData((id<<8) + 255); // Shift 8 bits. Make midi menu the last item at 255. +     +    MusECore::AudioTrack* track = static_cast<MusECore::AudioTrack*>(editAutomation); +    MusECore::MidiAudioCtrlMap* macm = track->controller()->midiControls(); +    MusECore::AudioMidiCtrlStructMap amcs; +    macm->find_audio_ctrl_structs(id, &amcs); +     +    // Group only the clear and status items so they can both be easily removed when clear is clicked. +    if(!amcs.empty()) +    { +      QActionGroup* midi_actgrp = new QActionGroup(m); +      QAction *cact = midi_actgrp->addAction(tr("Clear")); +      cact->setData((id<<8) + 254); // Shift 8 bits. Make clear the second-last item at 254 +      for(MusECore::iAudioMidiCtrlStructMap iamcs = amcs.begin(); iamcs != amcs.end(); ++iamcs) +      { +        int port, chan, mctrl; +        macm->hash_values((*iamcs)->first, &port, &chan, &mctrl); +        //QString s = QString("Port:%1 Chan:%2 Ctl:%3-%4").arg(port + 1) +        QString s = QString("Port:%1 Chan:%2 Ctl:%3").arg(port + 1) +                                                    .arg(chan + 1) +                                                    //.arg((mctrl >> 8) & 0xff) +                                                    //.arg(mctrl & 0xff); +                                                    .arg(MusECore::midiCtrlName(mctrl, true)); +        QAction *mact = midi_actgrp->addAction(s); +        mact->setEnabled(false); +        mact->setData(-1); // Not used +      } +      m->addActions(midi_actgrp->actions()); +    } +  } +      connect(m, SIGNAL(triggered(QAction*)), SLOT(changeAutomationColor(QAction*)));    return m; @@ -1611,14 +1728,53 @@ void TList::mousePressEvent(QMouseEvent* ev)                      p->setTitle(tr("Viewable automation"));                      MusECore::CtrlListList* cll = ((MusECore::AudioTrack*)t)->controller();                      QAction* act = 0; +                    int last_rackpos = -1; +                    bool internal_found = false; +                    bool synth_found = false;                      for(MusECore::CtrlListList::iterator icll =cll->begin();icll!=cll->end();++icll) {                        MusECore::CtrlList *cl = icll->second;                        if (cl->dontShow())                          continue; +                       +                      int ctrl = cl->id(); +                       +                      if(ctrl < AC_PLUGIN_CTL_BASE) +                      { +                        if(!internal_found) +                          p->addAction(new MusEGui::MenuTitleItem(tr("Internal"), p));  +                        internal_found = true; +                      } +                      else +                      { +                        if(ctrl < (int)MusECore::genACnum(MAX_PLUGINS, 0))  // The beginning of the special dssi synth controller block.              +                        { +                          int rackpos = (ctrl - AC_PLUGIN_CTL_BASE) >> AC_PLUGIN_CTL_BASE_POW; +                          if(rackpos < PipelineDepth) +                          { +                            if(rackpos != last_rackpos) +                            { +                              QString s = ((MusECore::AudioTrack*)t)->efxPipe()->name(rackpos); +                              p->addAction(new MusEGui::MenuTitleItem(s, p));  +                            } +                            last_rackpos = rackpos; +                          } +                        } +                        else +                        { +                          if(t->type() == MusECore::Track::AUDIO_SOFTSYNTH) +                          { +                            if(!synth_found) +                              p->addAction(new MusEGui::MenuTitleItem(tr("Synth"), p));  +                            synth_found = true; +                          } +                        } +                      } +                                              act = p->addAction(cl->name());                        act->setCheckable(true);                        act->setChecked(cl->isVisible()); -                      int data = cl->id() * 256; // shift 8 bits +                       +                      int data = ctrl<<8; // shift 8 bits                        data += 150; // illegal color > 100                        act->setData(data);                        PopupMenu *m = colorMenu(cl->color(), cl->id(), p); diff --git a/muse2/muse/audio.cpp b/muse2/muse/audio.cpp index cbcbd922..6349971b 100644 --- a/muse2/muse/audio.cpp +++ b/muse2/muse/audio.cpp @@ -45,6 +45,19 @@  #include "pos.h"  #include "ticksynth.h" +// Experimental for now - allow other Jack timebase masters to control our midi engine. +// TODO: Be friendly to other apps and ask them to be kind to us by using jack_transport_reposition.  +//       It is actually required IF we want the extra position info to show up +//        in the sync callback, otherwise we get just the frame only. +//       This information is shared on the server, it is directly passed around.  +//       jack_transport_locate blanks the info from sync until the timebase callback reads  +//        it again right after, from some timebase master.  +//       Sadly not many of us use jack_transport_reposition. So we need to work around it ! +//#define _JACK_TIMEBASE_DRIVES_MIDI_ + +#ifdef _JACK_TIMEBASE_DRIVES_MIDI_ +#include "jackaudio.h"   +#endif  namespace MusEGlobal {  MusECore::Audio* audio; @@ -102,6 +115,7 @@ const char* seqMsgList[] = {        "AUDIO_ADD_AC_EVENT",        "AUDIO_CHANGE_AC_EVENT",        "AUDIO_SET_SOLO", "AUDIO_SET_SEND_METRONOME",  +      "AUDIO_START_MIDI_LEARN",        "MS_PROCESS", "MS_STOP", "MS_SET_RTC", "MS_UPDATE_POLL_FD",        "SEQM_IDLE", "SEQM_SEEK"        }; @@ -127,6 +141,10 @@ Audio::Audio()        _pos.setType(Pos::FRAMES);        _pos.setFrame(0); +#ifdef _AUDIO_USE_TRUE_FRAME_ +      _previousPos.setType(Pos::FRAMES); +      _previousPos.setFrame(0); +#endif        nextTickPos = curTickPos = 0;        midiClick     = 0; @@ -143,6 +161,8 @@ Audio::Audio()        startRecordPos.setType(Pos::FRAMES);  // Tim        endRecordPos.setType(Pos::FRAMES); +      startExternalRecTick = 0; +      endExternalRecTick = 0;        _audioMonitor = 0;        _audioMaster  = 0; @@ -384,7 +404,10 @@ void Audio::process(unsigned frames)              (*i)->processInit(frames);        int samplePos = _pos.frame();        int offset    = 0;      // buffer offset in audio buffers - +#ifdef _JACK_TIMEBASE_DRIVES_MIDI_               +      bool use_jack_timebase = false; +#endif +              if (isPlaying()) {              if (!freewheel())                    MusEGlobal::audioPrefetch->msgTick(); @@ -394,7 +417,22 @@ void Audio::process(unsigned frames)                    write(sigFd, "F", 1);                    return;                    } - +                   +#ifdef _JACK_TIMEBASE_DRIVES_MIDI_ +            unsigned curr_jt_tick, next_jt_ticks; +            use_jack_timebase =  +                MusEGlobal::audioDevice->deviceType() == AudioDevice::JACK_AUDIO &&  +                !MusEGlobal::jackTransportMaster &&  +                !MusEGlobal::song->masterFlag() && +                !MusEGlobal::extSyncFlag.value() && +                static_cast<MusECore::JackAudioDevice*>(MusEGlobal::audioDevice)->timebaseQuery( +                  frames, NULL, NULL, NULL, &curr_jt_tick, &next_jt_ticks); +            // NOTE: I would rather trust the reported current tick than rely solely on the stream of  +            // tempos to correctly advance to the next position (which did actually test OK anyway). +            if(use_jack_timebase) +              curTickPos = curr_jt_tick; +#endif +                          //              //  check for end of song              // @@ -452,10 +490,19 @@ void Audio::process(unsigned frames)              }              else              { -               -              Pos ppp(_pos); -              ppp += frames; -              nextTickPos = ppp.tick(); + +#ifdef _JACK_TIMEBASE_DRIVES_MIDI_               +              if(use_jack_timebase) +                // With jack timebase this might not be accurate - +                //  we are relying on the tempo to figure out the next tick. +                nextTickPos = curTickPos + next_jt_ticks; +              else +#endif                 +              { +                Pos ppp(_pos); +                ppp += frames; +                nextTickPos = ppp.tick(); +              }              }            }        // @@ -468,9 +515,15 @@ void Audio::process(unsigned frames)        process1(samplePos, offset, frames);        for (iAudioOutput i = ol->begin(); i != ol->end(); ++i)              (*i)->processWrite(); +       +#ifdef _AUDIO_USE_TRUE_FRAME_ +      _previousPos = _pos; +#endif        if (isPlaying()) {              _pos += frames; -            curTickPos = nextTickPos; +            // With jack timebase this might not be accurate if we  +            //  set curTickPos (above) from the reported current tick. +            curTickPos = nextTickPos;               }        } @@ -626,6 +679,13 @@ void Audio::processMsg(AudioMsg* msg)                    msg->snode->setSendMetronome((bool)msg->ival);                    break; +            case AUDIO_START_MIDI_LEARN: +                  // Reset the values. The engine will fill these from driver events. +                  MusEGlobal::midiLearnPort = -1; +                  MusEGlobal::midiLearnChan = -1; +                  MusEGlobal::midiLearnCtrl = -1; +                  break; +                          case AUDIO_SET_SEG_SIZE:                    MusEGlobal::segmentSize = msg->ival;                    MusEGlobal::sampleRate  = msg->iival; @@ -690,6 +750,9 @@ void Audio::processMsg(AudioMsg* msg)                    MusEGlobal::song->processMsg(msg);                    if (isPlaying()) {                          if (!MusEGlobal::checkAudioDevice()) return; +#ifdef _AUDIO_USE_TRUE_FRAME_ +                        _previousPos = _pos; +#endif                          _pos.setTick(curTickPos);                          int samplePos = _pos.frame();                          syncFrame     = MusEGlobal::audioDevice->framePos(); @@ -742,10 +805,25 @@ void Audio::seek(const Pos& p)        if (MusEGlobal::heavyDebugMsg)          printf("Audio::seek frame:%d\n", p.frame()); +#ifdef _AUDIO_USE_TRUE_FRAME_ +      _previousPos = _pos; +#endif        _pos        = p;        if (!MusEGlobal::checkAudioDevice()) return;        syncFrame   = MusEGlobal::audioDevice->framePos();        frameOffset = syncFrame - _pos.frame(); +       +#ifdef _JACK_TIMEBASE_DRIVES_MIDI_ +      unsigned curr_jt_tick; +      if(MusEGlobal::audioDevice->deviceType() == AudioDevice::JACK_AUDIO &&  +         !MusEGlobal::jackTransportMaster &&  +         !MusEGlobal::song->masterFlag() && +         !MusEGlobal::extSyncFlag.value() && +         static_cast<MusECore::JackAudioDevice*>(MusEGlobal::audioDevice)->timebaseQuery( +             MusEGlobal::segmentSize, NULL, NULL, NULL, &curr_jt_tick, NULL)) +        curTickPos = curr_jt_tick; +      else +#endif        curTickPos  = _pos.tick();  // ALSA support        @@ -802,6 +880,7 @@ void Audio::startRolling()        if(_loopCount == 0) {          startRecordPos = _pos; +        startExternalRecTick = curTickPos;        }        if (MusEGlobal::song->record()) {              recording      = true; @@ -916,6 +995,7 @@ void Audio::stopRolling()              }        recording    = false;        endRecordPos = _pos; +      endExternalRecTick = curTickPos;        write(sigFd, "0", 1);   // STOP        } @@ -926,8 +1006,10 @@ void Audio::stopRolling()  void Audio::recordStop()        { +      MusEGlobal::song->processMasterRec();    +                if (MusEGlobal::debugMsg) -        printf("recordStop - startRecordPos=%d\n", startRecordPos.tick()); +        printf("recordStop - startRecordPos=%d\n", MusEGlobal::extSyncFlag.value() ? startExternalRecTick : startRecordPos.tick());        MusEGlobal::audio->msgIdle(true); // gain access to all data structures @@ -937,7 +1019,7 @@ void Audio::recordStop()        for (iWaveTrack it = wl->begin(); it != wl->end(); ++it) {              WaveTrack* track = *it;              if (track->recordFlag() || MusEGlobal::song->bounceTrack == track) { -                  MusEGlobal::song->cmdAddRecordedWave(track, startRecordPos, endRecordPos); +                  MusEGlobal::song->cmdAddRecordedWave(track, startRecordPos, endRecordPos);                       // The track's _recFile pointer may have been kept and turned                    //  into a SndFileR and added to a new part.                    // Or _recFile may have been discarded (no new recorded part created). @@ -960,7 +1042,8 @@ void Audio::recordStop()              // Do SysexMeta. Do loops.              buildMidiEventList(el, mpel, mt, MusEGlobal::config.division, true, true); -            MusEGlobal::song->cmdAddRecordedEvents(mt, el, startRecordPos.tick()); +            MusEGlobal::song->cmdAddRecordedEvents(mt, el,  +                 MusEGlobal::extSyncFlag.value() ? startExternalRecTick : startRecordPos.tick());              el->clear();              mpel->clear();              } @@ -981,6 +1064,7 @@ void Audio::recordStop()            msgSetRecord(ao, false);          }        }   +           MusEGlobal::audio->msgIdle(false);        MusEGlobal::song->endUndo(0);        MusEGlobal::song->setRecord(false); @@ -993,7 +1077,11 @@ void Audio::recordStop()  unsigned Audio::framesSinceCycleStart() const  { -  return lrint((curTime() - syncTime) * MusEGlobal::sampleRate); +  unsigned f =  lrint((curTime() - syncTime) * MusEGlobal::sampleRate); +  // Safety due to inaccuracies. It cannot be after the segment, right? +  if(f >= MusEGlobal::segmentSize) +    f = MusEGlobal::segmentSize - 1; +  return f;  }  //--------------------------------------------------------- diff --git a/muse2/muse/audio.h b/muse2/muse/audio.h index a9d2cc82..7c3d73ce 100644 --- a/muse2/muse/audio.h +++ b/muse2/muse/audio.h @@ -31,6 +31,12 @@  #include "route.h"  #include "event.h" +// An experiment to use true frames for time-stamping all recorded input.  +// (All recorded data actually arrived in the previous period.) +// TODO: Some more work needs to be done in WaveTrack::getData() in order to +//  make everything line up and sync correctly. Cannot use this yet! +//#define _AUDIO_USE_TRUE_FRAME_ +  namespace MusECore {  class AudioDevice;  class AudioTrack; @@ -94,6 +100,7 @@ enum {        AUDIO_ADD_AC_EVENT,        AUDIO_CHANGE_AC_EVENT,        AUDIO_SET_SOLO, AUDIO_SET_SEND_METRONOME,  +      AUDIO_START_MIDI_LEARN,        MS_PROCESS, MS_STOP, MS_SET_RTC, MS_UPDATE_POLL_FD,        SEQM_IDLE, SEQM_SEEK,        }; @@ -147,7 +154,11 @@ class Audio {        int _loopCount;         // Number of times we have looped so far        Pos _pos;               // current play position - +       +#ifdef _AUDIO_USE_TRUE_FRAME_ +      Pos _previousPos;       // previous play position +#endif +              unsigned curTickPos;   // pos at start of frame during play/record        unsigned nextTickPos;  // pos at start of next frame during play/record @@ -172,7 +183,8 @@ class Audio {        // record values:        Pos startRecordPos;        Pos endRecordPos; - +      unsigned startExternalRecTick; +      unsigned endExternalRecTick;        AudioOutput* _audioMaster;        AudioOutput* _audioMonitor; @@ -288,6 +300,7 @@ class Audio {        void msgRemapPortDrumCtlEvents(int, int, int, int);        void msgChangeAllPortDrumCtrlEvents(bool, bool);        void msgSetSendMetronome(AudioTrack*, bool); +      void msgStartMidiLearn();        void msgPlayMidiEvent(const MidiPlayEvent* event);        void rescanAlsaPorts(); @@ -295,8 +308,13 @@ class Audio {        void midiPortsChanged();        const Pos& pos() const { return _pos; } +#ifdef _AUDIO_USE_TRUE_FRAME_ +      const Pos& previousPos() const { return _previousPos; } +#endif        const Pos& getStartRecordPos() const { return startRecordPos; }        const Pos& getEndRecordPos() const { return endRecordPos; } +      unsigned getStartExternalRecTick() const { return startExternalRecTick; } +      unsigned getEndExternalRecTick() const { return endExternalRecTick; }        int loopCount() { return _loopCount; }         // Number of times we have looped so far        unsigned loopFrame() { return _loopFrame; }           diff --git a/muse2/muse/audiotrack.cpp b/muse2/muse/audiotrack.cpp index b0c52a54..dac496d7 100644 --- a/muse2/muse/audiotrack.cpp +++ b/muse2/muse/audiotrack.cpp @@ -39,6 +39,7 @@  #include "synth.h"  #include "dssihost.h"  #include "app.h" +#include "controlfifo.h"  namespace MusECore { @@ -385,6 +386,10 @@ void AudioTrack::addController(CtrlList* list)  void AudioTrack::removeController(int id)        { +      AudioMidiCtrlStructMap amcs; +      _controller.midiControls()->find_audio_ctrl_structs(id, &amcs);   +      for(ciAudioMidiCtrlStructMap iamcs = amcs.begin(); iamcs != amcs.end(); ++ iamcs) +        _controller.midiControls()->erase(*iamcs);        iCtrlList i = _controller.find(id);        if (i == _controller.end()) {              printf("AudioTrack::removeController id %d not found\n", id); @@ -399,20 +404,14 @@ void AudioTrack::removeController(int id)  void AudioTrack::swapControllerIDX(int idx1, int idx2)  { -  // FIXME This code is ugly. -  // At best we would like to modify the keys (IDXs) in-place and -  //  do some kind of deferred re-sort, but it can't be done... -   -  if(idx1 == idx2) -    return; -     -  if(idx1 < 0 || idx2 < 0 || idx1 >= PipelineDepth || idx2 >= PipelineDepth) +  if(idx1 == idx2 || idx1 < 0 || idx2 < 0 || idx1 >= PipelineDepth || idx2 >= PipelineDepth)      return;    CtrlList *cl;    CtrlList *newcl;    int id1 = (idx1 + 1) * AC_PLUGIN_CTL_BASE;    int id2 = (idx2 + 1) * AC_PLUGIN_CTL_BASE; +  int id_mask = ~((int)AC_PLUGIN_CTL_ID_MASK);    int i, j;    CtrlListList tmpcll; @@ -422,7 +421,7 @@ void AudioTrack::swapControllerIDX(int idx1, int idx2)    {      cl = icl->second;      i = cl->id() & AC_PLUGIN_CTL_ID_MASK; -    j = cl->id() & ~((unsigned long)AC_PLUGIN_CTL_ID_MASK); +    j = cl->id() & id_mask;      if(j == id1 || j == id2)      {        newcl = new CtrlList(i | (j == id1 ? id2 : id1)); @@ -460,74 +459,21 @@ void AudioTrack::swapControllerIDX(int idx1, int idx2)      _controller.insert(std::pair<const int, CtrlList*>(newcl->id(), newcl));    }  -  // DELETETHIS 67 -  /* -  unsigned int idmask = ~AC_PLUGIN_CTL_ID_MASK; -   -  CtrlList* cl; -  CtrlList* ctl1 = 0; -  CtrlList* ctl2 = 0; -  CtrlList* newcl1 = 0; -  CtrlList* newcl2 = 0; -  CtrlVal cv(0, 0.0); -  int id1 = (idx1 + 1) * AC_PLUGIN_CTL_BASE; -  int id2 = (idx2 + 1) * AC_PLUGIN_CTL_BASE; -  int i, j; -  double min, max; -   -  for(ciCtrlList icl = _controller.begin(); icl != _controller.end(); ++icl)  +  // Remap midi to audio controls... +  MidiAudioCtrlMap* macm = _controller.midiControls(); +  for(iMidiAudioCtrlMap imacm = macm->begin(); imacm != macm->end(); ++imacm)    { -    cl = icl->second; -    i = cl->id() & AC_PLUGIN_CTL_ID_MASK; -    j = cl->id() & idmask; -     -    if(j == id1) -    { -      ctl1 = cl; -      newcl1 = new CtrlList( i | id2 ); -      newcl1->setMode(cl->mode()); -      newcl1->setValueType(cl->valueType()); -      newcl1->setName(cl->name()); -      cl->range(&min, &max); -      newcl1->setRange(min, max); -      newcl1->setCurVal(cl->curVal()); -      newcl1->setDefault(cl->getDefault()); -      for(iCtrl ic = cl->begin(); ic != cl->end(); ++ic)  -      { -        cv = ic->second; -        newcl1->insert(std::pair<const int, CtrlVal>(cv.frame, cv)); -      } -    } -    //else   -    if(j == id2) -    { -      ctl2 = cl; -      newcl2 = new CtrlList( i | id1 ); -      newcl2->setMode(cl->mode()); -      newcl2->setValueType(cl->valueType()); -      newcl2->setName(cl->name()); -      cl->range(&min, &max); -      newcl2->setRange(min, max); -      newcl2->setCurVal(cl->curVal()); -      newcl2->setDefault(cl->getDefault()); -      for(iCtrl ic = cl->begin(); ic != cl->end(); ++ic)  -      { -        cv = ic->second; -        newcl2->insert(std::pair<const int, CtrlVal>(cv.frame, cv)); -      } -    } -  }   -  if(ctl1) -    _controller.erase(ctl1->id()); -  if(ctl2) -    _controller.erase(ctl2->id()); -  if(newcl1) -    //_controller.add(newcl1); -    _controller.insert(std::pair<const int, CtrlList*>(newcl1->id(), newcl1)); -  if(newcl2) -    _controller.insert(std::pair<const int, CtrlList*>(newcl2->id(), newcl2)); -    //_controller.add(newcl2); -  */   +    int actrl = imacm->second.audioCtrlId(); +    int id = actrl & id_mask; +    actrl &= AC_PLUGIN_CTL_ID_MASK; +    if(id == id1) +      actrl |= id2; +    else if(id == id2) +      actrl |= id1; +    else +      continue; +    imacm->second.setAudioCtrlId(actrl); +  }  }  //--------------------------------------------------------- @@ -610,11 +556,7 @@ void AudioTrack::processAutomationEvents()              if(icr->id == id && icr->type == ARVT_STOP)               {                int end = icr->frame; -              // Erase everything up to, not including, this stop event's frame. -              // Because an event was already stored directly when slider released. -              if(end > start) -                --end; -                   +                              iCtrl s = cl->lower_bound(start);                iCtrl e = cl->lower_bound(end); @@ -636,8 +578,21 @@ void AudioTrack::processAutomationEvents()      //  from CtrlRecList and put into cl.      for (iCtrlRec icr = _recEvents.begin(); icr != _recEvents.end(); ++icr)       { -          if (icr->id == id && (icr->type == ARVT_VAL || icr->type == ARVT_START)) +          if (icr->id == id) +          { +                // Must optimize these types otherwise multiple vertices appear on flat straight lines in the graphs. +                CtrlValueType vtype = cl->valueType(); +                if(!cl->empty() && (cl->mode() == CtrlList::DISCRETE || vtype == VAL_BOOL || vtype == VAL_INT)) +                { +                  iCtrl icl_prev = cl->lower_bound(icr->frame); +                  if(icl_prev != cl->begin()) +                    --icl_prev; +                  if(icl_prev->second.val == icr->val) +                    continue; +                }   +                // Now add the value.                  cl->add(icr->frame, icr->val); +          }      }    } @@ -790,7 +745,7 @@ void AudioTrack::changeACEvent(int id, int frame, int newframe, double newval)    iCtrl ic = cl->find(frame);     if(ic != cl->end())      cl->erase(ic); -  cl->insert(std::pair<const int, CtrlVal> (newframe, CtrlVal(newframe, newval)));       +  cl->insert(std::pair<const int, CtrlVal> (newframe, CtrlVal(newframe, newval)));  }  //--------------------------------------------------------- @@ -825,7 +780,7 @@ void AudioTrack::setVolume(double val)  double AudioTrack::pan() const        {        return _controller.value(AC_PAN, MusEGlobal::audio->curFramePos(),  -                               !MusEGlobal::automation || automationType() == AUTO_OFF || !_volumeEnCtrl || !_volumeEn2Ctrl); +                               !MusEGlobal::automation || automationType() == AUTO_OFF || !_panEnCtrl || !_panEn2Ctrl);        }  //--------------------------------------------------------- @@ -848,8 +803,49 @@ void AudioTrack::setPan(double val)  double AudioTrack::pluginCtrlVal(int ctlID) const        { +      bool en_1 = true, en_2 = true; +      if(ctlID < AC_PLUGIN_CTL_BASE)   +      { +        if(ctlID == AC_VOLUME) +        { +          en_1 = _volumeEnCtrl;  +          en_2 = _volumeEn2Ctrl; +        } +        else +        if(ctlID == AC_PAN) +        { +          en_1 = _panEnCtrl;  +          en_2 = _panEn2Ctrl; +        } +      } +      else +      { +        if(ctlID < (int)genACnum(MAX_PLUGINS, 0))  // The beginning of the special dssi synth controller block.              +        { +          _efxPipe->controllersEnabled(ctlID, &en_1, &en_2);  +        } +        else +        { +          if(type() == AUDIO_SOFTSYNTH) +          { +            const SynthI* synth = static_cast<const SynthI*>(this); +            if(synth->synth() && synth->synth()->synthType() == Synth::DSSI_SYNTH) +            { +              SynthIF* sif = synth->sif(); +              if(sif) +              { +                const DssiSynthIF* dssi_sif = static_cast<const DssiSynthIF*>(sif); +                int in_ctrl_idx = ctlID & AC_PLUGIN_CTL_ID_MASK; +                en_1 = dssi_sif->controllerEnabled(in_ctrl_idx); +                en_2 = dssi_sif->controllerEnabled2(in_ctrl_idx); +              } +            } +          } +        } +      }   +                    return _controller.value(ctlID, MusEGlobal::audio->curFramePos(),  -                               !MusEGlobal::automation || automationType() == AUTO_OFF); +                               !MusEGlobal::automation || automationType() == AUTO_OFF || !en_1 || !en_2);        }  //--------------------------------------------------------- @@ -865,6 +861,140 @@ void AudioTrack::setPluginCtrlVal(int param, double val)    cl->second->setCurVal(val);  } +//--------------------------------------------------------- +//   addScheduledControlEvent +//   returns true if event cannot be delivered +//--------------------------------------------------------- + +bool AudioTrack::addScheduledControlEvent(int track_ctrl_id, float val, unsigned frame)  +{ +  if(track_ctrl_id < AC_PLUGIN_CTL_BASE)  // FIXME: These controllers (three so far - vol, pan, mute) have no vari-run-length support. +  { +    iCtrlList icl = _controller.find(track_ctrl_id); +    if(icl == _controller.end()) +      return true; +    icl->second->setCurVal(val); +    return false; +  } +  else +  { +    if(track_ctrl_id < (int)genACnum(MAX_PLUGINS, 0))  // The beginning of the special dssi synth controller block.              +      return _efxPipe->addScheduledControlEvent(track_ctrl_id, val, frame); +    else +    { +      if(type() == AUDIO_SOFTSYNTH) +      { +        const SynthI* synth = static_cast<const SynthI*>(this); +        if(synth->synth() && synth->synth()->synthType() == Synth::DSSI_SYNTH) +        { +          SynthIF* sif = synth->sif(); +          if(sif) +          { +            DssiSynthIF* dssi_sif = static_cast<DssiSynthIF*>(sif); +            int in_ctrl_idx = track_ctrl_id & AC_PLUGIN_CTL_ID_MASK; +            return dssi_sif->addScheduledControlEvent(in_ctrl_idx, val, frame); +          } +        } +      } +    } +  }   +  return true; +} + +//--------------------------------------------------------- +//   enableController +//   Enable or disable gui controls.  +//   Used during automation recording to inhibit gui controls  +//    from playback controller stream +//--------------------------------------------------------- + +void AudioTrack::enableController(int track_ctrl_id, bool en)  +{ +  if(track_ctrl_id < AC_PLUGIN_CTL_BASE)   +  { +    if(track_ctrl_id == AC_VOLUME) +      enableVolumeController(en); +    else +    if(track_ctrl_id == AC_PAN) +      enablePanController(en); +  } +  else +  { +    if(track_ctrl_id < (int)genACnum(MAX_PLUGINS, 0))  // The beginning of the special dssi synth controller block.              +      _efxPipe->enableController(track_ctrl_id, en); +    else +    { +      if(type() == AUDIO_SOFTSYNTH) +      { +        SynthI* synth = static_cast<SynthI*>(this); +        if(synth->synth() && synth->synth()->synthType() == Synth::DSSI_SYNTH) +        { +          SynthIF* sif = synth->sif(); +          if(sif) +          { +            DssiSynthIF* dssi_sif = static_cast<DssiSynthIF*>(sif); +            int in_ctrl_idx = track_ctrl_id & AC_PLUGIN_CTL_ID_MASK; +            dssi_sif->enableController(in_ctrl_idx, en); +          } +        } +      } +    } +  }   +} + +//--------------------------------------------------------- +//   controllersEnabled +//--------------------------------------------------------- + +void AudioTrack::controllersEnabled(int track_ctrl_id, bool* en1, bool* en2) const +      { +      bool en_1 = true, en_2 = true; +      if(track_ctrl_id < AC_PLUGIN_CTL_BASE)   +      { +        if(track_ctrl_id == AC_VOLUME) +        { +          en_1 = _volumeEnCtrl;  +          en_2 = _volumeEn2Ctrl; +        } +        else +        if(track_ctrl_id == AC_PAN) +        { +          en_1 = _panEnCtrl;  +          en_2 = _panEn2Ctrl; +        } +      } +      else +      { +        if(track_ctrl_id < (int)genACnum(MAX_PLUGINS, 0))  // The beginning of the special dssi synth controller block.              +        { +          _efxPipe->controllersEnabled(track_ctrl_id, &en_1, &en_2);  +        } +        else +        { +          if(type() == AUDIO_SOFTSYNTH) +          { +            const SynthI* synth = static_cast<const SynthI*>(this); +            if(synth->synth() && synth->synth()->synthType() == Synth::DSSI_SYNTH) +            { +              SynthIF* sif = synth->sif(); +              if(sif) +              { +                const DssiSynthIF* dssi_sif = static_cast<const DssiSynthIF*>(sif); +                int in_ctrl_idx = track_ctrl_id & AC_PLUGIN_CTL_ID_MASK; +                en_1 = dssi_sif->controllerEnabled(in_ctrl_idx); +                en_2 = dssi_sif->controllerEnabled2(in_ctrl_idx); +              } +            } +          } +        } +      }   +         +      if(en1) +        *en1 = en_1; +      if(en2) +        *en2 = en_2; +      } +  void AudioTrack::recordAutomation(int n, double v)        {          if(!MusEGlobal::automation) @@ -883,7 +1013,7 @@ void AudioTrack::recordAutomation(int n, double v)              if (cl == _controller.end())                 return;              // Add will replace if found. -            cl->second->add(MusEGlobal::audio->curFramePos(), v);      +            cl->second->add(MusEGlobal::audio->curFramePos(), v);               }            }        } @@ -895,10 +1025,10 @@ void AudioTrack::startAutoRecord(int n, double v)          if(MusEGlobal::audio->isPlaying())          {            if(automationType() == AUTO_TOUCH) -              _recEvents.push_back(CtrlRecVal(MusEGlobal::audio->curFramePos(), n, v, ARVT_START));   -          else     +              _recEvents.push_back(CtrlRecVal(MusEGlobal::audio->curFramePos(), n, v, ARVT_START)); +          else            if(automationType() == AUTO_WRITE) -              _recEvents.push_back(CtrlRecVal(MusEGlobal::audio->curFramePos(), n, v));     +              _recEvents.push_back(CtrlRecVal(MusEGlobal::audio->curFramePos(), n, v));          }           else          { @@ -926,7 +1056,7 @@ void AudioTrack::stopAutoRecord(int n, double v)          {            if(automationType() == AUTO_TOUCH)            { -              MusEGlobal::audio->msgAddACEvent(this, n, MusEGlobal::audio->curFramePos(), v);         +              MusEGlobal::audio->msgAddACEvent(this, n, MusEGlobal::audio->curFramePos(), v);                _recEvents.push_back(CtrlRecVal(MusEGlobal::audio->curFramePos(), n, v, ARVT_STOP));               }             }  @@ -953,26 +1083,7 @@ void AudioTrack::writeProperties(int level, Xml& xml) const              if (*ip)                    (*ip)->writeConfiguration(level, xml);              } -      for (ciCtrlList icl = _controller.begin(); icl != _controller.end(); ++icl) { -            const CtrlList* cl = icl->second; - -            QString s= QString("controller id=\"%1\" cur=\"%2\"").arg(cl->id()).arg(cl->curVal()).toAscii().constData(); -            s += QString(" color=\"%1\" visible=\"%2\"").arg(cl->color().name()).arg(cl->isVisible()); -            xml.tag(level++, s.toAscii().constData()); -            int i = 0; -            for (ciCtrl ic = cl->begin(); ic != cl->end(); ++ic) { -                  QString s("%1 %2, "); -                  xml.nput(level, s.arg(ic->second.frame).arg(ic->second.val).toAscii().constData()); -                  ++i; -                  if (i >= 4) { -                        xml.put(level, ""); -                        i = 0; -                        } -                  } -            if (i) -                  xml.put(level, ""); -            xml.etag(level--, "controller"); -            } +      _controller.write(level, xml);                    }  //--------------------------------------------------------- @@ -1113,6 +1224,8 @@ bool AudioTrack::readProperties(Xml& xml, const QString& tag)                    l->setMode(p->ctrlMode(m));                    }               } +      else if (tag == "midiMapper")  +            _controller.midiControls()->read(xml);        else              return Track::readProperties(xml, tag);        return false; @@ -1230,20 +1343,20 @@ void AudioTrack::mapRackPluginsToControllers()        unsigned param = id & AC_PLUGIN_CTL_ID_MASK;            int idx = (id >> AC_PLUGIN_CTL_BASE_POW) - 1; -      PluginIBase* p = 0; +      const PluginIBase* p = 0;        if(idx >= 0 && idx < PipelineDepth)          p = (*_efxPipe)[idx];        // Support a special block for dssi synth ladspa controllers.         else if(idx == MAX_PLUGINS && type() == AUDIO_SOFTSYNTH)            { -        SynthI* synti = dynamic_cast < SynthI* > (this); +        const SynthI* synti = dynamic_cast < const SynthI* > (this);          if(synti)          {            SynthIF* sif = synti->sif();            if(sif)            {  #ifdef DSSI_SUPPORT -            DssiSynthIF* dsif = dynamic_cast < DssiSynthIF* > (sif); +            const DssiSynthIF* dsif = dynamic_cast < const DssiSynthIF* > (sif);              if(dsif)                p = dsif;  #endif diff --git a/muse2/muse/conf.cpp b/muse2/muse/conf.cpp index 6c5c93db..cca3cb94 100644 --- a/muse2/muse/conf.cpp +++ b/muse2/muse/conf.cpp @@ -561,6 +561,23 @@ void readConfiguration(Xml& xml, bool doReadMidiPortConfig, bool doReadGlobalCon                                  if(MusEGlobal::audioDevice)                                        MusEGlobal::audioDevice->setMaster(MusEGlobal::jackTransportMaster);                                      }   +                        else if (tag == "syncRecFilterPreset") +                              { +                              int p = xml.parseInt();   +                              if(p >= 0 && p < MidiSyncInfo::TYPE_END) +                              { +                                MusEGlobal::syncRecFilterPreset = MidiSyncInfo::SyncRecFilterPresetType(p); +                                if(MusEGlobal::midiSeq) +                                  MusEGlobal::midiSeq->setSyncRecFilterPreset(MusEGlobal::syncRecFilterPreset); +                              } +                              } +                        else if (tag == "syncRecTempoValQuant") +                              { +                                double qv = xml.parseDouble(); +                                MusEGlobal::syncRecTempoValQuant = qv; +                                if(MusEGlobal::midiSeq) +                                  MusEGlobal::midiSeq->setRecTempoValQuant(qv); +                              }                          else if (tag == "mtcoffset") {                                QString qs(xml.parse1());                                QByteArray ba = qs.toLatin1(); @@ -1348,6 +1365,8 @@ void MusE::writeConfiguration(int level, MusECore::Xml& xml) const        xml.uintTag(level, "sendClockDelay", MusEGlobal::syncSendFirstClockDelay);        xml.intTag(level, "useJackTransport", MusEGlobal::useJackTransport.value());        xml.intTag(level, "jackTransportMaster", MusEGlobal::jackTransportMaster); +      xml.intTag(level, "syncRecFilterPreset", MusEGlobal::syncRecFilterPreset); +      xml.doubleTag(level, "syncRecTempoValQuant", MusEGlobal::syncRecTempoValQuant);        MusEGlobal::extSyncFlag.save(level, xml);        xml.intTag(level, "bigtimeVisible",   viewBigtimeAction->isChecked()); diff --git a/muse2/muse/confmport.cpp b/muse2/muse/confmport.cpp index 637e927e..32e1dc91 100644 --- a/muse2/muse/confmport.cpp +++ b/muse2/muse/confmport.cpp @@ -888,7 +888,7 @@ void MPConfig::rbClicked(QTableWidgetItem* item)                    #endif                        MusEGlobal::midiSeq->msgSetMidiDevice(port, sdev); -		      MusEGlobal::muse->changeConfig(true);     // save configuration file +                      MusEGlobal::muse->changeConfig(true);     // save configuration file                        // Add all track routes to/from this port...                        if(sdev) @@ -904,7 +904,7 @@ void MPConfig::rbClicked(QTableWidgetItem* item)                                MusEGlobal::audio->msgAddRoute(MusECore::Route(no, chbits), MusECore::Route(*it, chbits));                            }                            } -                        chbits = MusEGlobal::midiPorts[no].defaultOutChannels(); +//                        chbits = MusEGlobal::midiPorts[no].defaultOutChannels();                          // Turn on if and when multiple output routes are supported. DELETETHIS?                      #if 0                          for(MusECore::iMidiTrack it = mtl->begin(); it != mtl->end(); ++it) @@ -914,22 +914,27 @@ void MPConfig::rbClicked(QTableWidgetItem* item)                              MusEGlobal::audio->msgAddRoute(MusECore::Route(no, chbits), MusECore::Route(*it, chbits));                          }                        #else -                        for(int ch = 0; ch < MIDI_CHANNELS; ++ch) -                          if(chbits & (1 << ch))  -                          {     -                            MusEGlobal::audio->msgIdle(true); -                            for(MusECore::iMidiTrack it = mtl->begin(); it != mtl->end(); ++it) -                            { -                              // Leave drum track channel at current setting. -                              if((*it)->type() == MusECore::Track::DRUM) -                                (*it)->setOutPortAndUpdate(no); -                              else -                                (*it)->setOutPortAndChannelAndUpdate(no, ch); -                            }   -                            MusEGlobal::audio->msgIdle(false); -                            // Stop at the first output channel found. -                            break; -                          }    +// REMOVE Tim.                     +//                         for(int ch = 0; ch < MIDI_CHANNELS; ++ch) +//                           if(chbits & (1 << ch))  +//                           {     +//                             MusEGlobal::audio->msgIdle(true); +//                             for(MusECore::iMidiTrack it = mtl->begin(); it != mtl->end(); ++it) +//                             { +//                               // We are only interested in tracks which use this port being changed now. +//                               if((*it)->outPort() != no) +//                                 continue; +//                               // Leave drum track channel at current setting.  // REMOVE Tim. +//                               //if((*it)->type() == MusECore::Track::DRUM) +//                               //  (*it)->setOutPortAndUpdate(no); +//                               //else +//                               //  (*it)->setOutPortAndChannelAndUpdate(no, ch); +//                               (*it)->setOutPortAndUpdate(no); +//                             }   +//                             MusEGlobal::audio->msgIdle(false); +//                             // Stop at the first output channel found. +//                             break; +//                           }                         #endif                        } diff --git a/muse2/muse/ctrl.cpp b/muse2/muse/ctrl.cpp index 8071491e..d7d42770 100644 --- a/muse2/muse/ctrl.cpp +++ b/muse2/muse/ctrl.cpp @@ -6,7 +6,7 @@  //    controller handling for mixer automation  //  //  (C) Copyright 2003 Werner Schweer (ws@seh.de) -//  (C) Copyright 2011 Time E. Real (terminator356 on users dot sourceforge dot net) +//  (C) Copyright 2011-2012 Tim E. Real (terminator356 on users dot sourceforge dot net)  //  //  This program is free software; you can redistribute it and/or  //  modify it under the terms of the GNU General Public License @@ -24,15 +24,20 @@  //  //========================================================= +// Turn on debugging messages +//#define _CTRL_DEBUG_  #include <QLocale>  #include <QColor> +#include <map> + +#include <math.h>  #include "gconfig.h"  #include "fastlog.h" -#include "math.h"  #include "globals.h"  #include "ctrl.h" +#include "midictrl.h"  #include "xml.h"  namespace MusECore { @@ -48,8 +53,268 @@ void CtrlList::initColor(int i)    _visible = false;  } +//--------------------------------------------------------- +//   midi2AudioCtrlValue +//   Apply mapper if it is non-null +//--------------------------------------------------------- + +double midi2AudioCtrlValue(const CtrlList* audio_ctrl_list, const MidiAudioCtrlStruct* /*mapper*/, int midi_ctlnum, int midi_val) +{ +  double fmin, fmax; +  audio_ctrl_list->range(&fmin, &fmax); +  double frng = fmax - fmin;             // The audio control range. +   +  MidiController::ControllerType t = midiControllerType(midi_ctlnum); +  CtrlValueType aud_t = audio_ctrl_list->valueType(); +   +  #ifdef _CTRL_DEBUG_ +  printf("midi2AudioCtrlValue: midi_ctlnum:%d val:%d fmin:%f fmax:%f\n", midi_ctlnum, midi_val, fmin, fmax);   +  #endif   +   +  int ctlmn = 0; +  int ctlmx = 127; +   +  int bval = midi_val; +  switch(t)  +  { +    case MidiController::RPN: +    case MidiController::NRPN: +    case MidiController::Controller7: +      ctlmn = 0; +      ctlmx = 127; +    break; +    case MidiController::Controller14: +    case MidiController::RPN14: +    case MidiController::NRPN14: +      ctlmn = 0; +      ctlmx = 16383; +    break; +    case MidiController::Program: +      ctlmn = 0; +      ctlmx = 0xffffff; +    break; +    case MidiController::Pitch: +      ctlmn = -8192; +      ctlmx = 8191; +      bval += 8192; +    break; +    case MidiController::Velo:        // cannot happen +    default: +      break; +  } +  double fictlrng = double(ctlmx - ctlmn);   // Float version of the integer midi range. +  double normval = double(bval) / fictlrng;  // Float version of the normalized midi value. + +  // ----------  TODO: Do stuff with the mapper, if supplied. +   +  if(aud_t == VAL_LOG) +  { +    // FIXME: Although this should be correct, some sliders show "---" at top end, some don't.  +    // Possibly because of use of fast_log10 in value(), and in sliders and automation IIRC. +    fmin = 20.0*log10(fmin); +    fmax = 20.0*log10(fmax); +    frng = fmax - fmin; +    double ret = exp10((normval * frng + fmin) / 20.0); +    #ifdef _CTRL_DEBUG_ +    printf("midi2AudioCtrlValue: is VAL_LOG normval:%f frng:%f returning:%f\n", normval, frng, ret);           +    #endif +    return ret; +  } + +  if(aud_t == VAL_LINEAR) +  { +    double ret = normval * frng + fmin; +    #ifdef _CTRL_DEBUG_ +    printf("midi2AudioCtrlValue: is VAL_LINEAR normval:%f frng:%f returning:%f\n", normval, frng, ret);        +    #endif +    return ret; +  } + +  if(aud_t == VAL_INT) +  { +    double ret = int(normval * frng + fmin); +    #ifdef _CTRL_DEBUG_ +    printf("midi2AudioCtrlValue: is VAL_INT returning:%f\n", ret);    +    #endif +    return ret;   +  } +   +  if(aud_t == VAL_BOOL)  +  { +    #ifdef _CTRL_DEBUG_ +    printf("midi2AudioCtrlValue: is VAL_BOOL\n");   +    #endif +    //if(midi_val > ((ctlmx - ctlmn)/2 + ctlmn)) +    if((normval * frng + fmin) > (frng/2.0 + fmin)) +      return fmax; +    else +      return fmin; +  } +   +  printf("midi2AudioCtrlValue: unknown audio controller type:%d\n", aud_t); +  return 0.0; +}       + +//--------------------------------------------------------- +// Midi to audio controller stuff +//--------------------------------------------------------- + +MidiAudioCtrlStruct::MidiAudioCtrlStruct()   +{  +  _audio_ctrl_id = 0; +}; + +MidiAudioCtrlStruct::MidiAudioCtrlStruct(int audio_ctrl_id) : _audio_ctrl_id(audio_ctrl_id)  +{  +}; + +MidiAudioCtrlMap_idx_t MidiAudioCtrlMap::index_hash(int midi_port, int midi_chan, int midi_ctrl_num) const +{  +  return ((MidiAudioCtrlMap_idx_t(midi_port) & 0xff) << 24) |  +          ((MidiAudioCtrlMap_idx_t(midi_chan) & 0xf) << 20) |  +          (MidiAudioCtrlMap_idx_t(midi_ctrl_num) & 0xfffff);   +} + +void MidiAudioCtrlMap::hash_values(MidiAudioCtrlMap_idx_t hash, int* midi_port, int* midi_chan, int* midi_ctrl_num) const +{ +  if(midi_ctrl_num) +    *midi_ctrl_num = hash & 0xfffff; +  if(midi_chan) +    *midi_chan     = (hash >> 20) & 0xf; +  if(midi_port) +    *midi_port     = (hash >> 24) & 0xff; +} + +iMidiAudioCtrlMap MidiAudioCtrlMap::add_ctrl_struct(int midi_port, int midi_chan, int midi_ctrl_num,   +                                                    const MidiAudioCtrlStruct& macs) +{ +  MidiAudioCtrlMap_idx_t h = index_hash(midi_port, midi_chan, midi_ctrl_num); +  std::pair<iMidiAudioCtrlMap, iMidiAudioCtrlMap> range = equal_range(h); +  for(iMidiAudioCtrlMap imacp = range.first; imacp != range.second; ++imacp) +    if(imacp->second.audioCtrlId() == macs.audioCtrlId()) +       return imacp; +  return insert(std::pair<MidiAudioCtrlMap_idx_t, MidiAudioCtrlStruct >(h, macs)); +} +void MidiAudioCtrlMap::erase_ctrl_struct(int midi_port, int midi_chan, int midi_ctrl_num, int audio_ctrl_id) +{ +  MidiAudioCtrlMap_idx_t h = index_hash(midi_port, midi_chan, midi_ctrl_num); +  std::pair<iMidiAudioCtrlMap, iMidiAudioCtrlMap> range = equal_range(h); +  MidiAudioCtrlMap macm; +  macm.insert(range.first, range.second); +  for(iMidiAudioCtrlMap imacm = macm.begin(); imacm != macm.end(); ++imacm) +    if(imacm->second.audioCtrlId() == audio_ctrl_id) +       erase(imacm); +} + +void MidiAudioCtrlMap::find_audio_ctrl_structs(int audio_ctrl_id, AudioMidiCtrlStructMap* amcs) //const +{ +  for(iMidiAudioCtrlMap imacm = begin(); imacm != end(); ++imacm) +    if(imacm->second.audioCtrlId() == audio_ctrl_id) +      amcs->push_back(imacm); +} + +void MidiAudioCtrlMap::write(int level, Xml& xml) const +{ +  for(ciMidiAudioCtrlMap imacm = begin(); imacm != end();  ++imacm) +  { +      int port, chan, mctrl; +      hash_values(imacm->first, &port, &chan, &mctrl); +      int actrl = imacm->second.audioCtrlId(); +      QString s= QString("midiMapper port=\"%1\" ch=\"%2\" mctrl=\"%3\" actrl=\"%4\"") +                          .arg(port) +                          .arg(chan) +                          .arg(mctrl) +                          .arg(actrl); +      xml.tag(level++, s.toAscii().constData()); +       +      // TODO +      //const MidiAudioCtrlStruct& macs = imacs->second; +      //xml.intTag(level, "macs ???", macs.); +       +      xml.etag(level--, "midiMapper"); +  } +} + +//--------------------------------------------------------- +//   read +//--------------------------------------------------------- + +void MidiAudioCtrlMap::read(Xml& xml) +      { +      int port = -1, chan = -1, midi_ctrl = -1; +      MidiAudioCtrlStruct macs(-1); +       +      QLocale loc = QLocale::c(); +      bool ok; +      int errcount = 0; +      for (;;) { +            Xml::Token token = xml.parse(); +            const QString& tag = xml.s1(); +            switch (token) { +                  case Xml::Error: +                  case Xml::End: +                        return; +                  case Xml::Attribut: +                        if (tag == "port") +                        { +                              port = loc.toInt(xml.s2(), &ok); +                              if(!ok) +                              {  +                                ++errcount; +                                printf("MidiAudioCtrlPortMap::read failed reading port string: %s\n", xml.s2().toLatin1().constData()); +                              } +                        } +                        else if (tag == "ch") +                        { +                              chan = loc.toInt(xml.s2(), &ok); +                              if(!ok) +                              { +                                ++errcount; +                                printf("MidiAudioCtrlPortMap::read failed reading ch string: %s\n", xml.s2().toLatin1().constData()); +                              } +                        }         +                        else if (tag == "mctrl") +                        { +                              midi_ctrl = loc.toInt(xml.s2(), &ok); +                              if(!ok) +                              { +                                ++errcount; +                                printf("MidiAudioCtrlPortMap::read failed reading mctrl string: %s\n", xml.s2().toLatin1().constData()); +                              }  +                        } +                        else if (tag == "actrl") +                        { +                              macs.setAudioCtrlId(loc.toInt(xml.s2(), &ok)); +                              if(!ok) +                              { +                                ++errcount; +                                printf("MidiAudioCtrlPortMap::read failed reading actrl string: %s\n", xml.s2().toLatin1().constData()); +                              } +                        } +                        else +                              printf("unknown tag %s\n", tag.toLatin1().constData()); +                        break; +                  case Xml::TagStart: +                        // TODO +                        //if (tag == "???") {   +                        //      } +                        //else +                              xml.unknown("midiMapper"); +                        break; +                  case Xml::TagEnd: +                        if (xml.s1() == "midiMapper") +                        { +                              if(errcount == 0 && port != -1 && chan != -1 && midi_ctrl != -1 && macs.audioCtrlId() != -1) +                                  add_ctrl_struct(port, chan, midi_ctrl, macs); +                              return; +                        } +                  default: +                        break; +                  } +            } +      }  //---------------------------------------------------------  //   CtrlList  //--------------------------------------------------------- @@ -62,6 +327,7 @@ CtrlList::CtrlList()        _mode    = INTERPOLATE;        _dontShow = false;        _visible = false; +      _guiUpdatePending = false;        initColor(0);        } @@ -73,6 +339,7 @@ CtrlList::CtrlList(int id)        _mode    = INTERPOLATE;        _dontShow = false;        _visible = false; +      _guiUpdatePending = false;        initColor(id);        } @@ -88,6 +355,7 @@ CtrlList::CtrlList(int id, QString name, double min, double max, CtrlValueType v        _valueType = v;        _dontShow = dontShow;        _visible = false; +      _guiUpdatePending = false;        initColor(id);  } @@ -115,39 +383,54 @@ void CtrlList::assign(const CtrlList& l, int flags)    if(flags & ASSIGN_VALUES)    {      *this = l; // Let the vector assign values. +    _guiUpdatePending = true;    }  }  //---------------------------------------------------------  //   value +//   Returns value at frame. +//   cur_val_only means read the current 'manual' value, not from the list even if it is not empty. +//   If passed a nextFrame, sets nextFrame to the next event frame, or -1 if no next frame (wide-open), or,  +//    since CtrlList is a map, ZERO if should be replaced with some other frame by the caller (interpolation).   //--------------------------------------------------------- -double CtrlList::value(int frame) const +double CtrlList::value(int frame, bool cur_val_only, int* nextFrame) const  { -      if(empty())  +      if(cur_val_only || empty())  +      { +        if(nextFrame) +          *nextFrame = -1;          return _curVal; +      }        double rv; -      ciCtrl i = upper_bound(frame); // get the index after current frame +      int nframe; +      ciCtrl i = upper_bound(frame); // get the index after current frame        if (i == end()) { // if we are past all items just return the last value              --i; -            rv = i->second.val; +            if(nextFrame) +              *nextFrame = -1; +            return i->second.val;              }        else if(_mode == DISCRETE)        {          if(i == begin())          { +            nframe = i->second.frame;              rv = i->second.val;          }            else          {   +          nframe = i->second.frame;            --i;            rv = i->second.val;          }          } -      else { +      else {                  // INTERPOLATE          if (i == begin()) { +            nframe = i->second.frame;              rv = i->second.val;          }          else { @@ -157,6 +440,12 @@ double CtrlList::value(int frame) const              int frame1 = i->second.frame;              double val1   = i->second.val; +             +            if(val2 != val1) +              nframe = 0; // Zero signifies the next frame should be determined by caller. +            else +              nframe = frame2; +                          if (_valueType == VAL_LOG) {                val1 = 20.0*fast_log10(val1);                if (val1 < MusEGlobal::config.minSlider) @@ -166,10 +455,8 @@ double CtrlList::value(int frame) const                  val2=MusEGlobal::config.minSlider;              } -            frame -= frame1;              val2  -= val1; -            frame2 -= frame1; -            val1 += (double(frame) * val2)/double(frame2); +            val1 += (double(frame - frame1) * val2)/double(frame2 - frame1);              if (_valueType == VAL_LOG) {                val1 = exp10(val1/20.0); @@ -178,6 +465,10 @@ double CtrlList::value(int frame) const              rv = val1;            }        } + +      if(nextFrame) +          *nextFrame = nframe; +              return rv;  } @@ -196,7 +487,101 @@ double CtrlList::curVal() const  //---------------------------------------------------------  void CtrlList::setCurVal(double val)  { +#ifdef _CTRL_DEBUG_ +  printf("CtrlList::setCurVal val:%f\n", val);   +#endif +   +  bool upd = (val != _curVal);    _curVal = val; +  // If empty, any controller graphs etc. will be displaying this value. +  // Otherwise they'll be displaying the list, so update is not required. +  if(empty() && upd)      +    _guiUpdatePending = true; +} + +//--------------------------------------------------------- +// +//     Catch all insert, erase, clear etc. +// +//--------------------------------------------------------- + +CtrlList& CtrlList::operator=(const CtrlList& cl) +{ +#ifdef _CTRL_DEBUG_ +  printf("CtrlList::operator= id:%d\n", cl.id());   +#endif +  std::map<int, CtrlVal, std::less<int> >::operator=(cl); +  _guiUpdatePending = true; +  return *this; +} + +void CtrlList::swap(CtrlList& cl) +{ +#ifdef _CTRL_DEBUG_ +  printf("CtrlList::swap id:%d\n", cl.id());   +#endif +  std::map<int, CtrlVal, std::less<int> >::swap(cl); +  cl.setGuiUpdatePending(true); +  _guiUpdatePending = true; +} + +std::pair<iCtrl, bool> CtrlList::insert(const std::pair<int, CtrlVal>& p) +{ +#ifdef _CTRL_DEBUG_ +  printf("CtrlList::insert frame:%d val:%f\n", p.first, p.second.val);   +#endif +  std::pair<iCtrl, bool> res = std::map<int, CtrlVal, std::less<int> >::insert(p); +  _guiUpdatePending = true; +  return res; +} + +iCtrl CtrlList::insert(iCtrl ic, const std::pair<int, CtrlVal>& p) +{ +#ifdef _CTRL_DEBUG_ +  printf("CtrlList::insert2 frame:%d val:%f\n", p.first, p.second.val);  +#endif +  iCtrl res = std::map<int, CtrlVal, std::less<int> >::insert(ic, p); +  _guiUpdatePending = true; +  return res; +} + +void CtrlList::erase(iCtrl ictl) +{ +#ifdef _CTRL_DEBUG_ +  printf("CtrlList::erase iCtrl frame:%d val:%f\n", ictl->second.frame, ictl->second.val);   +#endif +  std::map<int, CtrlVal, std::less<int> >::erase(ictl); +  _guiUpdatePending = true; +} + +std::map<int, CtrlVal, std::less<int> >::size_type CtrlList::erase(int frame) +{ +#ifdef _CTRL_DEBUG_ +  printf("CtrlList::erase frame:%d\n", frame);   +#endif +  std::map<int, CtrlVal, std::less<int> >::size_type res = std::map<int, CtrlVal, std::less<int> >::erase(frame); +  _guiUpdatePending = true; +  return res; +} + +void CtrlList::erase(iCtrl first, iCtrl last) +{ +#ifdef _CTRL_DEBUG_ +  printf("CtrlList::erase range first frame:%d val:%f second frame:%d val:%f\n",  +         first->second.frame, first->second.val, +         last->second.frame, last->second.val);   +#endif +  std::map<int, CtrlVal, std::less<int> >::erase(first, last); +  _guiUpdatePending = true; +} + +void CtrlList::clear() +{ +#ifdef _CTRL_DEBUG_ +  printf("CtrlList::clear\n");   +#endif +  std::map<int, CtrlVal, std::less<int> >::clear(); +  _guiUpdatePending = true;  }  //--------------------------------------------------------- @@ -208,7 +593,15 @@ void CtrlList::add(int frame, double val)        {        iCtrl e = find(frame);        if (e != end()) +      { +            bool upd = (val != e->second.val);              e->second.val = val; +#ifdef _CTRL_DEBUG_ +            printf("CtrlList::add frame:%d val:%f\n", frame, val);   +#endif +            if(upd) +              _guiUpdatePending = true; +      }        else              insert(std::pair<const int, CtrlVal> (frame, CtrlVal(frame, val)));        } @@ -234,7 +627,13 @@ void CtrlList::del(int frame)  void CtrlList::updateCurValue(int frame)  { -  _curVal = value(frame); +  double v = value(frame); +  bool upd = (v != _curVal); +  _curVal = v; +  // If empty, any controller graphs etc. will be displaying this value. +  // Otherwise they'll be displaying the list, so update is not required. +  if(empty() && upd)      +    _guiUpdatePending = true;  }  //--------------------------------------------------------- @@ -361,18 +760,23 @@ void CtrlListList::add(CtrlList* vl)  //---------------------------------------------------------  //   value +//   Returns value at frame for controller with id ctrlId. +//   cur_val_only means read the current 'manual' value, not from the list even if it is not empty. +//   If passed a nextFrame, sets nextFrame to the next event frame, or -1 if no next frame (wide-open), or,  +//    since CtrlList is a map, ZERO if should be replaced with some other frame by the caller (interpolation).   //--------------------------------------------------------- -double CtrlListList::value(int ctrlId, int frame, bool cur_val_only) const +double CtrlListList::value(int ctrlId, int frame, bool cur_val_only, int* nextFrame) const             {        ciCtrlList cl = find(ctrlId);        if (cl == end()) -            return 0.0; - -      if(cur_val_only) -        return cl->second->curVal(); +      { +        if(nextFrame) +          *nextFrame = -1; +        return 0.0; +      } -      return cl->second->value(frame);   +      return cl->second->value(frame, cur_val_only, nextFrame);          }  //--------------------------------------------------------- @@ -391,5 +795,36 @@ void CtrlListList::updateCurValues(int frame)    for(ciCtrlList cl = begin(); cl != end(); ++cl)      cl->second->updateCurValue(frame);  } -       + +//--------------------------------------------------------- +//   value +//--------------------------------------------------------- + +void CtrlListList::write(int level, Xml& xml) const +{ +  for (ciCtrlList icl = begin(); icl != end(); ++icl) { +        const CtrlList* cl = icl->second; + +        QString s= QString("controller id=\"%1\" cur=\"%2\"").arg(cl->id()).arg(cl->curVal()).toAscii().constData(); +        s += QString(" color=\"%1\" visible=\"%2\"").arg(cl->color().name()).arg(cl->isVisible()); +        xml.tag(level++, s.toAscii().constData()); +        int i = 0; +        for (ciCtrl ic = cl->begin(); ic != cl->end(); ++ic) { +              QString s("%1 %2, "); +              xml.nput(level, s.arg(ic->second.frame).arg(ic->second.val).toAscii().constData()); +              ++i; +              if (i >= 4) { +                    xml.put(level, ""); +                    i = 0; +                    } +              } +        if (i) +              xml.put(level, ""); +        xml.etag(level--, "controller"); +        } +   +  _midi_controls.write(level, xml); +} + +  } // namespace MusECore diff --git a/muse2/muse/ctrl.h b/muse2/muse/ctrl.h index 687c5610..c56abe28 100644 --- a/muse2/muse/ctrl.h +++ b/muse2/muse/ctrl.h @@ -6,7 +6,7 @@  //    controller for mixer automation  //  //  (C) Copyright 2003-2004 Werner Schweer (ws@seh.de) -//  (C) Copyright 2011 Time E. Real (terminator356 on users dot sourceforge dot net) +//  (C) Copyright 2011-2012 Tim E. Real (terminator356 on users dot sourceforge dot net)  //  //  This program is free software; you can redistribute it and/or  //  modify it under the terms of the GNU General Public License @@ -29,7 +29,9 @@  #include <map>  #include <list> +#include <vector>  #include <qcolor.h> +#include <lo/lo_osc_types.h>  #define AC_PLUGIN_CTL_BASE         0x1000  #define AC_PLUGIN_CTL_BASE_POW     12 @@ -85,6 +87,47 @@ class CtrlRecList : public std::list<CtrlRecVal> {  typedef CtrlRecList::iterator iCtrlRec;  //--------------------------------------------------------- +//   MidiAudioCtrlMap +//    Describes midi control of audio controllers +//--------------------------------------------------------- + +class MidiAudioCtrlStruct { +        int _audio_ctrl_id; +  public: +        MidiAudioCtrlStruct(); +        MidiAudioCtrlStruct(int audio_ctrl_id); +        int audioCtrlId() const        { return _audio_ctrl_id; }  +        void setAudioCtrlId(int actrl) { _audio_ctrl_id = actrl; }  +      }; +       +typedef uint32_t MidiAudioCtrlMap_idx_t; + +typedef std::multimap<MidiAudioCtrlMap_idx_t, MidiAudioCtrlStruct, std::less<MidiAudioCtrlMap_idx_t> >::iterator iMidiAudioCtrlMap; +typedef std::multimap<MidiAudioCtrlMap_idx_t, MidiAudioCtrlStruct, std::less<MidiAudioCtrlMap_idx_t> >::const_iterator ciMidiAudioCtrlMap; + +// Reverse lookup based on audio control. +typedef std::vector<iMidiAudioCtrlMap>::iterator iAudioMidiCtrlStructMap; +typedef std::vector<iMidiAudioCtrlMap>::const_iterator ciAudioMidiCtrlStructMap; +class AudioMidiCtrlStructMap : public std::vector<iMidiAudioCtrlMap> { +  public: +     +     }; +     +// Midi to audio controller map.      +// The index is a hash of port, chan, and midi control number.      +class MidiAudioCtrlMap : public std::multimap<MidiAudioCtrlMap_idx_t, MidiAudioCtrlStruct, std::less<MidiAudioCtrlMap_idx_t> > { +  public: +      MidiAudioCtrlMap_idx_t index_hash(int midi_port, int midi_chan, int midi_ctrl_num) const;  +      void hash_values(MidiAudioCtrlMap_idx_t hash, int* midi_port, int* midi_chan, int* midi_ctrl_num) const;  +      iMidiAudioCtrlMap add_ctrl_struct(int midi_port, int midi_chan, int midi_ctrl_num, const MidiAudioCtrlStruct& amcs);  +      void find_audio_ctrl_structs(int audio_ctrl_id, AudioMidiCtrlStructMap* amcs); // const; +      void erase_ctrl_struct(int midi_port, int midi_chan, int midi_ctrl_num, int audio_ctrl_id); +      void write(int level, Xml& xml) const; +      void read(Xml& xml); +      }; + +       +//---------------------------------------------------------  //   CtrlList  //    arrange controller events of a specific type in a  //    list for easy retrieval @@ -109,6 +152,7 @@ class CtrlList : public std::map<int, CtrlVal, std::less<int> > {        QColor _displayColor;        bool _visible;        bool _dontShow; // when this is true the control exists but is not compatible with viewing in the arranger +      volatile bool _guiUpdatePending; // Gui heartbeat routines read this. Checked and cleared in Song::beat().        void initColor(int i);     public: @@ -117,6 +161,15 @@ class CtrlList : public std::map<int, CtrlVal, std::less<int> > {        CtrlList(int id, QString name, double min, double max, CtrlValueType v, bool dontShow=false);        void assign(const CtrlList& l, int flags);  +      void swap(CtrlList&); +      std::pair<iCtrl, bool> insert(const std::pair<int, CtrlVal>& p); +      iCtrl insert(iCtrl ic, const std::pair<int, CtrlVal>& p); +      void erase(iCtrl ictl); +      size_type erase(int frame); +      void erase(iCtrl first, iCtrl last); +      void clear(); +      CtrlList& operator=(const CtrlList&); +        Mode mode() const          { return _mode; }        void setMode(Mode m)       { _mode = m; }        double getDefault() const   { return _default; } @@ -138,7 +191,7 @@ class CtrlList : public std::map<int, CtrlVal, std::less<int> > {        CtrlValueType valueType() const { return _valueType; }        void setValueType(CtrlValueType t) { _valueType = t; } -      double value(int frame) const; +      double value(int frame, bool cur_val_only = false, int* nextFrame = NULL) const;          void add(int frame, double value);        void del(int frame);        void read(Xml& xml); @@ -148,6 +201,8 @@ class CtrlList : public std::map<int, CtrlVal, std::less<int> > {        void setVisible(bool v) { _visible = v; }        bool isVisible() const { return _visible; }        bool dontShow() const { return _dontShow; } +      bool guiUpdatePending() const { return _guiUpdatePending; } +      void setGuiUpdatePending(bool v) { _guiUpdatePending = v; }        };  //--------------------------------------------------------- @@ -161,6 +216,8 @@ typedef std::map<int, CtrlList*, std::less<int> >::iterator iCtrlList;  typedef std::map<int, CtrlList*, std::less<int> >::const_iterator ciCtrlList;  class CtrlListList : public std::map<int, CtrlList*, std::less<int> > { +   private: +      MidiAudioCtrlMap _midi_controls;  // For midi control of audio controllers.     public:        void add(CtrlList* vl);        void clearDelete() { @@ -176,14 +233,19 @@ class CtrlListList : public std::map<int, CtrlList*, std::less<int> > {              return std::map<int, CtrlList*, std::less<int> >::find(id);              } -      double value(int ctrlId, int frame, bool cur_val_only = false) const; +      MidiAudioCtrlMap* midiControls() { return &_midi_controls; }   +       +      double value(int ctrlId, int frame, bool cur_val_only = false, int* nextFrame = NULL) const;           void updateCurValues(int frame);        void clearAllAutomation() {              for(iCtrlList i = begin(); i != end(); ++i)                i->second->clear();             }      +      void write(int level, Xml& xml) const;        }; +extern double midi2AudioCtrlValue(const CtrlList* audio_ctrl_list, const MidiAudioCtrlStruct* mapper, int midi_ctlnum, int midi_val); +  } // namespace MusECore  #endif diff --git a/muse2/muse/driver/alsamidi.cpp b/muse2/muse/driver/alsamidi.cpp index f75b9c33..e3e71365 100644 --- a/muse2/muse/driver/alsamidi.cpp +++ b/muse2/muse/driver/alsamidi.cpp @@ -1318,24 +1318,24 @@ void alsaProcessMidiInput()                          break;                    case SND_SEQ_EVENT_CLOCK: -                        MusEGlobal::midiSeq->realtimeSystemInput(curPort, ME_CLOCK); +                        MusEGlobal::midiSeq->realtimeSystemInput(curPort, ME_CLOCK, curTime());                          //mdev->syncInfo().trigMCSyncDetect();                          break;                    case SND_SEQ_EVENT_START: -                        MusEGlobal::midiSeq->realtimeSystemInput(curPort, ME_START); +                        MusEGlobal::midiSeq->realtimeSystemInput(curPort, ME_START, curTime());                          break;                    case SND_SEQ_EVENT_CONTINUE: -                        MusEGlobal::midiSeq->realtimeSystemInput(curPort, ME_CONTINUE); +                        MusEGlobal::midiSeq->realtimeSystemInput(curPort, ME_CONTINUE, curTime());                          break;                    case SND_SEQ_EVENT_STOP: -                        MusEGlobal::midiSeq->realtimeSystemInput(curPort, ME_STOP); +                        MusEGlobal::midiSeq->realtimeSystemInput(curPort, ME_STOP, curTime());                          break;                    case SND_SEQ_EVENT_TICK: -                        MusEGlobal::midiSeq->realtimeSystemInput(curPort, ME_TICK); +                        MusEGlobal::midiSeq->realtimeSystemInput(curPort, ME_TICK, curTime());                          //mdev->syncInfo().trigTickDetect();                          break; diff --git a/muse2/muse/driver/jack.cpp b/muse2/muse/driver/jack.cpp index 4cc8bfb8..05d47955 100644 --- a/muse2/muse/driver/jack.cpp +++ b/muse2/muse/driver/jack.cpp @@ -42,6 +42,7 @@  #include "tempo.h"  #include "sync.h"  #include "utils.h" +#include "gconfig.h"  #include "midi.h"  #include "mididev.h" @@ -50,7 +51,7 @@  #include "jackmidi.h" -#define JACK_DEBUG 0    +#define JACK_DEBUG 0   //#include "errorhandler.h" @@ -176,7 +177,7 @@ int JackAudioDevice::processAudio(jack_nframes_t frames, void*)                  }                }              } -         +                          //if(jackAudio->getState() != Audio::START_PLAY)  // Don't process while we're syncing. TODO: May need to deliver silence in process!                MusEGlobal::audio->process((unsigned long)frames);        } @@ -196,8 +197,21 @@ int JackAudioDevice::processAudio(jack_nframes_t frames, void*)  static int processSync(jack_transport_state_t state, jack_position_t* pos, void*)        {        if (JACK_DEBUG) -            printf("processSync()\n"); +      { +        printf("processSync frame:%u\n", pos->frame); +        if(pos->valid & JackPositionBBT) +        { +          if(JACK_DEBUG) +          { +            printf("processSync BBT:\n bar:%d beat:%d tick:%d\n bar_start_tick:%f beats_per_bar:%f beat_type:%f ticks_per_beat:%f beats_per_minute:%f\n", +                    pos->bar, pos->beat, pos->tick, pos->bar_start_tick, pos->beats_per_bar, pos->beat_type, pos->ticks_per_beat, pos->beats_per_minute); +            if(pos->valid & JackBBTFrameOffset) +              printf("processSync BBTFrameOffset: %u\n", pos->bbt_offset); +          } +        } +      } +                if(!MusEGlobal::useJackTransport.value())          return 1; @@ -237,49 +251,54 @@ static int processSync(jack_transport_state_t state, jack_position_t* pos, void*  //---------------------------------------------------------  static void timebase_callback(jack_transport_state_t /* state */, -   jack_nframes_t /* nframes */, +   jack_nframes_t nframes,     jack_position_t* pos, -   int /* new_pos */, +   int new_pos,     void*)    { -      if (JACK_DEBUG) -        printf("Jack timebase_callback pos->frame:%u MusEGlobal::audio->tickPos:%d MusEGlobal::song->cpos:%d\n", pos->frame, MusEGlobal::audio->tickPos(), MusEGlobal::song->cpos()); -       -      //Pos p(pos->frame, false); + +    if (JACK_DEBUG) +    { +      if(pos->valid & JackPositionBBT) +        printf("timebase_callback BBT:\n bar:%d beat:%d tick:%d\n bar_start_tick:%f beats_per_bar:%f beat_type:%f ticks_per_beat:%f beats_per_minute:%f\n", +                pos->bar, pos->beat, pos->tick, pos->bar_start_tick, pos->beats_per_bar, pos->beat_type, pos->ticks_per_beat, pos->beats_per_minute); +      if(pos->valid & JackBBTFrameOffset) +        printf("timebase_callback BBTFrameOffset: %u\n", pos->bbt_offset); +      if(pos->valid & JackPositionTimecode) +        printf("timebase_callback JackPositionTimecode: frame_time:%f next_time:%f\n", pos->frame_time, pos->next_time); +      if(pos->valid & JackAudioVideoRatio) +        printf("timebase_callback JackAudioVideoRatio: %f\n", pos->audio_frames_per_video_frame); +      if(pos->valid & JackVideoFrameOffset) +        printf("timebase_callback JackVideoFrameOffset: %u\n", pos->video_offset); +    } +     +    //Pos p(pos->frame, false);        Pos p(MusEGlobal::extSyncFlag.value() ? MusEGlobal::audio->tickPos() : pos->frame, MusEGlobal::extSyncFlag.value() ? true : false);        // Can't use song pos - it is only updated every (slow) GUI heartbeat !        //Pos p(MusEGlobal::extSyncFlag.value() ? MusEGlobal::song->cpos() : pos->frame, MusEGlobal::extSyncFlag.value() ? true : false);        pos->valid = JackPositionBBT;        p.mbt(&pos->bar, &pos->beat, &pos->tick); +      pos->bar_start_tick = Pos(pos->bar, 0, 0).tick();        pos->bar++;        pos->beat++; -      pos->bar_start_tick = Pos(pos->bar, 0, 0).tick(); -       -      // -      //  dummy: -      // -      //pos->beats_per_bar = 4; -      //pos->beat_type = 4; -      //pos->ticks_per_beat = 384; -      // -      /* // From example client transport.c : -      float time_beats_per_bar = 4.0; -      float time_beat_type = 0.25;            // Huh? Inverted? From docs: "Time signature 'denominator'"  -      double time_ticks_per_beat = 1920.0;    // Huh? Ticks per beat should be 24 etc. not 384 or 1920 etc. Otherwise it would be called 'frames_per_beat'. -      double time_beats_per_minute = 120.0; -      */ -      //        int z, n;        AL::sigmap.timesig(p.tick(), z, n);        pos->beats_per_bar = z;        pos->beat_type = n; -      //pos->ticks_per_beat = config.division; -      pos->ticks_per_beat = 24; +      pos->ticks_per_beat = MusEGlobal::config.division; +      //pos->ticks_per_beat = 24; +       +      double tempo = MusEGlobal::tempomap.tempo(p.tick()); +      pos->beats_per_minute = (60000000.0 / tempo) * double(MusEGlobal::tempomap.globalTempo())/100.0; +      if (JACK_DEBUG) +      { +        printf("timebase_callback is new_pos:%d nframes:%u frame:%u tickPos:%d cpos:%d\n", new_pos, nframes, pos->frame, MusEGlobal::audio->tickPos(), MusEGlobal::song->cpos()); +        printf(" new: bar:%d beat:%d tick:%d\n bar_start_tick:%f beats_per_bar:%f beat_type:%f ticks_per_beat:%f beats_per_minute:%f\n", +               pos->bar, pos->beat, pos->tick, pos->bar_start_tick, pos->beats_per_bar, pos->beat_type, pos->ticks_per_beat, pos->beats_per_minute); +      } -      int tempo = MusEGlobal::tempomap.tempo(p.tick()); -      pos->beats_per_minute = (60000000.0 / tempo) * MusEGlobal::tempomap.globalTempo()/100.0;        }  //--------------------------------------------------------- @@ -1226,6 +1245,69 @@ jack_transport_state_t JackAudioDevice::transportQuery(jack_position_t* pos)  }  //--------------------------------------------------------- +//   timebaseQuery +//   Given the number of frames in this period, get the bar, beat, tick,  +//    and current absolute tick, and number of ticks in this period. +//   Return false if information could not be obtained. +//--------------------------------------------------------- + +bool JackAudioDevice::timebaseQuery(unsigned frames, unsigned* bar, unsigned* beat, unsigned* tick, unsigned* curr_abs_tick, unsigned* next_ticks)  +{ +  jack_position_t jp; +  jack_transport_query(_client, &jp);  + +  if(JACK_DEBUG) +    printf("timebaseQuery frame:%u\n", jp.frame);  +   +  if(jp.valid & JackPositionBBT) +  { +    if(JACK_DEBUG) +    { +      printf("timebaseQuery BBT:\n bar:%d beat:%d tick:%d\n bar_start_tick:%f beats_per_bar:%f beat_type:%f ticks_per_beat:%f beats_per_minute:%f\n", +              jp.bar, jp.beat, jp.tick, jp.bar_start_tick, jp.beats_per_bar, jp.beat_type, jp.ticks_per_beat, jp.beats_per_minute); +      if(jp.valid & JackBBTFrameOffset) +        printf("timebaseQuery BBTFrameOffset: %u\n", jp.bbt_offset); +    } +     +    if(jp.ticks_per_beat > 0.0) +    { +      unsigned muse_tick = unsigned((double(jp.tick) / jp.ticks_per_beat) * double(MusEGlobal::config.division)); +      unsigned curr_tick = ((jp.bar - 1) * jp.beats_per_bar + (jp.beat - 1)) * double(MusEGlobal::config.division) + muse_tick; +      // Prefer the reported frame rate over the app's rate if possible.   +      double f_rate = jp.frame_rate != 0 ? jp.frame_rate : MusEGlobal::sampleRate; +      // beats_per_minute is "supposed" to be quantized to period size - that is, computed +      //  so that mid-period changes are averaged out to produce a single tempo which  +      //  produces the same tick in the end. If we can rely on that, we should be good accuracy. +      unsigned ticks  = double(MusEGlobal::config.division) * (jp.beats_per_minute / 60.0) * double(frames) / f_rate;    + +      if(JACK_DEBUG) +        printf("timebaseQuery curr_tick:%u f_rate:%f ticks:%u\n", curr_tick, f_rate, ticks);   + +      if(bar) *bar = jp.bar; +      if(beat) *beat = jp.beat; +      if(tick) *tick = muse_tick; +       +      if(curr_abs_tick) *curr_abs_tick = curr_tick; +      if(next_ticks) *next_ticks = ticks; +         +      return true; +    } +  } + +  if(JACK_DEBUG) +  { +    if(jp.valid & JackPositionTimecode) +      printf("timebaseQuery JackPositionTimecode: frame_time:%f next_time:%f\n", jp.frame_time, jp.next_time); +    if(jp.valid & JackAudioVideoRatio) +      printf("timebaseQuery JackAudioVideoRatio: %f\n", jp.audio_frames_per_video_frame); +    if(jp.valid & JackVideoFrameOffset) +      printf("timebaseQuery JackVideoFrameOffset: %u\n", jp.video_offset); +  } +   +  return false; +}                 + +//---------------------------------------------------------  //   systemTime  //   Return system time. Depends on selected clock source.   //   With Jack, may be based upon wallclock time, the    @@ -1617,7 +1699,7 @@ void JackAudioDevice::seekTransport(unsigned frame)  void JackAudioDevice::seekTransport(const Pos &p)        {        if (JACK_DEBUG) -            printf("JackAudioDevice::seekTransport() frame:%d\n", p.frame()); +            printf("JackAudioDevice::seekTransport(Pos) frame:%d\n", p.frame());        if(!MusEGlobal::useJackTransport.value())        { @@ -1628,25 +1710,29 @@ void JackAudioDevice::seekTransport(const Pos &p)        }        if(!checkJackClient(_client)) return; + +// TODO: Be friendly to other apps... Sadly not many of us use jack_transport_reposition. +//       This is actually required IF we want the extra position info to show up +//        in the sync callback, otherwise we get just the frame only. +//       This information is shared on the server, it is directly passed around.  +//       jack_transport_locate blanks the info from sync until the timebase callback reads  +//        it again right after, from some timebase master. See process in audio.cpp      + +//       jack_position_t jp; +//       jp.frame = p.frame(); +//        +//       jp.valid = JackPositionBBT; +//       p.mbt(&jp.bar, &jp.beat, &jp.tick); +//       jp.bar_start_tick = Pos(jp.bar, 0, 0).tick(); +//       jp.bar++; +//       jp.beat++; +//       jp.beats_per_bar = 5;  // TODO Make this correct ! +//       jp.beat_type = 8;      // +//       jp.ticks_per_beat = MusEGlobal::config.division; +//       int tempo = MusEGlobal::tempomap.tempo(p.tick()); +//       jp.beats_per_minute = (60000000.0 / tempo) * MusEGlobal::tempomap.globalTempo()/100.0; +//       jack_transport_reposition(_client, &jp); -      /* -      jack_position_t jp; -      jp.valid = JackPositionBBT; -      p.mbt(&jp.bar, &jp.beat, &jp.tick); -      jp.bar++; -      jp.beat++; -      jp.bar_start_tick = Pos(jp.bar, 0, 0).tick(); -      // -      //  dummy: -      // -      jp.beats_per_bar = 4; -      jp.beat_type = 4; -      jp.ticks_per_beat = 384; -      int tempo = MusEGlobal::tempomap.tempo(p.tick()); -      jp.beats_per_minute = (60000000.0 / tempo) * MusEGlobal::tempomap.globalTempo()/100.0; -       -      jack_transport_reposition(_client, &jp); -      */        jack_transport_locate(_client, p.frame());        } diff --git a/muse2/muse/driver/jackaudio.h b/muse2/muse/driver/jackaudio.h index 9640ca81..aab60d88 100644 --- a/muse2/muse/driver/jackaudio.h +++ b/muse2/muse/driver/jackaudio.h @@ -80,6 +80,7 @@ class JackAudioDevice : public AudioDevice {        virtual std::list<QString> outputPorts(bool midi = false, int aliases = -1);        virtual std::list<QString> inputPorts(bool midi = false, int aliases = -1); +      jack_client_t* jackClient() const { return _client; }        virtual void registerClient();        virtual const char* clientName() { return jackRegisteredName; } @@ -105,6 +106,7 @@ class JackAudioDevice : public AudioDevice {        virtual void seekTransport(const Pos &p);        virtual void setFreewheel(bool f);        jack_transport_state_t transportQuery(jack_position_t* pos); +      bool timebaseQuery(unsigned frames, unsigned* bar, unsigned* beat, unsigned* tick, unsigned* curr_abs_tick, unsigned* next_ticks);        void graphChanged();        void registrationChanged();        void connectJackMidiPorts(); diff --git a/muse2/muse/driver/jackmidi.cpp b/muse2/muse/driver/jackmidi.cpp index 706fa269..e3e67dfb 100644 --- a/muse2/muse/driver/jackmidi.cpp +++ b/muse2/muse/driver/jackmidi.cpp @@ -30,6 +30,7 @@  //#include <jack/midiport.h>  #include "jackmidi.h" +#include "jackaudio.h"  #include "song.h"  #include "globals.h"  #include "midi.h" @@ -50,10 +51,6 @@  // Turn on debug messages.  //#define JACK_MIDI_DEBUG -namespace MusEGlobal { -extern unsigned int volatile lastExtMidiSyncTick; -} -  namespace MusECore {  //--------------------------------------------------------- @@ -422,7 +419,7 @@ void MidiJackDevice::recordEvent(MidiRecordEvent& event)        // Split the events up into channel fifos. Special 'channel' number 17 for sysex events.        unsigned int ch = (typ == ME_SYSEX)? MIDI_CHANNELS : event.channel(); -      if(_recordFifo[ch].put(MidiPlayEvent(event))) +      if(_recordFifo[ch].put(event))          printf("MidiJackDevice::recordEvent: fifo channel %d overflow\n", ch);        } @@ -446,10 +443,18 @@ void MidiJackDevice::eventReceived(jack_midi_event_t* ev)        //               catch      process    play        // -      //int frameOffset = MusEGlobal::audio->getFrameOffset(); -      unsigned pos = MusEGlobal::audio->pos().frame(); -       -      event.setTime(MusEGlobal::extSyncFlag.value() ? MusEGlobal::lastExtMidiSyncTick : (pos + ev->time));      // p3.3.25 +      // These Jack events arrived in the previous period, and it may not have been at the audio position before this one (after a seek). +      // This is how our ALSA driver works, events there are timestamped asynchronous of any process, referenced to the CURRENT audio  +      //  position, so that by the time of the NEXT process, THOSE events have also occured in the previous period. +      // So, technically this is correct. What MATTERS is how we adjust the times for storage, and/or simultaneous playback in THIS period, +      //  and TEST: we'll need to make sure any non-contiguous previous period is handled correctly by process - will it work OK as is? +      // If ALSA works OK than this should too... +#ifdef _AUDIO_USE_TRUE_FRAME_ +      event.setTime(MusEGlobal::audio->previousPos().frame() + ev->time); +#else +      event.setTime(MusEGlobal::audio->pos().frame() + ev->time); +#endif +      event.setTick(MusEGlobal::lastExtMidiSyncTick);            event.setChannel(*(ev->buffer) & 0xf);        int type = *(ev->buffer) & 0xf0; @@ -509,9 +514,20 @@ void MidiJackDevice::eventReceived(jack_midi_event_t* ev)                            case ME_START:                                  case ME_CONTINUE:                               case ME_STOP:        -                                if(_port != -1) -                                  MusEGlobal::midiSeq->realtimeSystemInput(_port, type); +                          { +                                if(MusEGlobal::audioDevice && MusEGlobal::audioDevice->deviceType() == JACK_MIDI && _port != -1) +                                { +                                  MusECore::JackAudioDevice* jad = static_cast<MusECore::JackAudioDevice*>(MusEGlobal::audioDevice); +                                  jack_client_t* jc = jad->jackClient(); +                                  if(jc) +                                  { +                                    jack_nframes_t abs_ft = jack_last_frame_time(jc)  + ev->time; +                                    double abs_ev_t = double(jack_frames_to_time(jc, abs_ft)) / 1000000.0; +                                    MusEGlobal::midiSeq->realtimeSystemInput(_port, type, abs_ev_t); +                                  } +                                }                                  return; +                          }                            //case ME_SYSEX_END:                                    //break;                            //      return; diff --git a/muse2/muse/dssihost.cpp b/muse2/muse/dssihost.cpp index 5000e338..01f9c0f3 100644 --- a/muse2/muse/dssihost.cpp +++ b/muse2/muse/dssihost.cpp @@ -680,7 +680,7 @@ bool DssiSynthIF::init(DssiSynth* s)        // Set current program.        if(dssi->select_program) -        dssi->select_program(handle, synti->_curBankL, synti->_curProgram); +        doSelectProgram(handle, synti->_curBankL, synti->_curProgram);        //        // For stored initial control values, let SynthI::initInstance() take care of that via ::setParameter(). @@ -839,27 +839,7 @@ float DssiSynthIF::getParameterOut(unsigned long n) const  void DssiSynthIF::setParameter(unsigned long n, float v)  { -  if(n >= synth->_controlInPorts) -  { -    printf("DssiSynthIF::setParameter param number %lu out of range of ports:%lu\n", n, synth->_controlInPorts); -    return; -  } -   -  ControlEvent ce; -  ce.unique = false; -  ce.idx = n; -  ce.value = v; -  // Time-stamp the event. This does a possibly slightly slow call to gettimeofday via timestamp(). -  //  timestamp() is more or less an estimate of the current frame. (This is exactly how ALSA events  -  //  are treated when they arrive in our ALSA driver.)  -  //ce.frame = MusEGlobal::audio->timestamp();   -  // p4.0.23 timestamp() is circular, which is making it impossible to deal with 'modulo' events which  -  //  slip in 'under the wire' before processing the ring buffers. So try this linear timestamp instead: -  ce.frame = MusEGlobal::audio->curFrame();   -  if(_controlFifo.put(ce)) -  { -    fprintf(stderr, "DssiSynthIF::setParameter: fifo overflow: in control number:%lu\n", n); -  } +  addScheduledControlEvent(n, v, MusEGlobal::audio->curFrame());     }  //--------------------------------------------------------- @@ -1084,7 +1064,7 @@ bool DssiSynthIF::processEvent(const MusECore::MidiPlayEvent& e, snd_seq_event_t        synti->_curProgram = prog;        if(dssi->select_program) -        dssi->select_program(handle, bank, prog); +        doSelectProgram(handle, bank, prog);        // Event pointer not filled. Return false.        return false; @@ -1113,7 +1093,7 @@ bool DssiSynthIF::processEvent(const MusECore::MidiPlayEvent& e, snd_seq_event_t          synti->_curProgram = prog;          if(dssi->select_program) -          dssi->select_program(handle, bank, prog); +          doSelectProgram(handle, bank, prog);          // Event pointer not filled. Return false.          return false; @@ -1220,6 +1200,11 @@ bool DssiSynthIF::processEvent(const MusECore::MidiPlayEvent& e, snd_seq_event_t        // Set the ladspa port value.        controls[k].val = val; +      // Need to update the automation value, otherwise it overwrites later with the last automation value. +      if(id() != -1) +        // We're in the audio thread context: no need to send a message, just modify directly. +        synti->setPluginCtrlVal(genACnum(id(), k), val); +              // Since we absorbed the message as a ladspa control change, return false - the event is not filled.        return false;      } @@ -1404,7 +1389,7 @@ MusECore::iMPEvent DssiSynthIF::getData(MusECore::MidiPort* /*mp*/, MusECore::MP    if(fixedsize > nframes)      fixedsize = nframes; -  unsigned long min_per = MusEGlobal::config.minControlProcessPeriod;   +  unsigned long min_per = MusEGlobal::config.minControlProcessPeriod;  // Must be power of 2 !    if(min_per > nframes)      min_per = nframes; @@ -1412,7 +1397,7 @@ MusECore::iMPEvent DssiSynthIF::getData(MusECore::MidiPort* /*mp*/, MusECore::MP    fprintf(stderr, "DssiSynthIF::getData: Handling inputs...\n");    #endif -  // p4.0.38 Handle inputs... +  // Handle inputs...    if(!((MusECore::AudioTrack*)synti)->noInRoute())     {      RouteList* irl = ((MusECore::AudioTrack*)synti)->inRoutes(); @@ -1485,21 +1470,57 @@ MusECore::iMPEvent DssiSynthIF::getData(MusECore::MidiPort* /*mp*/, MusECore::MP    #ifdef DSSI_DEBUG_PROCESS     fprintf(stderr, "DssiSynthIF::getData: Processing automation control values...\n");    #endif -           -  // Process automation control values now. -  // TODO: This needs to be respect frame resolution. Put this inside the sample loop below. -  if(MusEGlobal::automation && synti && synti->automationType() != AUTO_OFF && id() != -1) -  { -    for(unsigned long k = 0; k < synth->_controlInPorts; ++k) -    { -      if(controls[k].enCtrl && controls[k].en2Ctrl ) -        controls[k].val = (static_cast<MusECore::AudioTrack*>(synti))->controller()->value(genACnum(id(), k), pos); -    }       -  } -         +        while(sample < nframes)    {      unsigned long nsamp = usefixedrate ? fixedsize : nframes - sample; + +    // +    // Process automation control values, while also determining the maximum acceptable  +    //  size of this run. Further processing, from FIFOs for example, can lower the size  +    //  from there, but this section determines where the next highest maximum frame  +    //  absolutely needs to be for smooth playback of the controller value stream... +    // +    if(id() != -1) +    { +      unsigned long frame = pos + sample; +      AutomationType at = AUTO_OFF; +      at = synti->automationType(); +      bool no_auto = !MusEGlobal::automation || at == AUTO_OFF; +      AudioTrack* track = (static_cast<AudioTrack*>(synti)); +      int nextFrame; +      for(unsigned long k = 0; k < synth->_controlInPorts; ++k) +      {   +        controls[k].val = track->controller()->value(genACnum(id(), k), frame, +                                no_auto || !controls[k].enCtrl || !controls[k].en2Ctrl, +                                &nextFrame); +#ifdef DSSI_DEBUG_PROCESS +        printf("DssiSynthIF::getData k:%lu sample:%lu frame:%lu nextFrame:%d nsamp:%lu \n", k, sample, frame, nextFrame, nsamp); +#endif +        if(MusEGlobal::audio->isPlaying() && !usefixedrate && nextFrame != -1) +        { +          // Returned value of nextFrame can be zero meaning caller replaces with some (constant) value. +          unsigned long samps = (unsigned long)nextFrame; +          if(samps > frame + min_per) +          { +            unsigned long diff = samps - frame; +            unsigned long mask = min_per-1;   // min_per must be power of 2 +            samps = diff & ~mask; +            if((diff & mask) != 0) +              samps += min_per; +          } +          else +            samps = min_per; +           +          if(samps < nsamp) +            nsamp = samps; +        } +      }   +#ifdef DSSI_DEBUG +      printf("DssiSynthIF::getData sample:%lu nsamp:%lu\n", sample, nsamp); +#endif +    } +          bool found = false;      unsigned long frame = 0;       unsigned long index = 0; @@ -1528,11 +1549,13 @@ MusECore::iMPEvent DssiSynthIF::getData(MusECore::MidiPort* /*mp*/, MusECore::MP          continue;        }     -      if(evframe >= nframes -         || (found && !v.unique && (evframe - sample >= min_per))   -         || (usefixedrate && found && v.unique && v.idx == index)) +      if(evframe >= nframes                                                        // Next events are for a later period. +         || (!usefixedrate && !found && !v.unique && (evframe - sample >= nsamp))  // Next events are for a later run in this period. (Autom took prio.) +         || (found && !v.unique && (evframe - sample >= min_per))                  // Eat up events within minimum slice - they're too close. +         || (usefixedrate && found && v.unique && v.idx == index))                 // Special for dssi-vst: Fixed rate and must reply to all.          break;        _controlFifo.remove();               // Done with the ring buffer's item. Remove it. +        if(v.idx >= synth->_controlInPorts) // Sanity check.          break;        found = true; @@ -1543,36 +1566,16 @@ MusECore::iMPEvent DssiSynthIF::getData(MusECore::MidiPort* /*mp*/, MusECore::MP        // Need to update the automation value, otherwise it overwrites later with the last automation value.        if(id() != -1) -      { -        // We're in the audio thread context: no need to send a message, just modify directly.          synti->setPluginCtrlVal(genACnum(id(), v.idx), v.value); -         -        /* Record automation. DELETETHIS? -         * NO! Take care of this immediately in the OSC control handler, because we don't want  -         *  any delay. -         * OTOH Since this is the actual place and time where the control ports values -         *  are set, best to reflect what happens here to automation. -         * However for dssi-vst it might be best to handle it that way. -         -        // TODO: Taken from our native gui control handlers.  -        // This may need modification or may cause problems -  -        //  we don't have the luxury of access to the dssi gui controls ! -        AutomationType at = _track->automationType(); -        if ((at == AUTO_WRITE) || -            (at == AUTO_TOUCH && MusEGlobal::audio->isPlaying())) -          enableController(k, false); -        _track->recordAutomation(id, v.value); -        */ -      }        } -    if(found && !usefixedrate) +    if(found && !usefixedrate)  // If a control FIFO item was found, takes priority over automation controller stream.        nsamp = frame - sample;      if(sample + nsamp >= nframes)         // Safety check.        nsamp = nframes - sample;  -    // TODO: TESTING: Don't allow zero-length runs. This could/should be checked in the control loop instead. +    // TODO: Don't allow zero-length runs. This could/should be checked in the control loop instead.      // Note this means it is still possible to get stuck in the top loop (at least for a while).      if(nsamp == 0)        continue; @@ -1586,8 +1589,13 @@ MusECore::iMPEvent DssiSynthIF::getData(MusECore::MidiPort* /*mp*/, MusECore::MP        #endif        if(start_event->time() >= (pos + sample + nsamp + frameOffset))  // frameOffset? Test again... +      { +        #ifdef DSSI_DEBUG  +        fprintf(stderr, " event is for future:%lu, breaking loop now\n", start_event->time() - frameOffset - pos - sample); +        #endif          break; -         +      } +              // Update hardware state so knobs and boxes are updated. Optimize to avoid re-setting existing values.        // Same code as in MidiPort::sendEvent()        if(synti->midiPort() != -1) @@ -2083,6 +2091,25 @@ void DssiSynthIF::queryPrograms()              }        } +void DssiSynthIF::doSelectProgram(LADSPA_Handle handle, int bank, int prog) +{ +  const DSSI_Descriptor* dssi = synth->dssi; +  dssi->select_program(handle, bank, prog); +   +  // Need to update the automation value, otherwise it overwrites later with the last automation value. +  //   "A plugin is permitted to re-write the values of its input control ports when select_program is called.   +  //    The host should re-read the input control port values and update its own records appropriately.   +  //    (This is the only circumstance in which a DSSI plugin is allowed to modify its own input ports.)"   From dssi.h +  if(id() != -1) +  { +    for(unsigned long k = 0; k < synth->_controlInPorts; ++k) +    {   +      // We're in the audio thread context: no need to send a message, just modify directly. +      synti->setPluginCtrlVal(genACnum(id(), k), controls[k].val); +    } +  } +} +  //---------------------------------------------------------  //   getPatchName  //--------------------------------------------------------- @@ -2235,14 +2262,30 @@ QString DssiSynthIF::titlePrefix() const                     { return QString();  MusECore::AudioTrack* DssiSynthIF::track()                   { return (MusECore::AudioTrack*)synti; }  void DssiSynthIF::enableController(unsigned long i, bool v)  { controls[i].enCtrl = v; }   bool DssiSynthIF::controllerEnabled(unsigned long i) const   { return controls[i].enCtrl; }   +void DssiSynthIF::enable2Controller(unsigned long i, bool v) { controls[i].en2Ctrl = v; }       bool DssiSynthIF::controllerEnabled2(unsigned long i) const  { return controls[i].en2Ctrl; }    +void DssiSynthIF::enableAllControllers(bool v)                +{  +  if(!synth) +    return; +  for(unsigned long i = 0; i < synth->_controlInPorts; ++i)  +    controls[i].enCtrl = v;  +} +void DssiSynthIF::enable2AllControllers(bool v) +{ +  if(!synth) +    return; +  for(unsigned long i = 0; i < synth->_controlInPorts; ++i) +    controls[i].en2Ctrl = v;  +} +  void DssiSynthIF::updateControllers()                        { }  void DssiSynthIF::writeConfiguration(int /*level*/, Xml& /*xml*/)        { }  bool DssiSynthIF::readConfiguration(Xml& /*xml*/, bool /*readPreset*/) { return false; }  unsigned long DssiSynthIF::parameters() const                { return synth ? synth->_controlInPorts : 0; }  unsigned long DssiSynthIF::parametersOut() const             { return synth ? synth->_controlOutPorts : 0; } -void DssiSynthIF::setParam(unsigned long i, float val)       { setParameter(i, val); } +void DssiSynthIF::setParam(unsigned long i, float val)  { setParameter(i, val); }  float DssiSynthIF::param(unsigned long i) const              { return getParameter(i); }  float DssiSynthIF::paramOut(unsigned long i) const           { return getParameterOut(i); }  const char* DssiSynthIF::paramName(unsigned long i)          { return (synth && synth->dssi) ? synth->dssi->LADSPA_Plugin->PortNames[controls[i].idx] : 0; } diff --git a/muse2/muse/dssihost.h b/muse2/muse/dssihost.h index 238b468e..46c9a07b 100644 --- a/muse2/muse/dssihost.h +++ b/muse2/muse/dssihost.h @@ -128,6 +128,7 @@ class DssiSynthIF : public SynthIF, public PluginIBase        std::vector<DSSI_Program_Descriptor> programs;        void queryPrograms(); +      void doSelectProgram(LADSPA_Handle handle, int bank, int prog);         bool processEvent(const MusECore::MidiPlayEvent&, snd_seq_event_t*);        float** audioInBuffers; @@ -209,7 +210,10 @@ class DssiSynthIF : public SynthIF, public PluginIBase        MusECore::AudioTrack* track();                  void enableController(unsigned long i, bool v = true);              bool controllerEnabled(unsigned long i) const;           +      void enable2Controller(unsigned long i, bool v = true);              bool controllerEnabled2(unsigned long i) const;           +      void enableAllControllers(bool v = true); +      void enable2AllControllers(bool v = true);        void updateControllers();        void writeConfiguration(int level, Xml& xml);        bool readConfiguration(Xml& xml, bool readPreset=false); diff --git a/muse2/muse/gconfig.cpp b/muse2/muse/gconfig.cpp index 1a0426a7..302007b3 100644 --- a/muse2/muse/gconfig.cpp +++ b/muse2/muse/gconfig.cpp @@ -186,7 +186,7 @@ GlobalConfigValues config = {        QString("./"),                // projectBaseFolder        true,                         // projectStoreInFolder        true,                         // useProjectSaveDialog -      64,                           // minControlProcessPeriod +      256,                          // minControlProcessPeriod        false,                        // popupsDefaultStayOpen        false,                        // leftMouseButtonCanDecrease        false,                        // rangeMarkerWithoutMMB diff --git a/muse2/muse/globals.cpp b/muse2/muse/globals.cpp index d92e6abf..b3765074 100644 --- a/muse2/muse/globals.cpp +++ b/muse2/muse/globals.cpp @@ -275,6 +275,12 @@ unsigned char rcPlayNote = 29;  unsigned char rcSteprecNote = 36;  bool automation = true; +// Midi learn params. These will be initialized to -1 by any midi learn function,  +//  and then filled by the midi engine in response to the drivers. +int midiLearnPort = -1; +int midiLearnChan = -1; +int midiLearnCtrl = -1; +  uid_t euid, ruid;  // effective user id, real user id  bool midiSeqRunning = false; diff --git a/muse2/muse/globals.h b/muse2/muse/globals.h index c64fdf89..bdf383c8 100644 --- a/muse2/muse/globals.h +++ b/muse2/muse/globals.h @@ -172,6 +172,10 @@ extern unsigned char rcGotoLeftMarkNote;  extern unsigned char rcPlayNote;  extern unsigned char rcSteprecNote; +extern int midiLearnPort; +extern int midiLearnChan; +extern int midiLearnCtrl; +  extern bool midiSeqRunning;  extern bool automation; diff --git a/muse2/muse/midi.cpp b/muse2/muse/midi.cpp index ae348b5f..503208e6 100644 --- a/muse2/muse/midi.cpp +++ b/muse2/muse/midi.cpp @@ -34,6 +34,7 @@  #include "marker/marker.h"  #include "midiport.h"  #include "midictrl.h" +#include "sync.h"  #include "audio.h"  #include "mididev.h"  #include "driver/alsamidi.h" @@ -860,31 +861,149 @@ void Audio::collectEvents(MusECore::MidiTrack* track, unsigned int cts, unsigned  void Audio::processMidi()        {        MusEGlobal::midiBusy=true; +       +      bool extsync = MusEGlobal::extSyncFlag.value(); +              //        // TODO: syntis should directly write into recordEventList        // -      for (iMidiDevice id = MusEGlobal::midiDevices.begin(); id != MusEGlobal::midiDevices.end(); ++id) { -            MidiDevice* md = *id; - -            // klumsy hack for synti devices: -            if(md->isSynti()) +      for (iMidiDevice id = MusEGlobal::midiDevices.begin(); id != MusEGlobal::midiDevices.end(); ++id)  +      { +        MidiDevice* md = *id; + +        // klumsy hack for MESS synti devices: +        if(md->isSynti()) +        { +          SynthI* s = (SynthI*)md; +          while (s->eventsPending())  +          { +            MusECore::MidiRecordEvent ev = s->receiveEvent(); +            md->recordEvent(ev); +          } +        } +         +        md->collectMidiEvents(); +         +        // Take snapshots of the current sizes of the recording fifos,  +        //  because they may change while here in process, asynchronously. +        md->beforeProcess(); +         +        // +        // --------- Handle midi events for audio tracks ----------- +        //  +         +        int port = md->midiPort(); // Port should be same as event.port() from this device. Same idea event.channel(). +        if(port < 0) +          continue; +         +        for(int chan = 0; chan < MIDI_CHANNELS; ++chan)      +        { +          MusECore::MidiRecFifo& rf = md->recordEvents(chan); +          int count = md->tmpRecordCount(chan); +          for(int i = 0; i < count; ++i)  +          { +            MusECore::MidiRecordEvent event(rf.peek(i)); + +            int etype = event.type(); +            if(etype == MusECore::ME_CONTROLLER || etype == MusECore::ME_PITCHBEND || etype == MusECore::ME_PROGRAM)              { -              SynthI* s = (SynthI*)md; -              while (s->eventsPending())  +              int ctl, val; +              if(etype == MusECore::ME_CONTROLLER)                { -                MusECore::MidiRecordEvent ev = s->receiveEvent(); -                md->recordEvent(ev); +                ctl = event.dataA(); +                val = event.dataB();                } +              else if(etype == MusECore::ME_PITCHBEND) +              { +                ctl = MusECore::CTRL_PITCH; +                val = event.dataA(); +              } +              else if(etype == MusECore::ME_PROGRAM) +              { +                ctl = MusECore::CTRL_PROGRAM; +                val = event.dataA(); +              } +               +              // Midi learn!  +              MusEGlobal::midiLearnPort = port; +              MusEGlobal::midiLearnChan = chan; +              MusEGlobal::midiLearnCtrl = ctl; +               +              // Send to audio tracks... +              for (MusECore::iTrack t = MusEGlobal::song->tracks()->begin(); t != MusEGlobal::song->tracks()->end(); ++t)  +              { +                if((*t)->isMidiTrack()) +                  continue; +                MusECore::AudioTrack* track = static_cast<MusECore::AudioTrack*>(*t); +                MidiAudioCtrlMap* macm = track->controller()->midiControls(); +                int h = macm->index_hash(port, chan, ctl); +                std::pair<ciMidiAudioCtrlMap, ciMidiAudioCtrlMap> range = macm->equal_range(h); +                for(ciMidiAudioCtrlMap imacm = range.first; imacm != range.second; ++imacm) +                { +                  const MidiAudioCtrlStruct* macs = &imacm->second; +                  int actrl = macs->audioCtrlId(); +                   +                  iCtrlList icl = track->controller()->find(actrl);  +                  if(icl == track->controller()->end()) +                    continue; +                  CtrlList* cl = icl->second; +                  double dval = midi2AudioCtrlValue(cl, macs, ctl, val); +                   +                  // Time here needs to be frames always.  +                  unsigned int ev_t = event.time(); +                  unsigned int t = ev_t; +                   +#ifdef _AUDIO_USE_TRUE_FRAME_ +                  unsigned int pframe = _previousPos.frame(); +#else +                  unsigned int pframe = _pos.frame(); +#endif +                  if(pframe > t)  // Technically that's an error, shouldn't happen +                    t = 0; +                  else +                    // Subtract the current audio position frame +                    t -= pframe;   +                   +                  // Add the current running sync frame to make the control processing happy +                  t += syncFrame; +                  track->addScheduledControlEvent(actrl, dval, t); +                     +                  // Rec automation... + +                  // For the record time, if stopped we don't want the circular running position, +                  //  just the static one. +                  unsigned int rec_t = isPlaying() ? ev_t : pframe; +                   +                  if(!MusEGlobal::automation) +                    continue; +                  AutomationType at = track->automationType(); +                  // Unlike our built-in gui controls, there is not much choice here but to  +                  //  just do this: +                  if ( (at == AUTO_WRITE) || +                       (at == AUTO_TOUCH && MusEGlobal::audio->isPlaying()) ) +                  //if(isPlaying() && (at == AUTO_WRITE || at == AUTO_TOUCH)) DELETETHIS +                    track->enableController(actrl, false); +                  if(isPlaying()) +                  { +                    if(at == AUTO_WRITE || at == AUTO_TOUCH) +                      track->recEvents()->push_back(CtrlRecVal(rec_t, actrl, dval));       +                  } +                  else  +                  { +                    if(at == AUTO_WRITE) +                      track->recEvents()->push_back(CtrlRecVal(rec_t, actrl, dval));     +                    else if(at == AUTO_TOUCH) +                      // In touch mode and not playing. Send directly to controller list. +                      // Add will replace if found. +                      cl->add(rec_t, dval);      +                  } +                } +              }                } -             -            md->collectMidiEvents(); -             -            // Take snapshots of the current sizes of the recording fifos,  -            //  because they may change while here in process, asynchronously. -            md->beforeProcess(); -            } +          }    +        } +      } -      bool extsync = MusEGlobal::extSyncFlag.value();        for (MusECore::iMidiTrack t = MusEGlobal::song->midis()->begin(); t != MusEGlobal::song->midis()->end(); ++t)         {              MusECore::MidiTrack* track = *t; @@ -934,15 +1053,30 @@ void Audio::processMidi()                              for(int i = 0; i < count; ++i)                               { -                              MusECore::MidiPlayEvent event(rf.peek(i)); +                              MusECore::MidiRecordEvent event(rf.peek(i));                                event.setPort(port);                                // dont't echo controller changes back to software                                // synthesizer:                                if(!dev->isSynti() && md && track->recEcho()) +                              { +                                // All recorded events arrived in the previous period. Shift into this period for playback. +                                unsigned int et = event.time(); +#ifdef _AUDIO_USE_TRUE_FRAME_ +                                unsigned int t = et - _previousPos.frame() + _pos.frame() + frameOffset; +#else                                 +                                unsigned int t = et + frameOffset; +#endif +                                event.setTime(t);                                  md->addScheduledEvent(event); -                              // If syncing externally the event time is already in units of ticks, set above.  p3.3.25 -                              if(!extsync) -                                event.setTime(MusEGlobal::tempomap.frame2tick(event.time()));  // set tick time +                                event.setTime(et);  // Restore for recording. +                              } +                               +                              // Make sure the event is recorded in units of ticks.   +                              if(extsync)   +                                event.setTime(event.tick());  // HACK: Transfer the tick to the frame time +                              else +                                event.setTime(MusEGlobal::tempomap.frame2tick(event.time())); +                                                                if(recording)                                   rl->add(event);                              }       @@ -953,7 +1087,7 @@ void Audio::processMidi()                            int count = dev->tmpRecordCount(channel);                            for(int i = 0; i < count; ++i)                             { -                                MusECore::MidiPlayEvent event(rf.peek(i)); +                                MusECore::MidiRecordEvent event(rf.peek(i));                                  int defaultPort = devport;                                  int drumRecPitch=0; //prevent compiler warning: variable used without initialization                                  MusECore::MidiController *mc = 0; @@ -1073,7 +1207,16 @@ void Audio::processMidi()                                  if (!dev->isSynti())                                   { -                                  //Check if we're outputting to another port than default: +                                  // All recorded events arrived in previous period. Shift into this period for playback.  +                                  //  frameoffset needed to make process happy. +                                  unsigned int et = event.time(); +#ifdef _AUDIO_USE_TRUE_FRAME_ +                                  unsigned int t = et - _previousPos.frame() + _pos.frame() + frameOffset; +#else +                                  unsigned int t = et + frameOffset; +#endif +                                  event.setTime(t);   +                                  // Check if we're outputting to another port than default:                                    if (devport == defaultPort) {                                          event.setPort(port);                                          if(md && track->recEcho()) @@ -1085,14 +1228,18 @@ void Audio::processMidi()                                          if(mdAlt && track->recEcho())                                            mdAlt->addScheduledEvent(event);                                          } +                                  event.setTime(et);  // Restore for recording. +                                                                      // Shall we activate meters even while rec echo is off? Sure, why not...                                    if(event.isNote() && event.dataB() > track->activity())                                      track->setActivity(event.dataB());                                  } -                                // If syncing externally the event time is already in units of ticks, set above.  p3.3.25 -                                if(!extsync) -                                  event.setTime(MusEGlobal::tempomap.frame2tick(event.time()));  // set tick time +                                // Make sure the event is recorded in units of ticks.   +                                if(extsync)   +                                  event.setTime(event.tick());  // HACK: Transfer the tick to the frame time +                                else +                                  event.setTime(MusEGlobal::tempomap.frame2tick(event.time()));                                  // Special handling of events stored in rec-lists. a bit hACKish. TODO: Clean up (after 0.7)! :-/ (ml)                                  if (recording)  diff --git a/muse2/muse/midictrl.cpp b/muse2/muse/midictrl.cpp index b95ccf77..63ce6fe6 100644 --- a/muse2/muse/midictrl.cpp +++ b/muse2/muse/midictrl.cpp @@ -306,6 +306,39 @@ MidiController::ControllerType midiControllerType(int num)        }  //--------------------------------------------------------- +//   midiCtrlTerms2Number +//--------------------------------------------------------- + +int midiCtrlTerms2Number(int type_num, int ctrl) +{ +  ctrl &= 0xffff; +  switch(type_num) +  { +    case MidiController::Controller7: +      return ctrl & 0xff; +    case MidiController::Controller14: +      return CTRL_14_OFFSET + ctrl; +    case MidiController::RPN: +      return CTRL_RPN_OFFSET + ctrl; +    case MidiController::NRPN: +      return CTRL_NRPN_OFFSET  + ctrl; +    case MidiController::Pitch: +      return CTRL_PITCH; +    case MidiController::Program: +      return CTRL_PROGRAM; +    case MidiController::Velo: +      return CTRL_VELOCITY; +    case MidiController::RPN14: +      return CTRL_RPN14_OFFSET + ctrl; +    case MidiController::NRPN14: +      return CTRL_NRPN14_OFFSET + ctrl; +    default: +      printf("MusE: unknown ctrl type in midiCtrTerms2Number()\n"); +      return ctrl; +  } +} + +//---------------------------------------------------------  //   updateBias  //--------------------------------------------------------- diff --git a/muse2/muse/midictrl.h b/muse2/muse/midictrl.h index 74902bc2..4c9a4097 100644 --- a/muse2/muse/midictrl.h +++ b/muse2/muse/midictrl.h @@ -242,6 +242,7 @@ typedef MidiControllerList MidiControllerList;  extern MidiControllerList defaultMidiController;  extern void initMidiController();  extern MidiController::ControllerType midiControllerType(int num); +extern int midiCtrlTerms2Number(int type_num, int ctrl);  extern const QString& int2ctrlType(int n); diff --git a/muse2/muse/mididev.cpp b/muse2/muse/mididev.cpp index becab6f7..5ff8bf94 100644 --- a/muse2/muse/mididev.cpp +++ b/muse2/muse/mididev.cpp @@ -46,7 +46,6 @@  namespace MusEGlobal {  MusECore::MidiDeviceList midiDevices; -extern unsigned int volatile lastExtMidiSyncTick;  }  namespace MusECore { @@ -215,10 +214,14 @@ void MidiDevice::beforeProcess()  void MidiDevice::recordEvent(MidiRecordEvent& event)        { -      // TODO: Tested, but record resolution not so good. Switch to wall clock based separate list in MidiDevice. And revert this line. -      //event.setTime(MusEGlobal::audio->timestamp()); -      event.setTime(MusEGlobal::extSyncFlag.value() ? MusEGlobal::lastExtMidiSyncTick : MusEGlobal::audio->timestamp()); -       +      // TODO: Tested, but record resolution not so good. Switch to wall clock based separate list in MidiDevice.  +      unsigned frame_ts = MusEGlobal::audio->timestamp(); +#ifndef _AUDIO_USE_TRUE_FRAME_ +      if(MusEGlobal::audio->isPlaying()) +       frame_ts += MusEGlobal::segmentSize;  // Shift forward into this period if playing +#endif +      event.setTime(frame_ts);   +      event.setTick(MusEGlobal::lastExtMidiSyncTick);            if(MusEGlobal::audio->isPlaying())          event.setLoopNum(MusEGlobal::audio->loopCount()); @@ -300,7 +303,7 @@ void MidiDevice::recordEvent(MidiRecordEvent& event)        // Split the events up into channel fifos. Special 'channel' number 17 for sysex events.        unsigned int ch = (typ == ME_SYSEX)? MIDI_CHANNELS : event.channel(); -      if(_recordFifo[ch].put(MidiPlayEvent(event))) +      if(_recordFifo[ch].put(event))          printf("MidiDevice::recordEvent: fifo channel %d overflow\n", ch);        } diff --git a/muse2/muse/midiseq.cpp b/muse2/muse/midiseq.cpp index 300382e9..2cfc1917 100644 --- a/muse2/muse/midiseq.cpp +++ b/muse2/muse/midiseq.cpp @@ -287,6 +287,18 @@ MidiSeq::MidiSeq(const char* name)        lastTempo = 0;        storedtimediffs = 0;        playStateExt = false; // not playing + +      _clockAveragerStages = new int[16]; // Max stages is 16! +      setSyncRecFilterPreset(MusEGlobal::syncRecFilterPreset); +       +      for(int i = 0; i < _clockAveragerPoles; ++i) +      { +        _avgClkDiffCounter[i] = 0; +        _averagerFull[i] = false; +      } +      _tempoQuantizeAmount = 1.0; +      _lastRealTempo      = 0.0; +              MusEGlobal::doSetuid();        timerFd=selectTimer();        MusEGlobal::undoSetuid(); @@ -300,6 +312,7 @@ MidiSeq::MidiSeq(const char* name)  MidiSeq::~MidiSeq()      {      delete timer; +    delete _clockAveragerStages;      }  //--------------------------------------------------------- @@ -505,11 +518,70 @@ void MidiSeq::checkAndReportTimingResolution()                               "Timing source frequency is %1hz, which is below the recommended minimum: 500hz!\n" \                               "This could lead to audible timing problems for MIDI.\n" \                               "Please see the FAQ on http://muse-sequencer.org for remedies.\n" \ -                             "Also please check console output for any further error messages\n ")).arg(freq) ); +                             "Also please check console output for any further error messages.\n ")).arg(freq) );      }  }  //--------------------------------------------------------- +//   setSyncRecFilterPreset +//   To be called in realtime thread only. +//--------------------------------------------------------- +void MidiSeq::setSyncRecFilterPreset(MidiSyncInfo::SyncRecFilterPresetType type) +{ +  _syncRecFilterPreset = type; +  alignAllTicks(); +   +  switch(_syncRecFilterPreset) +  { +    // NOTE: Max _clockAveragerPoles is 16 and maximum stages is 48 per pole ! +    case MidiSyncInfo::NONE: +      _clockAveragerPoles = 0;     +      _preDetect = false; +    break;   +    case MidiSyncInfo::TINY: +      _clockAveragerPoles = 2;     +      _clockAveragerStages[0] = 4;  +      _clockAveragerStages[1] = 4;  +      _preDetect = false; +    break;   +    case MidiSyncInfo::SMALL: +      _clockAveragerPoles = 3;     +      _clockAveragerStages[0] = 12;  +      _clockAveragerStages[1] = 8;  +      _clockAveragerStages[2] = 4;  +      _preDetect = false; +    break;   +    case MidiSyncInfo::MEDIUM: +      _clockAveragerPoles = 3;     +      _clockAveragerStages[0] = 28;  +      _clockAveragerStages[1] = 12;  +      _clockAveragerStages[2] = 8;  +      _preDetect = false; +    break;   +    case MidiSyncInfo::LARGE: +      _clockAveragerPoles = 4;     +      _clockAveragerStages[0] = 48;  +      _clockAveragerStages[1] = 48;  +      _clockAveragerStages[2] = 48;  +      _clockAveragerStages[3] = 48;  +      _preDetect = false; +    break;   +    case MidiSyncInfo::LARGE_WITH_PRE_DETECT: +      _clockAveragerPoles = 4;     +      _clockAveragerStages[0] = 8;  +      _clockAveragerStages[1] = 48;  +      _clockAveragerStages[2] = 48;  +      _clockAveragerStages[3] = 48;  +      _preDetect = true; +    break;   +     +    default: +      printf("MidiSeq::setSyncRecFilterPreset unknown preset type:%d\n", (int)type); +  } +} + + +//---------------------------------------------------------  //   processMidiClock  //--------------------------------------------------------- diff --git a/muse2/muse/midiseq.h b/muse2/muse/midiseq.h index b5ed1099..08adcdce 100644 --- a/muse2/muse/midiseq.h +++ b/muse2/muse/midiseq.h @@ -28,6 +28,7 @@  #include "mpevent.h"  #include "driver/alsatimer.h"  #include "driver/rtctimer.h" +#include "sync.h"  namespace MusECore { @@ -55,9 +56,17 @@ class MidiSeq : public Thread {        double songtick1, songtick2;        int recTick1, recTick2;        int lastTempo; -      double timediff[24]; +      double timediff[16][48];        int storedtimediffs; - +      int    _avgClkDiffCounter[16]; +      double _lastRealTempo; +      bool _averagerFull[16]; +      int _clockAveragerPoles; +      int* _clockAveragerStages;  +      bool _preDetect; +      double _tempoQuantizeAmount; +      MidiSyncInfo::SyncRecFilterPresetType _syncRecFilterPreset; +              void alignAllTicks(int frameOverride = 0);  /* Testing */ @@ -87,13 +96,17 @@ class MidiSeq : public Thread {        bool externalPlayState() const { return playStateExt; }        void setExternalPlayState(bool v) { playStateExt = v; } -      void realtimeSystemInput(int, int); +      void realtimeSystemInput(int port, int type, double time = 0.0);        void mtcInputQuarter(int, unsigned char);        void setSongPosition(int, int);        void mmcInput(int, const unsigned char*, int);        void mtcInputFull(int, const unsigned char*, int);        void nonRealtimeSystemSysex(int, const unsigned char*, int);        void checkAndReportTimingResolution(); +      MidiSyncInfo::SyncRecFilterPresetType syncRecFilterPreset() const { return _syncRecFilterPreset; } +      void setSyncRecFilterPreset(MidiSyncInfo::SyncRecFilterPresetType type); +      double recTempoValQuant() const { return _tempoQuantizeAmount; } +      void setRecTempoValQuant(double q) { _tempoQuantizeAmount = q; }        void msgMsg(int id);        void msgSeek(); diff --git a/muse2/muse/mixer/astrip.cpp b/muse2/muse/mixer/astrip.cpp index 7699af41..3b0a8707 100644 --- a/muse2/muse/mixer/astrip.cpp +++ b/muse2/muse/mixer/astrip.cpp @@ -455,10 +455,11 @@ void AudioStrip::auxLabelChanged(double val, unsigned int idx)  //   volumeChanged  //--------------------------------------------------------- -void AudioStrip::volumeChanged(double val) +void AudioStrip::volumeChanged(double val, int, bool shift_pressed)        {        AutomationType at = ((MusECore::AudioTrack*)track)->automationType(); -      if(at == AUTO_WRITE || (MusEGlobal::audio->isPlaying() && at == AUTO_TOUCH)) +      if ( (at == AUTO_WRITE) || +           (at == AUTO_TOUCH && MusEGlobal::audio->isPlaying()) )          track->enableVolumeController(false);        double vol; @@ -472,12 +473,7 @@ void AudioStrip::volumeChanged(double val)        //MusEGlobal::audio->msgSetVolume((MusECore::AudioTrack*)track, vol);        // p4.0.21 MusEGlobal::audio->msgXXX waits. Do we really need to?        ((MusECore::AudioTrack*)track)->setVolume(vol); -      MusEGlobal::song->controllerChange(track); -       -      ((MusECore::AudioTrack*)track)->recordAutomation(MusECore::AC_VOLUME, vol); - -      //MusEGlobal::song->update(SC_TRACK_MODIFIED); // for graphical automation update -      //MusEGlobal::song->controllerChange(track); +      if (!shift_pressed) ((MusECore::AudioTrack*)track)->recordAutomation(MusECore::AC_VOLUME, vol);        }  //--------------------------------------------------------- @@ -487,7 +483,7 @@ void AudioStrip::volumeChanged(double val)  void AudioStrip::volumePressed()        {        AutomationType at = ((MusECore::AudioTrack*)track)->automationType(); -      if(at == AUTO_WRITE || (at == AUTO_READ || at == AUTO_TOUCH)) +      if (at == AUTO_READ || at == AUTO_TOUCH || at == AUTO_WRITE)          track->enableVolumeController(false);        double val = slider->value(); @@ -502,8 +498,6 @@ void AudioStrip::volumePressed()        //MusEGlobal::audio->msgSetVolume((MusECore::AudioTrack*)track, volume);        // p4.0.21 MusEGlobal::audio->msgXXX waits. Do we really need to?        ((MusECore::AudioTrack*)track)->setVolume(volume); -      MusEGlobal::song->controllerChange(track); -              ((MusECore::AudioTrack*)track)->startAutoRecord(MusECore::AC_VOLUME, volume);        } @@ -513,7 +507,8 @@ void AudioStrip::volumePressed()  void AudioStrip::volumeReleased()        { -      if(track->automationType() != AUTO_WRITE) +      AutomationType at = track->automationType(); +      if (at == AUTO_OFF || at == AUTO_READ || at == AUTO_TOUCH)          track->enableVolumeController(true);        ((MusECore::AudioTrack*)track)->stopAutoRecord(MusECore::AC_VOLUME, volume); @@ -534,7 +529,8 @@ void AudioStrip::volumeRightClicked(const QPoint &p)  void AudioStrip::volLabelChanged(double val)        {        AutomationType at = ((MusECore::AudioTrack*)track)->automationType(); -      if(at == AUTO_WRITE || (MusEGlobal::audio->isPlaying() && at == AUTO_TOUCH)) +      if ( (at == AUTO_WRITE) || +           (at == AUTO_TOUCH && MusEGlobal::audio->isPlaying()) )          track->enableVolumeController(false);        double vol; @@ -549,8 +545,6 @@ void AudioStrip::volLabelChanged(double val)        //audio->msgSetVolume((MusECore::AudioTrack*)track, vol);        // p4.0.21 audio->msgXXX waits. Do we really need to?        ((MusECore::AudioTrack*)track)->setVolume(vol); -      MusEGlobal::song->controllerChange(track); -              ((MusECore::AudioTrack*)track)->startAutoRecord(MusECore::AC_VOLUME, vol);        } @@ -558,19 +552,18 @@ void AudioStrip::volLabelChanged(double val)  //   panChanged  //--------------------------------------------------------- -void AudioStrip::panChanged(double val) +void AudioStrip::panChanged(double val, int, bool shift_pressed)        {        AutomationType at = ((MusECore::AudioTrack*)track)->automationType(); -      if(at == AUTO_WRITE || (MusEGlobal::audio->isPlaying() && at == AUTO_TOUCH)) +      if ( (at == AUTO_WRITE) || +           (at == AUTO_TOUCH && MusEGlobal::audio->isPlaying()) )          track->enablePanController(false);        panVal = val;          //MusEGlobal::audio->msgSetPan(((MusECore::AudioTrack*)track), val);        // p4.0.21 MusEGlobal::audio->msgXXX waits. Do we really need to?        ((MusECore::AudioTrack*)track)->setPan(val); -      MusEGlobal::song->controllerChange(track); -       -      ((MusECore::AudioTrack*)track)->recordAutomation(MusECore::AC_PAN, val); +      if (!shift_pressed) ((MusECore::AudioTrack*)track)->recordAutomation(MusECore::AC_PAN, val);        }  //--------------------------------------------------------- @@ -580,15 +573,13 @@ void AudioStrip::panChanged(double val)  void AudioStrip::panPressed()        {        AutomationType at = ((MusECore::AudioTrack*)track)->automationType(); -      if(at == AUTO_WRITE || (at == AUTO_READ || at == AUTO_TOUCH)) +      if (at == AUTO_READ || at == AUTO_TOUCH || at == AUTO_WRITE)          track->enablePanController(false);        panVal = pan->value();          //MusEGlobal::audio->msgSetPan(((MusECore::AudioTrack*)track), panVal);        // p4.0.21 MusEGlobal::audio->msgXXX waits. Do we really need to?        ((MusECore::AudioTrack*)track)->setPan(panVal); -      MusEGlobal::song->controllerChange(track); -              ((MusECore::AudioTrack*)track)->startAutoRecord(MusECore::AC_PAN, panVal);        } @@ -598,7 +589,8 @@ void AudioStrip::panPressed()  void AudioStrip::panReleased()        { -      if(track->automationType() != AUTO_WRITE) +      AutomationType at = track->automationType(); +      if (at == AUTO_OFF || at == AUTO_READ || at == AUTO_TOUCH)          track->enablePanController(true);        ((MusECore::AudioTrack*)track)->stopAutoRecord(MusECore::AC_PAN, panVal);        } @@ -617,8 +609,9 @@ void AudioStrip::panRightClicked(const QPoint &p)  void AudioStrip::panLabelChanged(double val)        { -      AutomationType at = ((MusECore::AudioTrack*)track)->automationType(); -      if(at == AUTO_WRITE || (MusEGlobal::audio->isPlaying() && at == AUTO_TOUCH)) +      AutomationType at = ((MusECore::AudioTrack*)track)->automationType();  +      if ( (at == AUTO_WRITE) || +           (at == AUTO_TOUCH && MusEGlobal::audio->isPlaying()) )          track->enablePanController(false);        panVal = val; @@ -626,8 +619,6 @@ void AudioStrip::panLabelChanged(double val)        //MusEGlobal::audio->msgSetPan((MusECore::AudioTrack*)track, val);        // p4.0.21 MusEGlobal::audio->msgXXX waits. Do we really need to?        ((MusECore::AudioTrack*)track)->setPan(val); -      MusEGlobal::song->controllerChange(track); -              ((MusECore::AudioTrack*)track)->startAutoRecord(MusECore::AC_PAN, val);        } @@ -727,11 +718,10 @@ MusEGui::Knob* AudioStrip::addKnob(int type, int id, MusEGui::DoubleLabel** dlab        _curGridRow += 2;        connect(knob, SIGNAL(valueChanged(double,int)), pl, SLOT(setValue(double))); -      //connect(pl, SIGNAL(valueChanged(double, int)), SLOT(panChanged(double)));        if (type == 0) {              connect(pl, SIGNAL(valueChanged(double, int)), SLOT(panLabelChanged(double))); -            connect(knob, SIGNAL(sliderMoved(double,int)), SLOT(panChanged(double))); +            connect(knob, SIGNAL(sliderMoved(double,int,bool)), SLOT(panChanged(double,int,bool)));              connect(knob, SIGNAL(sliderPressed(int)), SLOT(panPressed()));              connect(knob, SIGNAL(sliderReleased(int)), SLOT(panReleased()));              connect(knob, SIGNAL(sliderRightClicked(const QPoint &, int)), SLOT(panRightClicked(const QPoint &))); @@ -894,9 +884,8 @@ AudioStrip::AudioStrip(QWidget* parent, MusECore::AudioTrack* at)        sl->setValue(MusECore::fast_log10(t->volume()) * 20.0);        connect(sl, SIGNAL(valueChanged(double,int)), SLOT(volLabelChanged(double))); -      //connect(sl, SIGNAL(valueChanged(double,int)), SLOT(volumeChanged(double)));        connect(slider, SIGNAL(valueChanged(double,int)), sl, SLOT(setValue(double))); -      connect(slider, SIGNAL(sliderMoved(double,int)), SLOT(volumeChanged(double))); +      connect(slider, SIGNAL(sliderMoved(double,int,bool)), SLOT(volumeChanged(double,int,bool)));        connect(slider, SIGNAL(sliderPressed(int)), SLOT(volumePressed()));        connect(slider, SIGNAL(sliderReleased(int)), SLOT(volumeReleased()));        connect(slider, SIGNAL(sliderRightClicked(const QPoint &, int)), SLOT(volumeRightClicked(const QPoint &))); diff --git a/muse2/muse/mixer/astrip.h b/muse2/muse/mixer/astrip.h index f5406652..c0df5360 100644 --- a/muse2/muse/mixer/astrip.h +++ b/muse2/muse/mixer/astrip.h @@ -94,10 +94,10 @@ class AudioStrip : public Strip {        void iRoutePressed();        void oRoutePressed();        void auxChanged(double, int); -      void volumeChanged(double); +      void volumeChanged(double,int,bool);        void volumePressed();        void volumeReleased(); -      void panChanged(double); +      void panChanged(double,int,bool);        void panPressed();        void panReleased();        void volLabelChanged(double); diff --git a/muse2/muse/mixer/panknob.cpp b/muse2/muse/mixer/panknob.cpp index dc2564a7..c54a112f 100644 --- a/muse2/muse/mixer/panknob.cpp +++ b/muse2/muse/mixer/panknob.cpp @@ -48,7 +48,6 @@ void PanKnob::valueChanged(double val)        //audio->msgSetPan(src, val);        // p4.0.21 audio->msgXXX waits. Do we really need to?        src->setPan(val); -      MusEGlobal::song->controllerChange(src);        }  } // namespace MusEGui diff --git a/muse2/muse/mpevent.cpp b/muse2/muse/mpevent.cpp index d3709b1f..a8596224 100644 --- a/muse2/muse/mpevent.cpp +++ b/muse2/muse/mpevent.cpp @@ -178,7 +178,7 @@ void MidiFifo::remove()  //    return true on fifo overflow  //--------------------------------------------------------- -bool MidiRecFifo::put(const MidiPlayEvent& event) +bool MidiRecFifo::put(const MidiRecordEvent& event)        {        if (size < MIDI_REC_FIFO_SIZE) {              fifo[wIndex] = event; @@ -193,9 +193,9 @@ bool MidiRecFifo::put(const MidiPlayEvent& event)  //   get  //--------------------------------------------------------- -MidiPlayEvent MidiRecFifo::get() +MidiRecordEvent MidiRecFifo::get()        { -      MidiPlayEvent event(fifo[rIndex]); +      MidiRecordEvent event(fifo[rIndex]);        rIndex = (rIndex + 1) % MIDI_REC_FIFO_SIZE;        --size;        return event; @@ -205,7 +205,7 @@ MidiPlayEvent MidiRecFifo::get()  //   peek  //--------------------------------------------------------- -const MidiPlayEvent& MidiRecFifo::peek(int n) +const MidiRecordEvent& MidiRecFifo::peek(int n)        {        int idx = (rIndex + n) % MIDI_REC_FIFO_SIZE;        return fifo[idx]; diff --git a/muse2/muse/mpevent.h b/muse2/muse/mpevent.h index 9b64f9cd..903a8126 100644 --- a/muse2/muse/mpevent.h +++ b/muse2/muse/mpevent.h @@ -111,6 +111,8 @@ class MEvent {  //---------------------------------------------------------  class MidiRecordEvent : public MEvent { +   private: +      unsigned int _tick; // To store tick when external sync is on, required besides frame.     public:        MidiRecordEvent() : MEvent() {}        MidiRecordEvent(const MEvent& e) : MEvent(e) {} @@ -121,6 +123,9 @@ class MidiRecordEvent : public MEvent {        MidiRecordEvent(unsigned t, int p, int type, EvData data)          : MEvent(t, p, type, data) {}        ~MidiRecordEvent() {} +       +      unsigned int tick() {return _tick;} +      void setTick(unsigned int tick) {_tick = tick;}        };  //--------------------------------------------------------- @@ -200,20 +205,19 @@ class MidiFifo {  //---------------------------------------------------------  //   MidiRecFifo -//   (Same as MidiFifo, but with a smaller size.)  //---------------------------------------------------------  class MidiRecFifo { -      MidiPlayEvent fifo[MIDI_REC_FIFO_SIZE]; +      MidiRecordEvent fifo[MIDI_REC_FIFO_SIZE];        volatile int size;        int wIndex;        int rIndex;     public:        MidiRecFifo()  { clear(); } -      bool put(const MidiPlayEvent& event);   // returns true on fifo overflow -      MidiPlayEvent get(); -      const MidiPlayEvent& peek(int = 0); +      bool put(const MidiRecordEvent& event);   // returns true on fifo overflow +      MidiRecordEvent get(); +      const MidiRecordEvent& peek(int = 0);        void remove();        bool isEmpty() const { return size == 0; }        void clear()         { size = 0, wIndex = 0, rIndex = 0; } diff --git a/muse2/muse/node.cpp b/muse2/muse/node.cpp index e56949aa..02264a37 100644 --- a/muse2/muse/node.cpp +++ b/muse2/muse/node.cpp @@ -401,10 +401,10 @@ void AudioTrack::copyData(unsigned pos, int dstChannels, int srcStartChan, int s    // precalculate stereo volume    double vol[2]; -  //double _volume = volume(); -  //double _pan = pan(); -  double _volume = controller()->value(AC_VOLUME, pos); -  double _pan    = controller()->value(AC_PAN,    pos); +  double _volume = controller()->value(AC_VOLUME, pos,  +                   !MusEGlobal::automation || automationType() == AUTO_OFF || !_volumeEnCtrl || !_volumeEn2Ctrl); +  double _pan    = controller()->value(AC_PAN, pos, +                   !MusEGlobal::automation || automationType() == AUTO_OFF || !_panEnCtrl || !_panEn2Ctrl);    vol[0] = _volume * (1.0 - _pan);    vol[1] = _volume * (1.0 + _pan); @@ -739,10 +739,10 @@ void AudioTrack::addData(unsigned pos, int dstChannels, int srcStartChan, int sr    // precalculate stereo volume    double vol[2]; -  //double _volume = volume(); -  //double _pan = pan(); -  double _volume = controller()->value(AC_VOLUME, pos); -  double _pan    = controller()->value(AC_PAN,    pos); +  double _volume = controller()->value(AC_VOLUME, pos,  +                   !MusEGlobal::automation || automationType() == AUTO_OFF || !_volumeEnCtrl || !_volumeEn2Ctrl); +  double _pan    = controller()->value(AC_PAN, pos, +                   !MusEGlobal::automation || automationType() == AUTO_OFF || !_panEnCtrl || !_panEn2Ctrl);    vol[0] = _volume * (1.0 - _pan);    vol[1] = _volume * (1.0 + _pan); diff --git a/muse2/muse/osc.cpp b/muse2/muse/osc.cpp index 0d4a1750..381e4acc 100644 --- a/muse2/muse/osc.cpp +++ b/muse2/muse/osc.cpp @@ -1092,7 +1092,7 @@ int OscDssiIF::oscControl(lo_arg** argv)    if(_oscSynthIF)    {      _oscSynthIF->oscControl(argv[0]->i, argv[1]->f); -    if (port<maxDssiPort) +    if (port<(int)maxDssiPort)        old_control[control_port_mapper->at(port)]=argv[1]->f;    } @@ -1170,7 +1170,7 @@ int OscEffectIF::oscControl(lo_arg** argv)    if(_oscPluginI)    {      _oscPluginI->oscControl(argv[0]->i, argv[1]->f); -    if (port<maxDssiPort) +    if (port<(int)maxDssiPort)        old_control[control_port_mapper->at(port)]=argv[1]->f;    } diff --git a/muse2/muse/part.cpp b/muse2/muse/part.cpp index a632bc9c..9950c362 100644 --- a/muse2/muse/part.cpp +++ b/muse2/muse/part.cpp @@ -864,17 +864,18 @@ void Song::cmdResizePart(Track* track, Part* oPart, unsigned int len, bool doClo                                unsigned event_endframe = event_startframe + e.lenFrame();                                if (event_endframe < new_partlength)                                      continue; -                              if (event_startframe > new_partlength) { // If event start was after the new length, remove it from part -                                    // Do not do port controller values and clone parts.  -                                    operations.push_back(UndoOp(UndoOp::DeleteEvent, e, nPart, false, false)); -                                    continue; -                                    } -                              if (event_endframe > new_partlength) { // If this event starts before new length and ends after, shrink it -                                    Event newEvent = e.clone(); -                                    newEvent.setLenFrame(new_partlength - event_startframe); -                                    // Do not do port controller values and clone parts.  -                                    operations.push_back(UndoOp(UndoOp::ModifyEvent, newEvent, e, nPart, false,false)); -                                    } +// REMOVE Tim.  +//                               if (event_startframe > new_partlength) { // If event start was after the new length, remove it from part +//                                     // Do not do port controller values and clone parts.  +//                                     operations.push_back(UndoOp(UndoOp::DeleteEvent, e, nPart, false, false)); +//                                     continue; +//                                     } +//                               if (event_endframe > new_partlength) { // If this event starts before new length and ends after, shrink it +//                                     Event newEvent = e.clone(); +//                                     newEvent.setLenFrame(new_partlength - event_startframe); +//                                     // Do not do port controller values and clone parts.  +//                                     operations.push_back(UndoOp(UndoOp::ModifyEvent, newEvent, e, nPart, false,false)); +//                                     }                                }                          nPart->setLenFrame(new_partlength);                          // Do not do port controller values and clone parts.  @@ -893,19 +894,20 @@ void Song::cmdResizePart(Track* track, Part* oPart, unsigned int len, bool doClo                            iEvent i = el->end();                            i--;                            Event last = i->second; -                          unsigned last_start = last.frame(); +// REMOVE Tim.              unsigned last_start = last.frame();                            MusECore::SndFileR file = last.sndFile();                            if (file.isNull())                                  return; -                          unsigned clipframes = (file.samples() - last.spos());// / file.channels(); +//                          unsigned clipframes = (file.samples() - last.spos());// / file.channels();                            Event newEvent = last.clone(); -                          unsigned new_eventlength = new_partlength - last_start; -                          if (new_eventlength > clipframes) // Shrink event length if new partlength exceeds last clip -                                new_eventlength = clipframes; -   -                          newEvent.setLenFrame(new_eventlength); +// REMOVE Tim.  +//                          unsigned new_eventlength = new_partlength - last_start; +//                           if (new_eventlength > clipframes) // Shrink event length if new partlength exceeds last clip +//                                 new_eventlength = clipframes; +//   +//                          newEvent.setLenFrame(new_eventlength);                            // Do not do port controller values and clone parts.                             operations.push_back(UndoOp(UndoOp::ModifyEvent, newEvent, last, nPart, false, false));                          }   @@ -1199,13 +1201,12 @@ WavePart* WavePart::clone() const        return new WavePart(*this);        } -  //---------------------------------------------------------  //   hasHiddenEvents  //   Returns combination of HiddenEventsType enum.  //--------------------------------------------------------- -int Part::hasHiddenEvents()  +int MidiPart::hasHiddenEvents()   {    unsigned len = lenTick(); @@ -1222,7 +1223,27 @@ int Part::hasHiddenEvents()    return _hiddenEvents;  } +//--------------------------------------------------------- +//   hasHiddenEvents +//   Returns combination of HiddenEventsType enum. +//--------------------------------------------------------- +int WavePart::hasHiddenEvents()  +{ +  unsigned len = lenFrame(); +   +  // TODO: For now, we don't support events before the left border, only events past the right border. +  for(iEvent ev=events()->begin(); ev!=events()->end(); ev++) +  { +    if(ev->second.endFrame() > len) +    { +      _hiddenEvents = RightEventsHidden;  // Cache the result for later. +      return _hiddenEvents; +    }   +  } +  _hiddenEvents = NoEventsHidden;  // Cache the result for later. +  return _hiddenEvents; +}  //---------------------------------------------------------  //   ClonePart diff --git a/muse2/muse/part.h b/muse2/muse/part.h index f2bc342b..357ec1db 100644 --- a/muse2/muse/part.h +++ b/muse2/muse/part.h @@ -76,14 +76,13 @@ class Part : public PosLen {        bool _mute;        int _colorIndex; -      int _hiddenEvents;   // Combination of HiddenEventsType. -                        protected:        Track* _track;        EventList* _events;        Part* _prevClone;        Part* _nextClone; - +      int _hiddenEvents;   // Combination of HiddenEventsType. +                        public:        Part(Track*);        Part(Track*, EventList*); @@ -114,7 +113,7 @@ class Part : public PosLen {        void setNextClone(Part* p)       { _nextClone = p; }        // Returns combination of HiddenEventsType enum. -      int hasHiddenEvents(); +      virtual int hasHiddenEvents() = 0;        // If repeated calls to hasHiddenEvents() are desired, then to avoid re-iteration of the event list,         //  call this after hasHiddenEvents().        int cachedHasHiddenEvents() const { return _hiddenEvents; } @@ -140,7 +139,9 @@ class MidiPart : public Part {        virtual ~MidiPart() {}        virtual MidiPart* clone() const;        MidiTrack* track() const   { return (MidiTrack*)Part::track(); } - +      // Returns combination of HiddenEventsType enum. +      int hasHiddenEvents(); +              virtual void dump(int n = 0) const;        }; @@ -161,6 +162,8 @@ class WavePart : public Part {        virtual ~WavePart() {}        virtual WavePart* clone() const;        WaveTrack* track() const   { return (WaveTrack*)Part::track(); } +      // Returns combination of HiddenEventsType enum. +      int hasHiddenEvents();        virtual void dump(int n = 0) const;        }; diff --git a/muse2/muse/plugin.cpp b/muse2/muse/plugin.cpp index 8bf35143..e8b0489c 100644 --- a/muse2/muse/plugin.cpp +++ b/muse2/muse/plugin.cpp @@ -32,6 +32,7 @@  #include <QButtonGroup>  #include <QCheckBox>  #include <QComboBox> +#include <QCursor>  #include <QDir>  #include <QFile>  #include <QGridLayout> @@ -78,6 +79,9 @@  // Turn on debugging messages.  //#define PLUGIN_DEBUGIN  +// Turn on constant stream of debugging messages. +//#define PLUGIN_DEBUGIN_PROCESS  +  namespace MusEGlobal {  MusECore::PluginList plugins;  } @@ -119,7 +123,7 @@ bool ladspa2MidiControlValues(const LADSPA_Descriptor* plugin, unsigned long por      *min = 0;      *max = 1; -    *def = (int)lrint(fdef); +    *def = (int)lrintf(fdef);      return hasdef;    } @@ -156,8 +160,8 @@ bool ladspa2MidiControlValues(const LADSPA_Descriptor* plugin, unsigned long por      fmax = 1.0;    frng = fmax - fmin; -  imin = lrint(fmin);   -  imax = lrint(fmax);   +  imin = lrintf(fmin);   +  imax = lrintf(fmax);      int ctlmn = 0;    int ctlmx = 127; @@ -230,7 +234,7 @@ bool ladspa2MidiControlValues(const LADSPA_Descriptor* plugin, unsigned long por      *min = imin;      *max = imax; -    *def = (int)lrint(fdef); +    *def = (int)lrintf(fdef);      return hasdef;    } @@ -244,7 +248,7 @@ bool ladspa2MidiControlValues(const LADSPA_Descriptor* plugin, unsigned long por    // FIXME: TODO: Incorrect... Fix this somewhat more trivial stuff later.... -  *def = (int)lrint(fdef) + bias; +  *def = (int)lrintf(fdef) + bias;    #ifdef PLUGIN_DEBUGIN     printf("ladspa2MidiControlValues: setting default:%d\n", *def); @@ -305,7 +309,7 @@ float midi2LadspaValue(const LADSPA_Descriptor* plugin, unsigned long port, int      fmax = 1.0;    frng = fmax - fmin; -  imin = lrint(fmin);   +  imin = lrintf(fmin);      if(desc & LADSPA_HINT_TOGGLED)     { @@ -619,40 +623,6 @@ void ladspaControlRange(const LADSPA_Descriptor* plugin, unsigned long port, flo              *max = 1.0;        } -// DELETETHIS 35 -/* -//--------------------------------------------------------- -//   PluginBase -//--------------------------------------------------------- - -//--------------------------------------------------------- -//   range -//--------------------------------------------------------- - -void PluginBase::range(unsigned long i, float* min, float* max) const -      { -      LADSPA_PortRangeHint range = plugin->PortRangeHints[i]; -      LADSPA_PortRangeHintDescriptor desc = range.HintDescriptor; -      if (desc & LADSPA_HINT_TOGGLED) { -            *min = 0.0; -            *max = 1.0; -            return; -            } -      float m = 1.0; -      if (desc & LADSPA_HINT_SAMPLE_RATE) -            m = float(MusEGlobal::sampleRate); - -      if (desc & LADSPA_HINT_BOUNDED_BELOW) -            *min =  range.LowerBound * m; -      else -            *min = 0.0; -      if (desc & LADSPA_HINT_BOUNDED_ABOVE) -            *max =  range.UpperBound * m; -      else -            *max = 1.0; -      } -*/ -  //---------------------------------------------------------  //   Plugin  //--------------------------------------------------------- @@ -792,8 +762,7 @@ int Plugin::incReferences(int val)      if(dssi)      {        const DSSI_Descriptor* descr; -      //for(int i = 0;; ++i)  -      for(unsigned long i = 0;; ++i)     // p4.0.21 +      for(unsigned long i = 0;; ++i)             {          descr = dssi(i);          if(descr == NULL) @@ -817,7 +786,7 @@ int Plugin::incReferences(int val)        if(ladspadf)        {          const LADSPA_Descriptor* descr; -        for(unsigned long i = 0;; ++i)       // p4.0.21 +        for(unsigned long i = 0;; ++i)                 {            descr = ladspadf(i);            if(descr == NULL) @@ -913,7 +882,7 @@ int Plugin::incReferences(int val)  void Plugin::range(unsigned long i, float* min, float* max) const        { -      ladspaControlRange(plugin, i, min, max);  // p4.0.20 +      ladspaControlRange(plugin, i, min, max);          }  //--------------------------------------------------------- @@ -922,59 +891,9 @@ void Plugin::range(unsigned long i, float* min, float* max) const  float Plugin::defaultValue(unsigned long port) const  { -    // p4.0.21      float val;      ladspaDefaultValue(plugin, port, &val);      return val; -     -    // DELETETHIS 50 -    /* -    if(port >= plugin->PortCount)  -      return 0.0; -       -    LADSPA_PortRangeHint range = plugin->PortRangeHints[port]; -    LADSPA_PortRangeHintDescriptor rh = range.HintDescriptor; -    //double val = 1.0; -    float val = 1.0;  -    if (LADSPA_IS_HINT_DEFAULT_MINIMUM(rh)) -          val = range.LowerBound; -    else if (LADSPA_IS_HINT_DEFAULT_LOW(rh)) -          if (LADSPA_IS_HINT_LOGARITHMIC(range.HintDescriptor)) -                //val = exp(fast_log10(range.LowerBound) * .75 + -                //    log(range.UpperBound) * .25); -                val = expf(fast_log10(range.LowerBound) * .75 + -                    logf(range.UpperBound) * .25); -          else -                val = range.LowerBound*.75 + range.UpperBound*.25; -    else if (LADSPA_IS_HINT_DEFAULT_MIDDLE(rh)) -          if (LADSPA_IS_HINT_LOGARITHMIC(range.HintDescriptor)) -                //val = exp(log(range.LowerBound) * .5 + -                //    log(range.UpperBound) * .5); -                val = expf(logf(range.LowerBound) * .5 + -                    logf(range.UpperBound) * .5); -          else -                val = range.LowerBound*.5 + range.UpperBound*.5; -    else if (LADSPA_IS_HINT_DEFAULT_HIGH(rh)) -          if (LADSPA_IS_HINT_LOGARITHMIC(range.HintDescriptor)) -                //val = exp(log(range.LowerBound) * .25 + -                //    log(range.UpperBound) * .75); -                val = expf(logf(range.LowerBound) * .25 + -                    logf(range.UpperBound) * .75); -          else -                val = range.LowerBound*.25 + range.UpperBound*.75; -    else if (LADSPA_IS_HINT_DEFAULT_MAXIMUM(rh)) -          val = range.UpperBound; -    else if (LADSPA_IS_HINT_DEFAULT_0(rh)) -          val = 0.0; -    else if (LADSPA_IS_HINT_DEFAULT_1(rh)) -          val = 1.0; -    else if (LADSPA_IS_HINT_DEFAULT_100(rh)) -          val = 100.0; -    else if (LADSPA_IS_HINT_DEFAULT_440(rh)) -          val = 440.0; -           -    return val;       -    */  }  //--------------------------------------------------------- @@ -1013,7 +932,7 @@ static void loadPluginLib(QFileInfo* fi)    if(dssi)    {      const DSSI_Descriptor* descr; -    for (unsigned long i = 0;; ++i)  // p4.0.21 +    for (unsigned long i = 0;; ++i)        {        descr = dssi(i);        if (descr == 0) @@ -1060,7 +979,7 @@ static void loadPluginLib(QFileInfo* fi)      }      const LADSPA_Descriptor* descr; -    for (unsigned long i = 0;; ++i)       // p4.0.21 +    for (unsigned long i = 0;; ++i)             {        descr = ladspa(i);        if (descr == NULL) @@ -1224,6 +1143,76 @@ Pipeline::~Pipeline()        }  //--------------------------------------------------------- +//   addScheduledControlEvent +//   track_ctrl_id is the fully qualified track audio controller number +//   Returns true if event cannot be delivered +//--------------------------------------------------------- + +bool Pipeline::addScheduledControlEvent(int track_ctrl_id, float val, unsigned frame)  +{ +  // If a track controller, or the special dssi synth controller block, just return. +  if(track_ctrl_id < AC_PLUGIN_CTL_BASE || track_ctrl_id >= (int)genACnum(MAX_PLUGINS, 0))  +    return true; +  int rack_idx = (track_ctrl_id - AC_PLUGIN_CTL_BASE) >> AC_PLUGIN_CTL_BASE_POW; +  for (int i = 0; i < PipelineDepth; ++i) +  { +    PluginI* p = (*this)[i]; +    if(p && p->id() == rack_idx) +      return p->addScheduledControlEvent(track_ctrl_id & AC_PLUGIN_CTL_ID_MASK, val, frame); +  } +  return true; +} +       +//--------------------------------------------------------- +//   controllersEnabled +//   Returns whether automation control stream is enabled or disabled.  +//   Used during automation recording to inhibit gui controls +//--------------------------------------------------------- + +void Pipeline::controllersEnabled(int track_ctrl_id, bool* en1, bool* en2) +{ +  // If a track controller, or the special dssi synth controller block, just return. +  if(track_ctrl_id < AC_PLUGIN_CTL_BASE || track_ctrl_id >= (int)genACnum(MAX_PLUGINS, 0))  +    return; +  int rack_idx = (track_ctrl_id - AC_PLUGIN_CTL_BASE) >> AC_PLUGIN_CTL_BASE_POW; +  for (int i = 0; i < PipelineDepth; ++i) +  { +    PluginI* p = (*this)[i]; +    if(p && p->id() == rack_idx) +    { +      if(en1) +        *en1 = p->controllerEnabled(track_ctrl_id & AC_PLUGIN_CTL_ID_MASK); +      if(en2) +        *en2 = p->controllerEnabled2(track_ctrl_id & AC_PLUGIN_CTL_ID_MASK); +      return; +    } +  } +} + +//--------------------------------------------------------- +//   enableController +//   Enable or disable gui automation control stream.  +//   Used during automation recording to inhibit gui controls +//--------------------------------------------------------- + +void Pipeline::enableController(int track_ctrl_id, bool en)  +{ +  // If a track controller, or the special dssi synth controller block, just return. +  if(track_ctrl_id < AC_PLUGIN_CTL_BASE || track_ctrl_id >= (int)genACnum(MAX_PLUGINS, 0))  +    return; +  int rack_idx = (track_ctrl_id - AC_PLUGIN_CTL_BASE) >> AC_PLUGIN_CTL_BASE_POW; +  for (int i = 0; i < PipelineDepth; ++i) +  { +    PluginI* p = (*this)[i]; +    if(p && p->id() == rack_idx) +    { +      p->enableController(track_ctrl_id & AC_PLUGIN_CTL_ID_MASK, en); +      return; +    } +  } +} +       +//---------------------------------------------------------  //   setChannels  //--------------------------------------------------------- @@ -1524,6 +1513,39 @@ PluginIBase::~PluginIBase()      delete _gui;  }  +//--------------------------------------------------------- +//   addScheduledControlEvent +//   i is the specific index of the control input port +//   Returns true if event cannot be delivered +//--------------------------------------------------------- + +bool PluginIBase::addScheduledControlEvent(unsigned long i, float val, unsigned frame)  +{  +  if(i >= parameters()) +  { +    printf("PluginIBase::addScheduledControlEvent param number %lu out of range of ports:%lu\n", i, parameters()); +    return true; +  } +  ControlEvent ce; +  ce.unique = false; +  ce.idx = i; +  ce.value = val; +  // Time-stamp the event. This does a possibly slightly slow call to gettimeofday via timestamp(). +  //  timestamp() is more or less an estimate of the current frame. (This is exactly how ALSA events  +  //  are treated when they arrive in our ALSA driver.)  +  //ce.frame = MusEGlobal::audio->timestamp();   +  // p4.0.23 timestamp() is circular, which is making it impossible to deal with 'modulo' events which  +  //  slip in 'under the wire' before processing the ring buffers. So try this linear timestamp instead: +  ce.frame = frame;   +   +  if(_controlFifo.put(ce)) +  { +    fprintf(stderr, "PluginIBase::addScheduledControlEvent: fifo overflow: in control number:%lu\n", i); +    return true; +  } +  return false; +}      +  QString PluginIBase::dssi_ui_filename() const   {     QString libr(lib()); @@ -1658,8 +1680,6 @@ void PluginI::updateControllers()    for(unsigned long i = 0; i < controlPorts; ++i)       _track->setPluginCtrlVal(genACnum(_id, i), controls[i].val);  // TODO A faster bulk message -    -  MusEGlobal::song->controllerChange(_track);  }  //--------------------------------------------------------- @@ -1698,7 +1718,7 @@ void PluginI::setChannels(int c)                    }              } -      unsigned long curPort = 0;      // p4.0.21 +      unsigned long curPort = 0;              unsigned long curOutPort = 0;        unsigned long ports   = _plugin->ports();        for (unsigned long k = 0; k < ports; ++k)  @@ -1733,27 +1753,7 @@ void PluginI::setChannels(int c)  void PluginI::setParam(unsigned long i, float val)   {  -  if(i >= _plugin->_controlInPorts) -  { -    printf("PluginI::setParameter param number %lu out of range of ports:%lu\n", i, _plugin->_controlInPorts); -    return; -  } -  ControlEvent ce; -  ce.unique = false; -  ce.idx = i; -  ce.value = val; -  // Time-stamp the event. This does a possibly slightly slow call to gettimeofday via timestamp(). -  //  timestamp() is more or less an estimate of the current frame. (This is exactly how ALSA events  -  //  are treated when they arrive in our ALSA driver.)  -  //ce.frame = MusEGlobal::audio->timestamp();   -  // p4.0.23 timestamp() is circular, which is making it impossible to deal with 'modulo' events which  -  //  slip in 'under the wire' before processing the ring buffers. So try this linear timestamp instead: -  ce.frame = MusEGlobal::audio->curFrame();   -   -  if(_controlFifo.put(ce)) -  { -    fprintf(stderr, "PluginI::setParameter: fifo overflow: in control number:%lu\n", i); -  } +  addScheduledControlEvent(i, val, MusEGlobal::audio->curFrame());  }       //--------------------------------------------------------- @@ -1865,7 +1865,7 @@ bool PluginI::initPluginInstance(Plugin* plug, int c)          {            if(pd & LADSPA_PORT_INPUT)            { -            float val = _plugin->defaultValue(k);    // p4.0.21 +            float val = _plugin->defaultValue(k);                  controls[curPort].val    = val;              controls[curPort].tmpVal = val;              controls[curPort].enCtrl  = true; @@ -1917,11 +1917,11 @@ bool PluginI::initPluginInstance(Plugin* plug, int c)  void PluginI::connect(unsigned long ports, unsigned long offset, float** src, float** dst)        { -      unsigned long port = 0;  // p4.0.21 +      unsigned long port = 0;          for (int i = 0; i < instances; ++i) {              for (unsigned long k = 0; k < _plugin->ports(); ++k) {                    if (isAudioIn(k)) { -                        _plugin->connectPort(handle[i], k, src[port] + offset);     // p4.0.21 +                        _plugin->connectPort(handle[i], k, src[port] + offset);                               port = (port + 1) % ports;                          }                    } @@ -1930,7 +1930,7 @@ void PluginI::connect(unsigned long ports, unsigned long offset, float** src, fl        for (int i = 0; i < instances; ++i) {              for (unsigned long k = 0; k < _plugin->ports(); ++k) {                    if (isAudioOut(k)) { -                        _plugin->connectPort(handle[i], k, dst[port] + offset);     // p4.0.21 +                        _plugin->connectPort(handle[i], k, dst[port] + offset);                               port = (port + 1) % ports;  // overwrite output?                          }                    } @@ -1979,7 +1979,7 @@ bool PluginI::setControl(const QString& s, float val)        {        for (unsigned long i = 0; i < controlPorts; ++i) {              if (_plugin->portName(controls[i].idx) == s) { -                  setParam(i, val);     // p4.0.21 +                  setParam(i, val);                         return false;                    }              } @@ -1997,7 +1997,7 @@ void PluginI::writeConfiguration(int level, Xml& xml)        xml.tag(level++, "plugin file=\"%s\" label=\"%s\" channel=\"%d\"",           Xml::xmlString(_plugin->lib()).toLatin1().constData(), Xml::xmlString(_plugin->label()).toLatin1().constData(), channel); -      for (unsigned long i = 0; i < controlPorts; ++i) {          // p4.0.21 +      for (unsigned long i = 0; i < controlPorts; ++i) {                        unsigned long idx = controls[i].idx;              QString s("control name=\"%1\" val=\"%2\" /");              xml.tag(level, s.arg(Xml::xmlString(_plugin->portName(idx)).toLatin1().constData()).arg(controls[i].tmpVal).toLatin1().constData()); @@ -2040,7 +2040,7 @@ bool PluginI::loadControl(Xml& xml)                          if (tag == "name")                                name = xml.s2();                          else if (tag == "val") -                              val = xml.s2().toFloat();     // p4.0.21 +                              val = xml.s2().toFloat();                               break;                    case Xml::TagEnd:                          if (tag == "control") { @@ -2102,7 +2102,7 @@ bool PluginI::readConfiguration(Xml& xml, bool readPreset)                                      xml.parse1();                                      printf("Error initializing plugin instance (%s, %s)\n",                                         file.toLatin1().constData(), label.toLatin1().constData()); -                                    //break;      // Don't break - let it read any control tags. DELETETHIS +                                    //break;      // Don't break - let it read any control tags.                                       }                                   }                                    } @@ -2352,28 +2352,152 @@ void PluginI::apply(unsigned long n, unsigned long ports, float** bufIn, float**        if(min_per > n)          min_per = n; -      // Process automation control values now. -      // TODO: This needs to be respect frame resolution. Put this inside the sample loop below. -      if(MusEGlobal::automation && _track && _track->automationType() != AUTO_OFF && _id != -1) +      // CtrlListList* cll = NULL;  // WIP +      AutomationType at = AUTO_OFF; +      if(_track)        { -        for(unsigned long k = 0; k < controlPorts; ++k) -        { -          if(controls[k].enCtrl && controls[k].en2Ctrl ) -            controls[k].tmpVal = _track->controller()->value(genACnum(_id, k), MusEGlobal::audio->pos().frame()); -        }       +        at = _track->automationType(); +        //cll = _track->controller();  // WIP        } -         +      bool no_auto = !MusEGlobal::automation || at == AUTO_OFF; +              while(sample < n)        {          // nsamp is the number of samples the plugin->process() call will be supposed to do          unsigned long nsamp = usefixedrate ? fixedsize : n - sample; +        // +        // Process automation control values, while also determining the maximum acceptable  +        //  size of this run. Further processing, from FIFOs for example, can lower the size  +        //  from there, but this section determines where the next highest maximum frame  +        //  absolutely needs to be for smooth playback of the controller value stream... +        // +        if(_track && _id != -1 && ports != 0) // Don't bother if not 'running'. +        { +          unsigned long frame = MusEGlobal::audio->pos().frame() + sample; +          int nextFrame; +          //double val;  // WIP +          for(unsigned long k = 0; k < controlPorts; ++k) +          { + +             +#if 0  // WIP - Work in progress. Tim.     + +            ciCtrlList icl = cll->find(genACnum(_id, k)); +            if(icl == cll->end()) +              continue; +            CtrlList* cl = icl->second; +            if(no_auto || !controls[k].enCtrl || !controls[k].en2Ctrl || cl->empty())  +            { +              nextFrame = -1; +              val = cl->curVal(); +            } +            else +            { +              ciCtrl i = cl->upper_bound(frame); // get the index after current frame +              if (i == cl->end()) { // if we are past all items just return the last value +                    --i; +                    nextFrame = -1; +                    val = i->second.val; +                    } +              else if(cl->mode() == CtrlList::DISCRETE) +              { +                if(i == cl->begin()) +                { +                    nextFrame = i->second.frame; +                    val = i->second.val; +                }   +                else +                {   +                  nextFrame = i->second.frame; +                  --i; +                  val = i->second.val; +                }   +              } +              else {                  // INTERPOLATE +                if (i == cl->begin()) { +                    nextFrame = i->second.frame; +                    val = i->second.val; +                } +                else { +                    int frame2 = i->second.frame; +                    double val2 = i->second.val; +                    --i; +                    int frame1 = i->second.frame; +                    double val1   = i->second.val; + +                     +                    if(val2 != val1) +                      nextFrame = 0; // Zero signifies the next frame should be determined by caller. +                    else +                      nextFrame = frame2; +                     +                    if (cl->valueType() == VAL_LOG) { +                      val1 = 20.0*fast_log10(val1); +                      if (val1 < MusEGlobal::config.minSlider) +                        val1=MusEGlobal::config.minSlider; +                      val2 = 20.0*fast_log10(val2); +                      if (val2 < MusEGlobal::config.minSlider) +                        val2=MusEGlobal::config.minSlider; +                    } + +                    val2  -= val1; +                    val1 += (double(frame - frame1) * val2)/double(frame2 - frame1); +             +                    if (cl->valueType() == VAL_LOG) { +                      val1 = exp10(val1/20.0); +                    } + +                    val = val1; +                  } +              } +            } +             +            controls[k].tmpVal = val; +             +             +#else             +            controls[k].tmpVal = _track->controller()->value(genACnum(_id, k), frame, +                                    no_auto || !controls[k].enCtrl || !controls[k].en2Ctrl, +                                    &nextFrame); +#endif             +             +             +#ifdef PLUGIN_DEBUGIN_PROCESS +            printf("PluginI::apply k:%lu sample:%lu frame:%lu nextFrame:%d nsamp:%lu \n", k, sample, frame, nextFrame, nsamp); +#endif +            if(MusEGlobal::audio->isPlaying() && !usefixedrate && nextFrame != -1) +            { +              // Returned value of nextFrame can be zero meaning caller replaces with some (constant) value. +              unsigned long samps = (unsigned long)nextFrame; +              if(samps > frame + min_per) +              { +                unsigned long diff = samps - frame; +                unsigned long mask = min_per-1;   // min_per must be power of 2 +                samps = diff & ~mask; +                if((diff & mask) != 0) +                  samps += min_per; +              } +              else +                samps = min_per; +               +              if(samps < nsamp) +                nsamp = samps; +            } +          } +           +#ifdef PLUGIN_DEBUGIN_PROCESS +          printf("PluginI::apply sample:%lu nsamp:%lu\n", sample, nsamp); +#endif +        } +         +        // +        // Process all control ring buffer items valid for this time period... +        //          bool found = false;          unsigned long frame = 0;           unsigned long index = 0;          unsigned long evframe;  - -        // Get all control ring buffer items valid for this time period...          while(!_controlFifo.isEmpty())          {            ControlEvent v = _controlFifo.peek();  @@ -2398,9 +2522,10 @@ void PluginI::apply(unsigned long n, unsigned long ports, float** bufIn, float**            // but stop after a control event was found (then process(),            // then loop here again), but ensure that process() must process            // at least min_per frames. -          if(evframe >= n -              || (found && !v.unique && (evframe - sample >= min_per)) -              || (usefixedrate && found && v.unique && v.idx == index)) +          if(evframe >= n                                                               // Next events are for a later period. +              || (!usefixedrate && !found && !v.unique && (evframe - sample >= nsamp))  // Next events are for a later run in this period. (Autom took prio.) +              || (found && !v.unique && (evframe - sample >= min_per))                  // Eat up events within minimum slice - they're too close. +              || (usefixedrate && found && v.unique && v.idx == index))                 // Special for dssi-vst: Fixed rate and must reply to all.              break;            _controlFifo.remove();               // Done with the ring buffer's item. Remove it. @@ -2415,27 +2540,20 @@ void PluginI::apply(unsigned long n, unsigned long ports, float** bufIn, float**            // Need to update the automation value, otherwise it overwrites later with the last automation value.            if(_track && _id != -1) -          { -            // We're in the audio thread context: no need to send a message, just modify directly.              _track->setPluginCtrlVal(genACnum(_id, v.idx), v.value); -             -            /* Recording automation is done immediately in the         * -             * OSC control handler, because we don't want any delay.   * -             * we might want to handle dssi-vst synthes here, however! */ -          }            }          // Now update the actual values from the temporary values...          for(unsigned long k = 0; k < controlPorts; ++k)            controls[k].val = controls[k].tmpVal; -        if(found && !usefixedrate) -          nsamp = frame - sample; +        if(found && !usefixedrate) // If a control FIFO item was found, takes priority over automation controller stream. +          nsamp = frame - sample;   -        if(sample + nsamp >= n)         // Safety check. +        if(sample + nsamp >= n)    // Safety check.            nsamp = n - sample;  -        // Don't allow zero-length runs. This could/should be checked in the control loop instead. +        // TODO: Don't allow zero-length runs. This could/should be checked in the control loop instead.          // Note this means it is still possible to get stuck in the top loop (at least for a while).          if(nsamp == 0)            continue; @@ -2563,13 +2681,8 @@ int PluginI::oscUpdate()        usleep(300000);        // Send current control values. -      //unsigned long ports = controlPorts; DELETETHIS 2 -      //for(int i = 0; i < controlPorts; ++i)         for(unsigned long i = 0; i < controlPorts; ++i)         { -        //unsigned long k = synth->pIdx(i); DELETETHIS 2 -        //_oscIF.oscSendControl(k, controls[i], true /*force*/); -        //printf("PluginI::oscUpdate() sending control:%lu val:%f\n", i, controls[i].val);          _oscif.oscSendControl(controls[i].idx, controls[i].val, true /*force*/);          // Avoid overloading the GUI if there are lots and lots of ports.           if((i+1) % 50 == 0) @@ -2638,7 +2751,6 @@ int PluginI::oscControl(unsigned long port, float value)      }    }    */ -  // p4.0.21    ControlEvent ce;    ce.unique = _plugin->_isDssiVst;   // Special for messages from vst gui to host - requires processing every message.    ce.idx = cport; @@ -2693,27 +2805,6 @@ int PluginI::oscControl(unsigned long port, float value)    }    */ -// DELETETHIS 20 -#if 0 -      int port = argv[0]->i; -      LADSPA_Data value = argv[1]->f; - -      if (port < 0 || port > instance->plugin->descriptor->LADSPA_Plugin->PortCount) { -            fprintf(stderr, "MusE: OSC: %s port number (%d) is out of range\n", -               instance->friendly_name, port); -            return 0; -            } -      if (instance->pluginPortControlInNumbers[port] == -1) { -            fprintf(stderr, "MusE: OSC: %s port %d is not a control in\n", -               instance->friendly_name, port); -            return 0; -            } -      pluginControlIns[instance->pluginPortControlInNumbers[port]] = value; -      if (verbose) { -            printf("MusE: OSC: %s port %d = %f\n", -               instance->friendly_name, port, value); -            } -#endif        return 0;        } @@ -2795,7 +2886,7 @@ PluginDialog::PluginDialog(QWidget* parent)        ok_lo->addWidget(cancelB);        QGroupBox* plugSelGroup = new QGroupBox(this); -      plugSelGroup->setTitle("Show plugs:"); +      plugSelGroup->setTitle(tr("Show plugs:"));        plugSelGroup->setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Maximum);        QGridLayout* psl = new QGridLayout;        plugSelGroup->setLayout(psl); @@ -2972,7 +3063,7 @@ void PluginDialog::fillPlugs()      QString type_name;      pList->clear();      for (MusECore::iPlugin i = MusEGlobal::plugins.begin(); i != MusEGlobal::plugins.end(); ++i) { -          unsigned long ai = i->inports();       // p4.0.21 +          unsigned long ai = i->inports();                   unsigned long ao = i->outports();            unsigned long ci = i->controlInPorts();            unsigned long co = i->controlOutPorts(); @@ -3115,7 +3206,7 @@ PluginGui::PluginGui(MusECore::PluginIBase* p)                    const char* name = ba.constData();                    if (*name !='P')                          continue; -                  unsigned long parameter;                        // p4.0.21 +                  unsigned long parameter;                                            int rv = sscanf(name, "P%lu", ¶meter);                    if(rv != 1)                      continue; @@ -3126,15 +3217,17 @@ PluginGui::PluginGui(MusECore::PluginIBase* p)              nobj = 0;              QSignalMapper* mapper = new QSignalMapper(this); -            // FIXME: There's no unsigned for gui params. We would need to limit nobj to MAXINT.    // p4.0.21 -            // FIXME: Our MusEGui::Slider class uses doubles for values, giving some problems with float conversion.    // p4.0.21 +            // FIXME: There's no unsigned for gui params. We would need to limit nobj to MAXINT.     +            // FIXME: Our MusEGui::Slider class uses doubles for values, giving some problems with float conversion.                  connect(mapper, SIGNAL(mapped(int)), SLOT(guiParamChanged(int))); -            QSignalMapper* mapperPressed = new QSignalMapper(this); -            QSignalMapper* mapperReleased = new QSignalMapper(this); +            QSignalMapper* mapperPressed        = new QSignalMapper(this); +            QSignalMapper* mapperReleased       = new QSignalMapper(this); +            QSignalMapper* mapperContextMenuReq = new QSignalMapper(this);              connect(mapperPressed, SIGNAL(mapped(int)), SLOT(guiParamPressed(int)));              connect(mapperReleased, SIGNAL(mapped(int)), SLOT(guiParamReleased(int))); +            connect(mapperContextMenuReq, SIGNAL(mapped(int)), SLOT(guiContextMenuReq(int)));              for (it = l.begin(); it != l.end(); ++it) {                    obj = *it; @@ -3142,7 +3235,7 @@ PluginGui::PluginGui(MusECore::PluginIBase* p)                    const char* name = ba.constData();                    if (*name !='P')                          continue; -                  unsigned long parameter;                         // p4.0.21 +                  unsigned long parameter;                                             int rv = sscanf(name, "P%lu", ¶meter);                  if(rv != 1)                      continue; @@ -3150,6 +3243,7 @@ PluginGui::PluginGui(MusECore::PluginIBase* p)                    mapper->setMapping(obj, nobj);                    mapperPressed->setMapping(obj, nobj);                    mapperReleased->setMapping(obj, nobj); +                  mapperContextMenuReq->setMapping(obj, nobj);                    gw[nobj].widget = (QWidget*)obj;                    gw[nobj].param  = parameter; @@ -3159,15 +3253,15 @@ PluginGui::PluginGui(MusECore::PluginIBase* p)                          gw[nobj].type = GuiWidgets::SLIDER;                          ((Slider*)obj)->setId(nobj);                          ((Slider*)obj)->setCursorHoming(true); -                        for(unsigned long i = 0; i < nobj; i++)             // p4.0.21 +                        for(unsigned long i = 0; i < nobj; i++)                                       {                            if(gw[i].type == GuiWidgets::DOUBLE_LABEL && gw[i].param == parameter)                              ((DoubleLabel*)gw[i].widget)->setSlider((Slider*)obj);                          } -                        connect(obj, SIGNAL(sliderMoved(double,int)), mapper, SLOT(map())); -                        connect(obj, SIGNAL(sliderPressed(int)), SLOT(guiSliderPressed(int))); -                        connect(obj, SIGNAL(sliderReleased(int)), SLOT(guiSliderReleased(int))); -                        connect(obj, SIGNAL(sliderRightClicked(const QPoint &, int)), SLOT(guiSliderRightClicked(const QPoint &, int))); +                        connect((Slider*)obj, SIGNAL(sliderMoved(double,int)), mapper, SLOT(map())); +                        connect((Slider*)obj, SIGNAL(sliderPressed(int)), SLOT(guiSliderPressed(int))); +                        connect((Slider*)obj, SIGNAL(sliderReleased(int)), SLOT(guiSliderReleased(int))); +                        connect((Slider*)obj, SIGNAL(sliderRightClicked(const QPoint &, int)), SLOT(guiSliderRightClicked(const QPoint &, int)));                          }                    else if (strcmp(obj->metaObject()->className(), "MusEGui::DoubleLabel") == 0) {                          gw[nobj].type = GuiWidgets::DOUBLE_LABEL; @@ -3180,17 +3274,23 @@ PluginGui::PluginGui(MusECore::PluginIBase* p)                              break;                              }                            } -                        connect(obj, SIGNAL(valueChanged(double,int)), mapper, SLOT(map())); +                        connect((DoubleLabel*)obj, SIGNAL(valueChanged(double,int)), mapper, SLOT(map()));                          }                    else if (strcmp(obj->metaObject()->className(), "QCheckBox") == 0) {                          gw[nobj].type = GuiWidgets::QCHECKBOX; -                        connect(obj, SIGNAL(toggled(bool)), mapper, SLOT(map())); -                        connect(obj, SIGNAL(pressed()), mapperPressed, SLOT(map())); -                        connect(obj, SIGNAL(released()), mapperReleased, SLOT(map())); +                        gw[nobj].widget->setContextMenuPolicy(Qt::CustomContextMenu); +                        connect((QCheckBox*)obj, SIGNAL(toggled(bool)), mapper, SLOT(map())); +                        connect((QCheckBox*)obj, SIGNAL(pressed()), mapperPressed, SLOT(map())); +                        connect((QCheckBox*)obj, SIGNAL(released()), mapperReleased, SLOT(map())); +                        connect((QCheckBox*)obj, SIGNAL(customContextMenuRequested(const QPoint &)),  +                                mapperContextMenuReq, SLOT(map()));                          }                    else if (strcmp(obj->metaObject()->className(), "QComboBox") == 0) {                          gw[nobj].type = GuiWidgets::QCOMBOBOX; -                        connect(obj, SIGNAL(activated(int)), mapper, SLOT(map())); +                        gw[nobj].widget->setContextMenuPolicy(Qt::CustomContextMenu); +                        connect((QComboBox*)obj, SIGNAL(activated(int)), mapper, SLOT(map())); +                        connect((QComboBox*)obj, SIGNAL(customContextMenuRequested(const QPoint &)),  +                                mapperContextMenuReq, SLOT(map()));                          }                    else {                          printf("unknown widget class %s\n", obj->metaObject()->className()); @@ -3201,7 +3301,6 @@ PluginGui::PluginGui(MusECore::PluginIBase* p)                updateValues(); // otherwise the GUI won't have valid data              }        else { -            // p3.4.43              view = new QScrollArea;              view->setWidgetResizable(true);              setCentralWidget(view); @@ -3212,13 +3311,13 @@ PluginGui::PluginGui(MusECore::PluginIBase* p)              mw->setSizePolicy(QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding)); -            unsigned long n  = plugin->parameters();   // p4.0.21 +            unsigned long n  = plugin->parameters();                 params = new GuiParam[n];              QFontMetrics fm = fontMetrics();              int h           = fm.height() + 4; -            for (unsigned long i = 0; i < n; ++i) {       // p4.0.21 +            for (unsigned long i = 0; i < n; ++i) {                           QLabel* label = 0;                    LADSPA_PortRangeHint range = plugin->range(i);                    double lower = 0.0;     // default values @@ -3281,7 +3380,7 @@ PluginGui::PluginGui(MusECore::PluginIBase* p)                          grid->addWidget(params[i].actuator, i, 0, 1, 3);                          }                    if (params[i].type == GuiParam::GUI_SLIDER) { -                        connect(params[i].actuator, SIGNAL(sliderMoved(double,int)), SLOT(sliderChanged(double,int))); +                        connect(params[i].actuator, SIGNAL(sliderMoved(double,int,bool)), SLOT(sliderChanged(double,int,bool)));                          connect(params[i].label,    SIGNAL(valueChanged(double,int)), SLOT(labelChanged(double,int)));                          connect(params[i].actuator, SIGNAL(sliderPressed(int)), SLOT(ctrlPressed(int)));                          connect(params[i].actuator, SIGNAL(sliderReleased(int)), SLOT(ctrlReleased(int))); @@ -3429,21 +3528,17 @@ void PluginGui::ctrlPressed(int param)          if(track)          {            track->setPluginCtrlVal(id, val); -          MusEGlobal::song->controllerChange(track); -                      track->startAutoRecord(id, val);          }        }        else if(params[param].type == GuiParam::GUI_SWITCH)        { -        float val = (float)((CheckBox*)params[param].actuator)->isChecked();      // p4.0.21 +        float val = (float)((CheckBox*)params[param].actuator)->isChecked();                plugin->setParam(param, val);          if(track)          {            track->setPluginCtrlVal(id, val); -          MusEGlobal::song->controllerChange(track); -                      track->startAutoRecord(id, val);          }        } @@ -3498,13 +3593,17 @@ void PluginGui::ctrlRightClicked(const QPoint &p, int param)  //   sliderChanged  //--------------------------------------------------------- -void PluginGui::sliderChanged(double val, int param) +void PluginGui::sliderChanged(double val, int param, bool shift_pressed)  {        AutomationType at = AUTO_OFF;        MusECore::AudioTrack* track = plugin->track();        if(track)          at = track->automationType(); +      if ( (at == AUTO_WRITE) || +           (at == AUTO_TOUCH && MusEGlobal::audio->isPlaying()) ) +        plugin->enableController(param, false); +              if (LADSPA_IS_HINT_LOGARITHMIC(params[param].hint))              val = pow(10.0, val/20.0);        else if (LADSPA_IS_HINT_INTEGER(params[param].hint)) @@ -3523,9 +3622,7 @@ void PluginGui::sliderChanged(double val, int param)        if(track)        {          track->setPluginCtrlVal(id, val); -        MusEGlobal::song->controllerChange(track); -         -        track->recordAutomation(id, val); +        if (!shift_pressed) track->recordAutomation(id, val); //with shift, we get straight lines :)        }    } @@ -3540,6 +3637,10 @@ void PluginGui::labelChanged(double val, int param)        if(track)          at = track->automationType(); +      if ( (at == AUTO_WRITE) || +           (at == AUTO_TOUCH && MusEGlobal::audio->isPlaying()) ) +        plugin->enableController(param, false); +              double dval = val;        if (LADSPA_IS_HINT_LOGARITHMIC(params[param].hint))              dval = MusECore::fast_log10(val) * 20.0; @@ -3559,8 +3660,6 @@ void PluginGui::labelChanged(double val, int param)        if(track)        {          track->setPluginCtrlVal(id, val); -        MusEGlobal::song->controllerChange(track); -                  track->startAutoRecord(id, val);        }    } @@ -3689,7 +3788,7 @@ void PluginGui::setOn(bool val)  void PluginGui::updateValues()        {        if (params) { -            for (unsigned long i = 0; i < plugin->parameters(); ++i) {       // p4.0.21 +            for (unsigned long i = 0; i < plugin->parameters(); ++i) {                           GuiParam* gp = ¶ms[i];                    if (gp->type == GuiParam::GUI_SLIDER) {                          double lv = plugin->param(i); @@ -3710,10 +3809,10 @@ void PluginGui::updateValues()                    }              }        else if (gw) { -            for (unsigned long i = 0; i < nobj; ++i) {      // p4.0.21 +            for (unsigned long i = 0; i < nobj; ++i) {                          QWidget* widget = gw[i].widget;                    int type = gw[i].type; -                  unsigned long param = gw[i].param;        // p4.0.21 +                  unsigned long param = gw[i].param;                            float val = plugin->param(param);                    switch(type) {                          case GuiWidgets::SLIDER: @@ -3766,12 +3865,16 @@ void PluginGui::updateControls()        if (params) { -            for (unsigned long i = 0; i < plugin->parameters(); ++i) {      // p4.0.21 +            for (unsigned long i = 0; i < plugin->parameters(); ++i) {                          GuiParam* gp = ¶ms[i];                    if (gp->type == GuiParam::GUI_SLIDER) { -                        if( plugin->controllerEnabled(i) && plugin->controllerEnabled2(i) )                            { -                            double lv = plugin->track()->pluginCtrlVal(MusECore::genACnum(plugin->id(), i)); +                            double lv = plugin->track()->controller()->value(MusECore::genACnum(plugin->id(), i),  +                                                                             MusEGlobal::audio->curFramePos(),  +                                                                             !MusEGlobal::automation ||  +                                                                             plugin->track()->automationType() == AUTO_OFF ||  +                                                                             !plugin->controllerEnabled(i) ||  +                                                                             !plugin->controllerEnabled2(i));                                                          double sv = lv;                              if (LADSPA_IS_HINT_LOGARITHMIC(params[i].hint))                                    sv = MusECore::fast_log10(lv) * 20.0; @@ -3791,12 +3894,15 @@ void PluginGui::updateControls()                                gp->label->blockSignals(false);                              }                             } -                                                    }                    else if (gp->type == GuiParam::GUI_SWITCH) { -                        if( plugin->controllerEnabled(i) && plugin->controllerEnabled2(i) )                            { -                            bool v = (int)plugin->track()->pluginCtrlVal(MusECore::genACnum(plugin->id(), i)); +                            bool v = (int)plugin->track()->controller()->value(MusECore::genACnum(plugin->id(), i),  +                                                                             MusEGlobal::audio->curFramePos(),  +                                                                             !MusEGlobal::automation ||  +                                                                             plugin->track()->automationType() == AUTO_OFF ||  +                                                                             !plugin->controllerEnabled(i) ||  +                                                                             !plugin->controllerEnabled2(i));                                                          if(((CheckBox*)(gp->actuator))->isChecked() != v)                              {                                ((CheckBox*)(gp->actuator))->blockSignals(true); @@ -3808,15 +3914,19 @@ void PluginGui::updateControls()                    }              }        else if (gw) { -            for (unsigned long i = 0; i < nobj; ++i) {    // p4.0.21 +            for (unsigned long i = 0; i < nobj; ++i) {                        QWidget* widget = gw[i].widget;                    int type = gw[i].type; -                  unsigned long param = gw[i].param;      // p4.0.21 +                  unsigned long param = gw[i].param;                          switch(type) {                          case GuiWidgets::SLIDER: -                              if( plugin->controllerEnabled(param) && plugin->controllerEnabled2(param) )                                { -                                double v = plugin->track()->pluginCtrlVal(MusECore::genACnum(plugin->id(), param)); +                                double v = plugin->track()->controller()->value(MusECore::genACnum(plugin->id(), param),  +                                                                                MusEGlobal::audio->curFramePos(),  +                                                                                !MusEGlobal::automation ||  +                                                                                plugin->track()->automationType() == AUTO_OFF ||  +                                                                                !plugin->controllerEnabled(param) ||  +                                                                                !plugin->controllerEnabled2(param));                                                              if(((Slider*)widget)->value() != v)                                  {                                    ((Slider*)widget)->blockSignals(true); @@ -3826,9 +3936,13 @@ void PluginGui::updateControls()                                }                                break;                          case GuiWidgets::DOUBLE_LABEL: -                              if( plugin->controllerEnabled(param) && plugin->controllerEnabled2(param) )                                { -                                double v = plugin->track()->pluginCtrlVal(MusECore::genACnum(plugin->id(), param)); +                                double v = plugin->track()->controller()->value(MusECore::genACnum(plugin->id(), param),  +                                                                                MusEGlobal::audio->curFramePos(),  +                                                                                !MusEGlobal::automation ||  +                                                                                plugin->track()->automationType() == AUTO_OFF ||  +                                                                                !plugin->controllerEnabled(param) ||  +                                                                                !plugin->controllerEnabled2(param));                                                              if(((DoubleLabel*)widget)->value() != v)                                  {                                    ((DoubleLabel*)widget)->blockSignals(true); @@ -3838,9 +3952,13 @@ void PluginGui::updateControls()                                }                                break;                          case GuiWidgets::QCHECKBOX: -                              if( plugin->controllerEnabled(param) && plugin->controllerEnabled2(param) )                                {  -                                bool b = (bool) plugin->track()->pluginCtrlVal(MusECore::genACnum(plugin->id(), param)); +                                bool b = (bool) plugin->track()->controller()->value(MusECore::genACnum(plugin->id(), param),  +                                                                                MusEGlobal::audio->curFramePos(),  +                                                                                !MusEGlobal::automation ||  +                                                                                plugin->track()->automationType() == AUTO_OFF ||  +                                                                                !plugin->controllerEnabled(param) ||  +                                                                                !plugin->controllerEnabled2(param));                                                              if(((QCheckBox*)widget)->isChecked() != b)                                  {                                    ((QCheckBox*)widget)->blockSignals(true); @@ -3850,9 +3968,13 @@ void PluginGui::updateControls()                                }                                break;                          case GuiWidgets::QCOMBOBOX: -                              if( plugin->controllerEnabled(param) && plugin->controllerEnabled2(param) )                                {  -                                int n = (int) plugin->track()->pluginCtrlVal(MusECore::genACnum(plugin->id(), param)); +                                int n = (int) plugin->track()->controller()->value(MusECore::genACnum(plugin->id(), param),  +                                                                                MusEGlobal::audio->curFramePos(),  +                                                                                !MusEGlobal::automation ||  +                                                                                plugin->track()->automationType() == AUTO_OFF ||  +                                                                                !plugin->controllerEnabled(param) ||  +                                                                                !plugin->controllerEnabled2(param));                                                              if(((QComboBox*)widget)->currentIndex() != n)                                  {                                    ((QComboBox*)widget)->blockSignals(true); @@ -3873,7 +3995,7 @@ void PluginGui::updateControls()  void PluginGui::guiParamChanged(int idx)  {        QWidget* w = gw[idx].widget; -      unsigned long param  = gw[idx].param;    // p4.0.21 +      unsigned long param  = gw[idx].param;            int type   = gw[idx].type;        AutomationType at = AUTO_OFF; @@ -3881,6 +4003,10 @@ void PluginGui::guiParamChanged(int idx)        if(track)          at = track->automationType(); +      if ( (at == AUTO_WRITE) || +           (at == AUTO_TOUCH && MusEGlobal::audio->isPlaying()) ) +        plugin->enableController(param, false); +              double val = 0.0;        switch(type) {              case GuiWidgets::SLIDER: @@ -3897,7 +4023,7 @@ void PluginGui::guiParamChanged(int idx)                    break;              } -      for (unsigned long i = 0; i < nobj; ++i) {      // p4.0.21 +      for (unsigned long i = 0; i < nobj; ++i) {                    QWidget* widget = gw[i].widget;              if (widget == w || param != gw[i].param)                    continue; @@ -3922,10 +4048,7 @@ void PluginGui::guiParamChanged(int idx)        if(track && id != -1)        {            id = MusECore::genACnum(id, param); -                      track->setPluginCtrlVal(id, val); -          MusEGlobal::song->controllerChange(track); -                      switch(type)             {               case GuiWidgets::DOUBLE_LABEL: @@ -3946,7 +4069,7 @@ void PluginGui::guiParamChanged(int idx)  void PluginGui::guiParamPressed(int idx)        { -      unsigned long param  = gw[idx].param;     // p4.0.21 +      unsigned long param  = gw[idx].param;             AutomationType at = AUTO_OFF;        MusECore::AudioTrack* track = plugin->track(); @@ -3965,8 +4088,8 @@ void PluginGui::guiParamPressed(int idx)        // NOTE: For this to be of any use, the freeverb gui 2142.ui        //  would have to be used, and changed to use CheckBox and ComboBox        //  instead of QCheckBox and QComboBox, since both of those would -      //  need customization (Ex. QCheckBox doesn't check on click). -      /* DELETETHIS 10 plus above +      //  need customization (Ex. QCheckBox doesn't check on click). RECHECK: Qt4 it does? +      /*         switch(type) {              case GuiWidgets::QCHECKBOX:                      double val = (double)((CheckBox*)w)->isChecked(); @@ -3986,7 +4109,7 @@ void PluginGui::guiParamPressed(int idx)  void PluginGui::guiParamReleased(int idx)        { -      unsigned long param  = gw[idx].param;    // p4.0.21 +      unsigned long param  = gw[idx].param;            int type   = gw[idx].type;        AutomationType at = AUTO_OFF; @@ -4011,8 +4134,8 @@ void PluginGui::guiParamReleased(int idx)        // NOTE: For this to be of any use, the freeverb gui 2142.ui        //  would have to be used, and changed to use CheckBox and ComboBox        //  instead of QCheckBox and QComboBox, since both of those would -      //  need customization (Ex. QCheckBox doesn't check on click). -      /* DELETETHIS 10 plus above +      //  need customization (Ex. QCheckBox doesn't check on click).  // RECHECK Qt4 it does? +      /*         switch(type) {              case GuiWidgets::QCHECKBOX:                      double val = (double)((CheckBox*)w)->isChecked(); @@ -4032,7 +4155,7 @@ void PluginGui::guiParamReleased(int idx)  void PluginGui::guiSliderPressed(int idx)        { -      unsigned long param  = gw[idx].param;    // p4.0.21 +      unsigned long param  = gw[idx].param;            QWidget *w = gw[idx].widget;        AutomationType at = AUTO_OFF; @@ -4054,12 +4177,10 @@ void PluginGui::guiSliderPressed(int idx)        plugin->setParam(param, val);        track->setPluginCtrlVal(id, val); -      MusEGlobal::song->controllerChange(track); -              track->startAutoRecord(id, val);        // Needed so that paging a slider updates a label or other buddy control. -      for (unsigned long i = 0; i < nobj; ++i) {           // p4.0.21 +      for (unsigned long i = 0; i < nobj; ++i) {                         QWidget* widget = gw[i].widget;              if (widget == w || param != gw[i].param)                    continue; @@ -4127,6 +4248,15 @@ void PluginGui::guiSliderRightClicked(const QPoint &p, int idx)  }  //--------------------------------------------------------- +//   guiContextMenuReq +//--------------------------------------------------------- + +void PluginGui::guiContextMenuReq(int idx) +{ +  guiSliderRightClicked(QCursor().pos(), idx); +} + +//---------------------------------------------------------  //   PluginLoader  //---------------------------------------------------------  QWidget* PluginLoader::createWidget(const QString & className, QWidget * parent, const QString & name) diff --git a/muse2/muse/plugin.h b/muse2/muse/plugin.h index 9c671097..06e99564 100644 --- a/muse2/muse/plugin.h +++ b/muse2/muse/plugin.h @@ -246,12 +246,16 @@ class PluginIBase        virtual void enableController(unsigned long i, bool v = true) = 0;           virtual bool controllerEnabled(unsigned long i) const = 0;           +      virtual void enable2Controller(unsigned long i, bool v = true) = 0;         virtual bool controllerEnabled2(unsigned long i) const = 0;           +      virtual void enableAllControllers(bool v = true) = 0; +      virtual void enable2AllControllers(bool v = true) = 0;        virtual void updateControllers() = 0;        virtual void writeConfiguration(int level, Xml& xml) = 0;        virtual bool readConfiguration(Xml& xml, bool readPreset=false) = 0; +      virtual bool addScheduledControlEvent(unsigned long i, float val, unsigned frame);    // returns true if event cannot be delivered        virtual unsigned long parameters() const = 0;                          virtual unsigned long parametersOut() const = 0;        virtual void setParam(unsigned long i, float val) = 0; @@ -412,6 +416,10 @@ class Pipeline : public std::vector<PluginI*> {        void move(int idx, bool up);        bool empty(int idx) const;        void setChannels(int); +      bool addScheduledControlEvent(int track_ctrl_id, float val, unsigned frame); // returns true if event cannot be delivered +      void enableController(int track_ctrl_id, bool en);  +      void enable2Controller(int track_ctrl_id, bool en);  +      void controllersEnabled(int track_ctrl_id, bool* en1, bool* en2);         };  typedef Pipeline::iterator iPluginI; @@ -497,7 +505,7 @@ class PluginGui : public QMainWindow {        void load();        void save();        void bypassToggled(bool); -      void sliderChanged(double, int); +      void sliderChanged(double, int, bool);        void labelChanged(double, int);        void guiParamChanged(int);        void ctrlPressed(int); @@ -508,6 +516,7 @@ class PluginGui : public QMainWindow {        void guiSliderReleased(int);        void ctrlRightClicked(const QPoint &, int);        void guiSliderRightClicked(const QPoint &, int); +      void guiContextMenuReq(int idx);     protected slots:        void heartBeat(); diff --git a/muse2/muse/seqmsg.cpp b/muse2/muse/seqmsg.cpp index d5257f80..f60a2d51 100644 --- a/muse2/muse/seqmsg.cpp +++ b/muse2/muse/seqmsg.cpp @@ -46,6 +46,7 @@ namespace MusECore {  //   sendMsg  //--------------------------------------------------------- +// this function blocks until the request has been processed  void Audio::sendMsg(AudioMsg* m)        {        static int sno = 0; @@ -522,7 +523,6 @@ void Audio::msgSwapControllerIDX(AudioTrack* node, int idx1, int idx2)        msg.a      = idx1;        msg.b      = idx2;        sendMsg(&msg); -      MusEGlobal::song->controllerChange(node);  }  //--------------------------------------------------------- @@ -537,7 +537,6 @@ void Audio::msgClearControllerEvents(AudioTrack* node, int acid)        msg.snode  = node;        msg.ival   = acid;        sendMsg(&msg); -      MusEGlobal::song->controllerChange(node);  }  //--------------------------------------------------------- @@ -581,7 +580,6 @@ void Audio::msgEraseACEvent(AudioTrack* node, int acid, int frame)        msg.ival   = acid;        msg.a      = frame;         sendMsg(&msg); -      MusEGlobal::song->controllerChange(node);  }  //--------------------------------------------------------- @@ -598,7 +596,6 @@ void Audio::msgEraseRangeACEvents(AudioTrack* node, int acid, int frame1, int fr        msg.a      = frame1;         msg.b      = frame2;         sendMsg(&msg); -      MusEGlobal::song->controllerChange(node);  }  //--------------------------------------------------------- @@ -615,7 +612,6 @@ void Audio::msgAddACEvent(AudioTrack* node, int acid, int frame, double val)        msg.a      = frame;         msg.dval   = val;        sendMsg(&msg); -      MusEGlobal::song->controllerChange(node);  }  //--------------------------------------------------------- @@ -633,7 +629,6 @@ void Audio::msgChangeACEvent(AudioTrack* node, int acid, int frame, int newFrame        msg.b      = newFrame;         msg.dval   = val;        sendMsg(&msg); -      MusEGlobal::song->controllerChange(node);  }  //--------------------------------------------------------- @@ -1297,6 +1292,18 @@ void Audio::msgSetSendMetronome(AudioTrack* track, bool b)  }  //--------------------------------------------------------- +//   msgStartMidiLearn +//    Start learning midi  +//--------------------------------------------------------- + +void Audio::msgStartMidiLearn() +{ +      AudioMsg msg; +      msg.id    = AUDIO_START_MIDI_LEARN; +      sendMessage(&msg, false); +} + +//---------------------------------------------------------  //   msgBounce  //    start bounce operation  //--------------------------------------------------------- diff --git a/muse2/muse/song.cpp b/muse2/muse/song.cpp index 0c7a0c73..020d620c 100644 --- a/muse2/muse/song.cpp +++ b/muse2/muse/song.cpp @@ -57,11 +57,13 @@  #include "sync.h"  #include "midictrl.h"  #include "menutitleitem.h" +#include "midi_audio_control.h"  #include "tracks_duplicate.h"  #include "midi.h"  #include "al/sig.h"  #include "keyevent.h"  #include <sys/wait.h> +#include "tempo.h"  namespace MusEGlobal {  MusECore::Song* song = 0; @@ -762,18 +764,11 @@ void Song::changeAllPortDrumCtrlEvents(bool add, bool drumonly)  void Song::addACEvent(AudioTrack* t, int acid, int frame, double val)  {    MusEGlobal::audio->msgAddACEvent(t, acid, frame, val); -  emit controllerChanged(t);   }  void Song::changeACEvent(AudioTrack* t, int acid, int frame, int newFrame, double val)  {    MusEGlobal::audio->msgChangeACEvent(t, acid, frame, newFrame, val); -  emit controllerChanged(t);  -} - -void Song::controllerChange(Track* t) -{ -  emit controllerChanged(t);   }  //--------------------------------------------------------- @@ -883,7 +878,6 @@ void Song::cmdAddRecordedEvents(MidiTrack* mt, EventList* events, unsigned start                    if (endTick < tick)                          endTick = tick;                    } -            // Added by Tim. p3.3.8              // Round the end up (again) using the Arranger part snap raster value.               endTick   = AL::sigmap.raster2(endTick, arrangerRaster()); @@ -1626,6 +1620,26 @@ void Song::beat()        if (MusEGlobal::audio->isPlaying())          setPos(0, MusEGlobal::audio->tickPos(), true, false, true); +      // Process external tempo changes: +      while(!_tempoFifo.isEmpty()) +        MusEGlobal::tempo_rec_list.addTempo(_tempoFifo.get());  +       +      // Update anything related to audio controller graphs etc. +      for(ciTrack it = _tracks.begin(); it != _tracks.end(); ++ it) +      { +        if((*it)->isMidiTrack()) +          continue; +        AudioTrack* at = static_cast<AudioTrack*>(*it);  +        CtrlListList* cll = at->controller(); +        for(ciCtrlList icl = cll->begin(); icl != cll->end(); ++icl) +        { +          CtrlList* cl = icl->second; +          if(cl->isVisible() && !cl->dontShow() && cl->guiUpdatePending())   +            emit controllerChanged(at, cl->id()); +          cl->setGuiUpdatePending(false); +        } +      } +              // Update synth native guis at the heartbeat rate.        for(ciSynthI is = _synthIs.begin(); is != _synthIs.end(); ++is)          (*is)->guiHeartBeat(); @@ -2078,6 +2092,7 @@ void Song::clear(bool signal, bool clear_all)        while (loop);        MusEGlobal::tempomap.clear(); +      MusEGlobal::tempo_rec_list.clear();        AL::sigmap.clear();        MusEGlobal::keymap.clear(); @@ -2419,13 +2434,14 @@ void Song::recordEvent(MidiTrack* mt, Event& event)  int Song::execAutomationCtlPopup(AudioTrack* track, const QPoint& menupos, int acid)  { -  enum { PREV_EVENT, NEXT_EVENT, ADD_EVENT, CLEAR_EVENT, CLEAR_RANGE, CLEAR_ALL_EVENTS }; +  enum { PREV_EVENT=0, NEXT_EVENT, ADD_EVENT, CLEAR_EVENT, CLEAR_RANGE, CLEAR_ALL_EVENTS, MIDI_ASSIGN, MIDI_CLEAR };    QMenu* menu = new QMenu;    int count = 0;    bool isEvent = false, canSeekPrev = false, canSeekNext = false, canEraseRange = false;    bool canAdd = false;    double ctlval = 0.0; +  int frame = 0;    if(track)    {      ciCtrlList icl = track->controller()->find(acid); @@ -2434,11 +2450,17 @@ int Song::execAutomationCtlPopup(AudioTrack* track, const QPoint& menupos, int a        CtrlList *cl = icl->second;        canAdd = true; -      //int frame = pos[0].frame(); DELETETHIS -      int frame = MusEGlobal::audio->pos().frame();       // Try this. p4.0.33 DELETETHIS +      frame = MusEGlobal::audio->pos().frame();        +       +      bool en1, en2; +      track->controllersEnabled(acid, &en1, &en2); +       +      AutomationType at = track->automationType(); +      if(!MusEGlobal::automation || at == AUTO_OFF || !en1 || !en2)  +        ctlval = cl->curVal();   +      else   +        ctlval = cl->value(frame); -      ctlval = cl->curVal(); -        count = cl->size();        if(count)        { @@ -2491,6 +2513,40 @@ int Song::execAutomationCtlPopup(AudioTrack* track, const QPoint& menupos, int a    clearAction->setData(CLEAR_ALL_EVENTS);    clearAction->setEnabled((bool)count); + +  menu->addSeparator(); +  menu->addAction(new MusEGui::MenuTitleItem(tr("Midi control"), menu)); +   +  QAction *assign_act = menu->addAction(tr("Assign")); +  assign_act->setCheckable(false); +  assign_act->setData(MIDI_ASSIGN);  +   +  MidiAudioCtrlMap* macm = track->controller()->midiControls(); +  AudioMidiCtrlStructMap amcs; +  macm->find_audio_ctrl_structs(acid, &amcs); +   +  if(!amcs.empty()) +  { +    QAction *cact = menu->addAction(tr("Clear")); +    cact->setData(MIDI_CLEAR);  +    menu->addSeparator(); +  } +   +  for(iAudioMidiCtrlStructMap iamcs = amcs.begin(); iamcs != amcs.end(); ++iamcs) +  { +    int port, chan, mctrl; +    macm->hash_values((*iamcs)->first, &port, &chan, &mctrl); +    //QString s = QString("Port:%1 Chan:%2 Ctl:%3-%4").arg(port + 1) +    QString s = QString("Port:%1 Chan:%2 Ctl:%3").arg(port + 1) +                                                  .arg(chan + 1) +                                                  //.arg((mctrl >> 8) & 0xff) +                                                  //.arg(mctrl & 0xff); +                                                  .arg(midiCtrlName(mctrl, true)); +    QAction *mact = menu->addAction(s); +    mact->setEnabled(false); +    mact->setData(-1); // Not used +  } +      QAction* act = menu->exec(menupos);    if (!act || !track)    { @@ -2504,10 +2560,10 @@ int Song::execAutomationCtlPopup(AudioTrack* track, const QPoint& menupos, int a    switch(sel)    {      case ADD_EVENT: -          MusEGlobal::audio->msgAddACEvent(track, acid, pos[0].frame(), ctlval); +          MusEGlobal::audio->msgAddACEvent(track, acid, frame, ctlval);      break;      case CLEAR_EVENT: -          MusEGlobal::audio->msgEraseACEvent(track, acid, pos[0].frame()); +          MusEGlobal::audio->msgEraseACEvent(track, acid, frame);      break;      case CLEAR_RANGE: @@ -2529,6 +2585,45 @@ int Song::execAutomationCtlPopup(AudioTrack* track, const QPoint& menupos, int a            MusEGlobal::audio->msgSeekNextACEvent(track, acid);      break; +    case MIDI_ASSIGN: +          { +            int port = -1, chan = 0, ctrl = 0; +            for(MusECore::iAudioMidiCtrlStructMap iamcs = amcs.begin(); iamcs != amcs.end(); ++iamcs) +            { +              macm->hash_values((*iamcs)->first, &port, &chan, &ctrl); +              break; // Only a single item for now, thanks! +            } +             +            MusEGui::MidiAudioControl* pup = new MusEGui::MidiAudioControl(port, chan, ctrl); +             +            if(pup->exec() == QDialog::Accepted) +            { +              MusEGlobal::audio->msgIdle(true);  // Gain access to structures, and sync with audio +              // Erase all for now. +              for(MusECore::iAudioMidiCtrlStructMap iamcs = amcs.begin(); iamcs != amcs.end(); ++iamcs) +                macm->erase(*iamcs); +               +              port = pup->port(); chan = pup->chan(); ctrl = pup->ctrl(); +              if(port >= 0 && chan >=0 && ctrl >= 0) +                // Add will replace if found. +                macm->add_ctrl_struct(port, chan, ctrl, MusECore::MidiAudioCtrlStruct(acid)); +               +              MusEGlobal::audio->msgIdle(false); +            } +             +            delete pup; +          } +          break; +     +    case MIDI_CLEAR: +          if(!amcs.empty()) +            MusEGlobal::audio->msgIdle(true);  // Gain access to structures, and sync with audio +          for(MusECore::iAudioMidiCtrlStructMap iamcs = amcs.begin(); iamcs != amcs.end(); ++iamcs) +            macm->erase(*iamcs); +          if(!amcs.empty()) +            MusEGlobal::audio->msgIdle(false); +    break; +          default:            return -1;      break;       @@ -2755,8 +2850,62 @@ void Song::processAutomationEvents()        // Process (and clear) rec events.        ((AudioTrack*)(*i))->processAutomationEvents();    } + +  MusEGlobal::audio->msgIdle(false);  +} + +//--------------------------------------------------------- +//   processMasterRec +//--------------------------------------------------------- + +void Song::processMasterRec() +{ +  bool do_tempo = false; +   +  // Wait a few seconds for the tempo fifo to be empty. +  int tout = 30; +  while(!_tempoFifo.isEmpty()) +  { +    usleep(100000); +    --tout; +    if(tout == 0) +      break; +  } +   +  int tempo_rec_list_sz = MusEGlobal::tempo_rec_list.size(); +  if(tempo_rec_list_sz != 0)  +  { +    if(QMessageBox::question(MusEGlobal::muse,  +                          tr("MusE: Tempo list"),  +                          tr("External tempo changes were recorded.\nTransfer them to master tempo list?"), +                          QMessageBox::Ok | QMessageBox::Cancel, QMessageBox::Cancel) == QMessageBox::Ok) +       do_tempo = true; +  } +   +  MusEGlobal::audio->msgIdle(true); // gain access to all data structures + +  if(do_tempo) +  { +    // Erase from master tempo the (approximate) recording start/end tick range according to the recorded tempo map, +    //MusEGlobal::tempomap.eraseRange(MusEGlobal::tempo_rec_list.frame2tick(MusEGlobal::audio->getStartRecordPos().frame()),  +    //                                MusEGlobal::tempo_rec_list.frame2tick(MusEGlobal::audio->getEndRecordPos().frame())); +    // This is more accurate but lacks resolution: +    MusEGlobal::tempomap.eraseRange(MusEGlobal::audio->getStartExternalRecTick(), MusEGlobal::audio->getEndExternalRecTick()); + +    // Add the recorded tempos to the master tempo list: +    for(int i = 0; i < tempo_rec_list_sz; ++i) +      MusEGlobal::tempomap.addTempo(MusEGlobal::tempo_rec_list[i].tick,  +                                    MusEGlobal::tempo_rec_list[i].tempo,  +                                    false);  // False: Defer normalize +    MusEGlobal::tempomap.normalize(); +  } +   +  MusEGlobal::tempo_rec_list.clear();    MusEGlobal::audio->msgIdle(false);  + +  if(do_tempo) +    update(SC_TEMPO);  }  //--------------------------------------------------------- diff --git a/muse2/muse/song.h b/muse2/muse/song.h index 82b8cf18..6570ad8d 100644 --- a/muse2/muse/song.h +++ b/muse2/muse/song.h @@ -126,6 +126,8 @@ class Song : public QObject {        int noteFifoWindex;        int noteFifoRindex; +      TempoFifo _tempoFifo; // External tempo changes, processed in heartbeat. +              int updateFlags;        TrackList _tracks;      // tracklist as seen by arranger @@ -263,7 +265,7 @@ class Song : public QObject {        //   event manipulations        //----------------------------------------- -      void cmdAddRecordedWave(WaveTrack* track, Pos, Pos); +      void cmdAddRecordedWave(WaveTrack* track, Pos, Pos);          void cmdAddRecordedEvents(MidiTrack*, EventList*, unsigned);        bool addEvent(Event&, Part*);        void changeEvent(Event&, Event&, Part*); @@ -274,8 +276,8 @@ class Song : public QObject {        void addACEvent(AudioTrack* t, int acid, int frame, double val);        void changeACEvent(AudioTrack* t, int acid, int frame, int newFrame, double val); -      void controllerChange(Track* t); - +      void addExternalTempo(const TempoRecEvent& e) { _tempoFifo.put(e); } +              //-----------------------------------------        //   part manipulations        //----------------------------------------- @@ -332,6 +334,7 @@ class Song : public QObject {        void msgInsertTrack(Track* track, int idx, bool u = true);        void clearRecAutomation(bool clearList);        void processAutomationEvents(); +      void processMasterRec();        int execAutomationCtlPopup(AudioTrack*, const QPoint&, int);        int execMidiAutomationCtlPopup(MidiTrack*, MidiPart*, const QPoint&, int);        void connectJackRoutes(AudioTrack* track, bool disconnect); @@ -428,7 +431,7 @@ class Song : public QObject {        void markerChanged(int);        void midiPortsChanged();        void midiNote(int pitch, int velo);   -      void controllerChanged(MusECore::Track* t); // maybe DELETETHIS: this only triggers a redraw in pcanvas.cpp; what is this for? +      void controllerChanged(MusECore::Track*, int);         void newPartsCreated(const std::map< MusECore::Part*, std::set<MusECore::Part*> >&);        }; diff --git a/muse2/muse/sync.cpp b/muse2/muse/sync.cpp index 56560a5e..bf9d2613 100644 --- a/muse2/muse/sync.cpp +++ b/muse2/muse/sync.cpp @@ -21,6 +21,7 @@  //  //========================================================= +#include <stdlib.h>  #include <cmath>  #include "sync.h"  #include "song.h" @@ -59,12 +60,13 @@ static unsigned int curExtMidiSyncTick = 0;  unsigned int volatile lastExtMidiSyncTick = 0;  double volatile curExtMidiSyncTime = 0.0;  double volatile lastExtMidiSyncTime = 0.0; +MusECore::MidiSyncInfo::SyncRecFilterPresetType syncRecFilterPreset = MusECore::MidiSyncInfo::SMALL; +double syncRecTempoValQuant = 1.0;  // Not used yet. DELETETHIS?  // static bool mcStart = false;  // static int mcStartTick; -// p3.3.25  // From the "Introduction to the Volatile Keyword" at Embedded dot com  /* A variable should be declared volatile whenever its value could change unexpectedly.    ... <such as> global variables within a multi-threaded application     @@ -820,18 +822,25 @@ void MidiSeq::alignAllTicks(int frameOverride)          recTick2 = 0;        if (MusEGlobal::debugSync)          printf("alignAllTicks curFrame=%d recTick=%d tempo=%.3f frameOverride=%d\n",curFrame,recTick,(float)((1000000.0 * 60.0)/tempo), frameOverride); - +       +      lastTempo = 0; +      for(int i = 0; i < _clockAveragerPoles; ++i) +      { +        _avgClkDiffCounter[i] = 0; +        _averagerFull[i] = false; +      } +      _lastRealTempo = 0.0;        }  //---------------------------------------------------------  //   realtimeSystemInput  //    real time message received  //--------------------------------------------------------- -void MidiSeq::realtimeSystemInput(int port, int c) +void MidiSeq::realtimeSystemInput(int port, int c, double time)        {        if (MusEGlobal::midiInputTrace) -            printf("realtimeSystemInput port:%d 0x%x\n", port+1, c); +            printf("realtimeSystemInput port:%d 0x%x time:%f\n", port+1, c, time);        MidiPort* mp = &MusEGlobal::midiPorts[port]; @@ -873,6 +882,9 @@ void MidiSeq::realtimeSystemInput(int port, int c)                      if(p != port && MusEGlobal::midiPorts[p].syncInfo().MCOut())                        MusEGlobal::midiPorts[p].sendClock(); +                  MusEGlobal::lastExtMidiSyncTime = MusEGlobal::curExtMidiSyncTime; +                  MusEGlobal::curExtMidiSyncTime = time; +                                      if(MusEGlobal::playPendingFirstClock)                    {                      MusEGlobal::playPendingFirstClock = false; @@ -887,12 +899,158 @@ void MidiSeq::realtimeSystemInput(int port, int c)                    // Can't check audio state, might not be playing yet, we might miss some increments.                    if(playStateExt)                    { -                    MusEGlobal::lastExtMidiSyncTime = MusEGlobal::curExtMidiSyncTime; -                    MusEGlobal::curExtMidiSyncTime = curTime();                      int div = MusEGlobal::config.division/24;                      MusEGlobal::midiExtSyncTicks += div;                      MusEGlobal::lastExtMidiSyncTick = MusEGlobal::curExtMidiSyncTick;                      MusEGlobal::curExtMidiSyncTick += div; +                     +                    if(MusEGlobal::song->record() && MusEGlobal::lastExtMidiSyncTime > 0.0) +                    { +                      double diff = MusEGlobal::curExtMidiSyncTime - MusEGlobal::lastExtMidiSyncTime; +                      if(diff != 0.0) +                      { +                        if(_clockAveragerPoles == 0) +                        { +                          double real_tempo = 60.0/(diff * 24.0); +                          if(_tempoQuantizeAmount > 0.0) +                          { +                            double f_mod = fmod(real_tempo, _tempoQuantizeAmount); +                            if(f_mod < _tempoQuantizeAmount/2.0) +                              real_tempo -= f_mod; +                            else +                              real_tempo += _tempoQuantizeAmount - f_mod; +                          } +                          int new_tempo = ((1000000.0 * 60.0) / (real_tempo)); +                          if(new_tempo != lastTempo) +                          { +                            lastTempo = new_tempo; +                            // Compute tick for this tempo - it is one step back in time.  +                            int add_tick = MusEGlobal::curExtMidiSyncTick - div; +                            if(MusEGlobal::debugSync) +                              printf("adding new tempo tick:%d curExtMidiSyncTick:%d avg_diff:%f real_tempo:%f new_tempo:%d = %f\n", add_tick, MusEGlobal::curExtMidiSyncTick, diff, real_tempo, new_tempo, (double)((1000000.0 * 60.0)/new_tempo)); +                            MusEGlobal::song->addExternalTempo(TempoRecEvent(add_tick, new_tempo)); +                          }   +                        } +                        else +                        { +                          double avg_diff = diff; +                          for(int pole = 0; pole < _clockAveragerPoles; ++pole) +                          { +                            timediff[pole][_avgClkDiffCounter[pole]] = avg_diff; +                            ++_avgClkDiffCounter[pole]; +                            if(_avgClkDiffCounter[pole] >= _clockAveragerStages[pole]) +                            { +                              _avgClkDiffCounter[pole] = 0; +                              _averagerFull[pole] = true; +                            } +                             +                            // Each averager needs to be full before we can pass the data to  +                            //  the next averager or use the data if all averagers are full... +                            if(!_averagerFull[pole])   +                              break; +                            else +                            { +                              avg_diff = 0.0; +                              for(int i = 0; i < _clockAveragerStages[pole]; ++i)  +                                avg_diff += timediff[pole][i]; +                              avg_diff /= _clockAveragerStages[pole]; +                               +                              int fin_idx = _clockAveragerPoles - 1; +                                 +                              // On the first pole? Check for large differences. +                              if(_preDetect && pole == 0) +                              { +                                double real_tempo = 60.0/(avg_diff * 24.0); +                                double real_tempo_diff = abs(real_tempo - _lastRealTempo); +                                 +                                // If the tempo changed a large amount, reset. +                                if(real_tempo_diff >= 10.0)  // TODO: User-adjustable? +                                { +                                  if(_tempoQuantizeAmount > 0.0) +                                  { +                                    double f_mod = fmod(real_tempo, _tempoQuantizeAmount); +                                    if(f_mod < _tempoQuantizeAmount/2.0) +                                      real_tempo -= f_mod; +                                    else +                                      real_tempo += _tempoQuantizeAmount - f_mod; +                                  } +                                  _lastRealTempo = real_tempo; +                                  int new_tempo = ((1000000.0 * 60.0) / (real_tempo)); +                                   +                                  if(new_tempo != lastTempo) +                                  { +                                    lastTempo = new_tempo; +                                    // Compute tick for this tempo - it is way back in time.  +                                    int add_tick = MusEGlobal::curExtMidiSyncTick - _clockAveragerStages[0] * div; +                                    if(add_tick < 0)  +                                    { +                                      printf("FIXME sync: adding restart tempo curExtMidiSyncTick:%d: add_tick:%d < 0 !\n", MusEGlobal::curExtMidiSyncTick, add_tick); +                                      add_tick = 0; +                                    } +                                    if(MusEGlobal::debugSync) +                                      printf("adding restart tempo tick:%d curExtMidiSyncTick:%d tick_idx_sub:%d avg_diff:%f real_tempo:%f real_tempo_diff:%f new_tempo:%d = %f\n", add_tick, MusEGlobal::curExtMidiSyncTick, _clockAveragerStages[0], avg_diff, real_tempo, real_tempo_diff, new_tempo, (double)((1000000.0 * 60.0)/new_tempo)); +                                    MusEGlobal::song->addExternalTempo(TempoRecEvent(add_tick, new_tempo)); +                                  }   +                                   +                                  // Reset all the poles. +                                  //for(int i = 0; i < clockAveragerPoles; ++i) +                                  // We have a value for this pole, let's keep it but reset the other poles. +                                  for(int i = 1; i < _clockAveragerPoles; ++i) +                                  { +                                    _avgClkDiffCounter[i] = 0; +                                    _averagerFull[i] = false; +                                  } +                                  break; +                                } +                              } +                               +                              // On the last pole?  +                              // All averagers need to be full before we can use the data... +                              if(pole == fin_idx) +                              { +                                double real_tempo = 60.0/(avg_diff * 24.0); +                                double real_tempo_diff = abs(real_tempo - _lastRealTempo); +                                 +                                if(real_tempo_diff >= _tempoQuantizeAmount/2.0) // Anti-hysteresis +                                { +                                  if(_tempoQuantizeAmount > 0.0) +                                  { +                                    double f_mod = fmod(real_tempo, _tempoQuantizeAmount); +                                    if(f_mod < _tempoQuantizeAmount/2.0) +                                      real_tempo -= f_mod; +                                    else +                                      real_tempo += _tempoQuantizeAmount - f_mod; +                                  } +                                  _lastRealTempo = real_tempo; +                                  int new_tempo = ((1000000.0 * 60.0) / (real_tempo)); +                                   +                                  if(new_tempo != lastTempo) +                                  { +                                    lastTempo = new_tempo; +                                    // Compute tick for this tempo - it is way back in time.  +                                    int tick_idx_sub = 0; +                                    for(int i = 0; i <= pole; ++i) +                                      tick_idx_sub += _clockAveragerStages[i]; +                                    // Compensate: Each pole > 0 has a delay one less than its number of stages.  +                                    // For example three pole {8, 8, 8} has a delay of 22 not 24. +                                    tick_idx_sub -= pole; +                                    int add_tick = MusEGlobal::curExtMidiSyncTick - tick_idx_sub * div; +                                    if(add_tick < 0)  +                                    { +                                      printf("FIXME sync: adding new tempo curExtMidiSyncTick:%d: add_tick:%d < 0 !\n", MusEGlobal::curExtMidiSyncTick, add_tick); +                                      add_tick = 0; +                                    } +                                    if(MusEGlobal::debugSync) +                                      printf("adding new tempo tick:%d curExtMidiSyncTick:%d tick_idx_sub:%d avg_diff:%f real_tempo:%f new_tempo:%d = %f\n", add_tick, MusEGlobal::curExtMidiSyncTick, tick_idx_sub, avg_diff, real_tempo, new_tempo, (double)((1000000.0 * 60.0)/new_tempo)); +                                    MusEGlobal::song->addExternalTempo(TempoRecEvent(add_tick, new_tempo)); +                                  }   +                                } +                              } +                            } +                          } +                        } +                      } +                    }                    }  //BEGIN : Original code: DELETETHIS 250 @@ -1185,8 +1343,6 @@ void MidiSeq::realtimeSystemInput(int port, int c)                          alignAllTicks();                          storedtimediffs = 0; -                        for (int i=0; i<24; i++) -                              timediff[i] = 0.0;                          // p3.3.26 1/23/10 DELETETHIS 6                          // Changed because msgPlay calls MusEGlobal::audioDevice->seekTransport(song->cPos()) @@ -1243,7 +1399,7 @@ void MidiSeq::realtimeSystemInput(int port, int c)                      if (MusEGlobal::debugSync)                            printf("realtimeSystemInput stop\n"); -                     +                      //DELETETHIS 7                      // Just in case the process still runs a cycle or two and causes the                       //  audio tick position to increment, reset the incrementer and force  diff --git a/muse2/muse/sync.h b/muse2/muse/sync.h index 41ad34ad..09ea06e9 100644 --- a/muse2/muse/sync.h +++ b/muse2/muse/sync.h @@ -32,9 +32,11 @@ namespace MusECore {  class Xml; -  class MidiSyncInfo  { +  public: +    enum SyncRecFilterPresetType { NONE=0, TINY, SMALL, MEDIUM, LARGE, LARGE_WITH_PRE_DETECT, TYPE_END }; +        private:      int _port; @@ -151,6 +153,9 @@ extern int volatile curMidiSyncInPort;  extern MusECore::BValue useJackTransport;  extern bool volatile jackTransportMaster;  extern unsigned int syncSendFirstClockDelay; // In milliseconds. +extern unsigned int volatile lastExtMidiSyncTick; +extern MusECore::MidiSyncInfo::SyncRecFilterPresetType syncRecFilterPreset; +extern double syncRecTempoValQuant;  } // namespace MusEGlobal diff --git a/muse2/muse/tempo.cpp b/muse2/muse/tempo.cpp index 1147fd78..d339f516 100644 --- a/muse2/muse/tempo.cpp +++ b/muse2/muse/tempo.cpp @@ -32,6 +32,7 @@  namespace MusEGlobal {  MusECore::TempoList tempomap; +MusECore::TempoRecList tempo_rec_list;  }  namespace MusECore { @@ -59,7 +60,7 @@ TempoList::~TempoList()  //   add  //--------------------------------------------------------- -void TempoList::add(unsigned tick, int tempo) +void TempoList::add(unsigned tick, int tempo, bool do_normalize)        {        if (tick > MAX_TICK)              tick = MAX_TICK; @@ -74,7 +75,8 @@ void TempoList::add(unsigned tick, int tempo)              ne->tick   = tick;              insert(std::pair<const unsigned, TEvent*> (tick, ev));              } -      normalize(); +      if(do_normalize)       +        normalize();        }  //--------------------------------------------------------- @@ -120,6 +122,33 @@ void TempoList::clear()        }  //--------------------------------------------------------- +//   eraseRange +//--------------------------------------------------------- + +void TempoList::eraseRange(unsigned stick, unsigned etick) +{ +    if(stick >= etick || stick > MAX_TICK) +      return; +    if(etick > MAX_TICK) +      etick = MAX_TICK; +     +    iTEvent se = MusEGlobal::tempomap.upper_bound(stick); +    if(se == end() || (se->first == MAX_TICK+1)) +      return; + +    iTEvent ee = MusEGlobal::tempomap.upper_bound(etick); + +    ee->second->tempo = se->second->tempo; +    ee->second->tick = se->second->tick; + +    for(iTEvent ite = se; ite != ee; ++ite) +      delete ite->second; +    erase(se, ee); // Erase range does NOT include the last element. +    normalize(); +    ++_tempoSN; +} +       +//---------------------------------------------------------  //   tempo  //--------------------------------------------------------- @@ -224,9 +253,9 @@ void TempoList::setGlobalTempo(int val)  //   addTempo  //--------------------------------------------------------- -void TempoList::addTempo(unsigned t, int tempo) +void TempoList::addTempo(unsigned t, int tempo, bool do_normalize)        { -      add(t, tempo); +      add(t, tempo, do_normalize);        ++_tempoSN;        } @@ -538,5 +567,54 @@ int TEvent::read(Xml& xml)        return 0;        } +//--------------------------------------------------------- +//   put +//    return true on fifo overflow +//--------------------------------------------------------- + +bool TempoFifo::put(const TempoRecEvent& event) +      { +      if (size < TEMPO_FIFO_SIZE) { +            fifo[wIndex] = event; +            wIndex = (wIndex + 1) % TEMPO_FIFO_SIZE; +            // q_atomic_increment(&size); +            ++size; +            return false; +            } +      return true; +      } + +//--------------------------------------------------------- +//   get +//--------------------------------------------------------- + +TempoRecEvent TempoFifo::get() +      { +      TempoRecEvent event(fifo[rIndex]); +      rIndex = (rIndex + 1) % TEMPO_FIFO_SIZE; +      --size; +      return event; +      } + +//--------------------------------------------------------- +//   peek +//--------------------------------------------------------- + +const TempoRecEvent& TempoFifo::peek(int n) +      { +      int idx = (rIndex + n) % TEMPO_FIFO_SIZE; +      return fifo[idx]; +      } + +//--------------------------------------------------------- +//   remove +//--------------------------------------------------------- + +void TempoFifo::remove() +      { +      rIndex = (rIndex + 1) % TEMPO_FIFO_SIZE; +      --size; +      } +        } // namespace MusECore diff --git a/muse2/muse/tempo.h b/muse2/muse/tempo.h index 7a3f413b..71f1580c 100644 --- a/muse2/muse/tempo.h +++ b/muse2/muse/tempo.h @@ -25,11 +25,16 @@  #define __TEMPO_H__  #include <map> +#include <vector>  #ifndef MAX_TICK  #define MAX_TICK (0x7fffffff/100)  #endif +// Tempo ring buffer size +#define TEMPO_FIFO_SIZE    1024 + +  namespace MusECore {  class Xml; @@ -70,8 +75,7 @@ class TempoList : public TEMPOLIST {        int _tempo;             // tempo if not using tempo list        int _globalTempo;       // %percent 50-200% -      void normalize(); -      void add(unsigned tick, int tempo); +      void add(unsigned tick, int tempo, bool do_normalize = true);        void change(unsigned tick, int newTempo);        void del(iTEvent);        void del(unsigned tick); @@ -79,7 +83,9 @@ class TempoList : public TEMPOLIST {     public:        TempoList();        ~TempoList(); +      void normalize();        void clear(); +      void eraseRange(unsigned stick, unsigned etick);        void read(Xml&);        void write(int, Xml&) const; @@ -96,18 +102,62 @@ class TempoList : public TEMPOLIST {        int tempoSN() const { return _tempoSN; }        void setTempo(unsigned tick, int newTempo); -      void addTempo(unsigned t, int tempo); +      void addTempo(unsigned t, int tempo, bool do_normalize = true);        void delTempo(unsigned tick);        void changeTempo(unsigned tick, int newTempo); +      bool masterFlag() const { return useList; }        bool setMasterFlag(unsigned tick, bool val);        int globalTempo() const           { return _globalTempo; }        void setGlobalTempo(int val);        }; +//--------------------------------------------------------- +//   Tempo Record Event +//--------------------------------------------------------- + +struct TempoRecEvent { +      int tempo; +      unsigned tick;     +      TempoRecEvent() { } +      TempoRecEvent(unsigned tk, unsigned t) { +            tick  = tk; +            tempo = t; +            } +      }; + +class TempoRecList : public std::vector<TempoRecEvent > +{ +  public: +    void addTempo(int tick, int tempo)    { push_back(TempoRecEvent(tick, tempo)); } +    void addTempo(const TempoRecEvent& e) { push_back(e); } +}; + +//--------------------------------------------------------- +//   TempoFifo +//--------------------------------------------------------- + +class TempoFifo { +      TempoRecEvent fifo[TEMPO_FIFO_SIZE]; +      volatile int size; +      int wIndex; +      int rIndex; + +   public: +      TempoFifo()  { clear(); } +      bool put(const TempoRecEvent& event);   // returns true on fifo overflow +      TempoRecEvent get(); +      const TempoRecEvent& peek(int = 0); +      void remove(); +      bool isEmpty() const { return size == 0; } +      void clear()         { size = 0, wIndex = 0, rIndex = 0; } +      int getSize() const  { return size; } +      }; +        } // namespace MusECore  namespace MusEGlobal {  extern MusECore::TempoList tempomap; +extern MusECore::TempoRecList tempo_rec_list;  }  #endif diff --git a/muse2/muse/thread.cpp b/muse2/muse/thread.cpp index 69238922..14f9750e 100644 --- a/muse2/muse/thread.cpp +++ b/muse2/muse/thread.cpp @@ -250,61 +250,11 @@ void Thread::removePollFd(int fd, int action)  void Thread::loop()        { -      // Changed by Tim. p3.3.17 -        if (!MusEGlobal::debugMode) {              if (mlockall(MCL_CURRENT | MCL_FUTURE))                    perror("WARNING: Cannot lock memory:");              } -/*      DELETETHIS 46  -      pthread_attr_t* attributes = 0; -      attributes = (pthread_attr_t*) malloc(sizeof(pthread_attr_t)); -      pthread_attr_init(attributes); - -      if (MusEGlobal::realTimeScheduling && realTimePriority > 0) { - -            doSetuid(); -//             if (pthread_attr_setschedpolicy(attributes, SCHED_FIFO)) { -//                   printf("cannot set FIFO scheduling class for RT thread\n"); -//                   } -//             if (pthread_attr_setscope (attributes, PTHREAD_SCOPE_SYSTEM)) { -//                   printf("Cannot set scheduling scope for RT thread\n"); -//                   } -//             struct sched_param rt_param; -//             memset(&rt_param, 0, sizeof(rt_param)); -//             rt_param.sched_priority = realTimePriority; -//             if (pthread_attr_setschedparam (attributes, &rt_param)) { -//                   printf("Cannot set scheduling priority %d for RT thread (%s)\n", -//                      realTimePriority, strerror(errno)); -//                   } - -           // do the SCHED_FIFO stuff _after_ thread creation: -           struct sched_param *param = new struct sched_param; -           param->sched_priority = realTimePriority; -           int error = pthread_setschedparam(pthread_self(), SCHED_FIFO, param); -            if (error != 0) -              perror( "error set_schedparam 2:"); - -//          if (!MusEGlobal::debugMode) { -//                if (mlockall(MCL_CURRENT|MCL_FUTURE)) -//                      perror("WARNING: Cannot lock memory:"); -//                } - -            undoSetuid(); -            } - -*/ - - -/* -#define BIG_ENOUGH_STACK (1024*1024*1) -      char buf[BIG_ENOUGH_STACK]; -      for (int i = 0; i < BIG_ENOUGH_STACK; i++) -            buf[i] = i; -#undef BIG_ENOUGH_STACK -*/ -  #ifdef __APPLE__  #define BIG_ENOUGH_STACK (1024*256*1)  #else @@ -318,7 +268,8 @@ void Thread::loop()        pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, 0);        pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, 0); -      int policy = 0; +      int policy = buf[0]; // Initialize using buf[0] to keep the compiler from complaining about unused buf. +      policy = 0;          // Now set the true desired inital value.        if ((policy = sched_getscheduler (0)) < 0) {              printf("Thread: Cannot get current client scheduler: %s\n", strerror(errno));              } diff --git a/muse2/muse/track.cpp b/muse2/muse/track.cpp index 905f9030..d353b4bb 100644 --- a/muse2/muse/track.cpp +++ b/muse2/muse/track.cpp @@ -37,6 +37,7 @@  #include "midictrl.h"  #include "helper.h"  #include "limits.h" +#include "dssihost.h"  namespace MusECore { @@ -384,7 +385,21 @@ void Track::clearRecAutomation(bool clearList)          continue;        p->enableAllControllers(true);      } -       + +    if(type() == AUDIO_SOFTSYNTH) +    { +      SynthI* synth = static_cast<SynthI*>(this); +      if(synth->synth() && synth->synth()->synthType() == Synth::DSSI_SYNTH) +      { +        SynthIF* sif = synth->sif(); +        if(sif) +        { +          DssiSynthIF* dssi_sif = static_cast<DssiSynthIF*>(sif); +          dssi_sif->enableAllControllers(true); +        } +      } +    } +          if(clearList)        t->recEvents()->clear();  } diff --git a/muse2/muse/track.h b/muse2/muse/track.h index 93f2f673..3da34912 100644 --- a/muse2/muse/track.h +++ b/muse2/muse/track.h @@ -46,7 +46,7 @@ class PluginI;  class SynthI;  class Xml;  class DrumMap; - +class ControlEvent;  //---------------------------------------------------------  //   Track @@ -447,6 +447,9 @@ class AudioTrack : public Track {        virtual void setAutomationType(AutomationType t);        void processAutomationEvents();        CtrlRecList* recEvents()                         { return &_recEvents; } +      bool addScheduledControlEvent(int track_ctrl_id, float val, unsigned frame); // return true if event cannot be delivered +      void enableController(int track_ctrl_id, bool en);  +      void controllersEnabled(int track_ctrl_id, bool* en1, bool* en2) const;        void recordAutomation(int n, double v);        void startAutoRecord(int, double);        void stopAutoRecord(int, double); diff --git a/muse2/muse/undo.h b/muse2/muse/undo.h index b88a9457..2f582d8e 100644 --- a/muse2/muse/undo.h +++ b/muse2/muse/undo.h @@ -125,12 +125,12 @@ struct UndoOp {        UndoOp(UndoType type, Track* track, const char* old_name, const char* new_name);        UndoOp(UndoType type, Track* track, int old_chan, int new_chan);        UndoOp(UndoType type); -      }; +};  class Undo : public std::list<UndoOp> {     public:        bool empty() const; -      }; +};  typedef Undo::iterator iUndoOp;  typedef Undo::reverse_iterator riUndoOp; @@ -141,7 +141,7 @@ class UndoList : public std::list<Undo> {     public:        void clearDelete();        UndoList(bool _isUndo) : std::list<Undo>() { isUndo=_isUndo; } -      }; +};  typedef UndoList::iterator iUndo;  typedef UndoList::reverse_iterator riUndo; diff --git a/muse2/muse/wave.cpp b/muse2/muse/wave.cpp index 5e19648d..8d17a10d 100644 --- a/muse2/muse/wave.cpp +++ b/muse2/muse/wave.cpp @@ -964,7 +964,7 @@ int ClipList::idx(const Clip& clip) const  //   cmdAddRecordedWave  //--------------------------------------------------------- -void Song::cmdAddRecordedWave(MusECore::WaveTrack* track, MusECore::Pos s, MusECore::Pos e) +void Song::cmdAddRecordedWave(MusECore::WaveTrack* track, MusECore::Pos s, MusECore::Pos e)         {        if (MusEGlobal::debugMsg)            printf("cmdAddRecordedWave - loopCount = %d, punchin = %d", MusEGlobal::audio->loopCount(), punchin()); @@ -976,15 +976,31 @@ void Song::cmdAddRecordedWave(MusECore::WaveTrack* track, MusECore::Pos s, MusEC              return;              } +      // If externally clocking (and therefore master was forced off),  +      //  tempos may have been recorded. We really should temporarily force +      //  the master tempo map on in order to properly determine the ticks below. +      // Else internal clocking, the user decided to record either with or without  +      //  master on, so let it be. +      // FIXME: We really should allow the master flag to be on at the same time as +      //  the external sync flag! AFAIR when external sync is on, no part of the app shall +      //  depend on the tempo map anyway, so it should not matter whether it's on or off. +      // If we do that, then we may be able to remove this section and user simply decides +      //  whether master is on/off, because we may be able to use the flag to determine +      //  whether to record external tempos at all, because we may want a switch for it! +      bool master_was_on = MusEGlobal::tempomap.masterFlag(); +      if(MusEGlobal::extSyncFlag.value() && !master_was_on) +        MusEGlobal::tempomap.setMasterFlag(0, true); +              if((MusEGlobal::audio->loopCount() > 0 && s.tick() > lPos().tick()) || (punchin() && s.tick() < lPos().tick()))          s.setTick(lPos().tick());        // If we are looping, just set the end to the right marker, since we don't know how many loops have occurred.        // (Fixed: Added Audio::loopCount)        // Otherwise if punchout is on, limit the end to the right marker. -      if((MusEGlobal::audio->loopCount() > 0) || (punchout() && e.tick() > rPos().tick()) ) +      if((MusEGlobal::audio->loopCount() > 0) || (punchout() && e.tick() > rPos().tick()) )            e.setTick(rPos().tick()); +        // No part to be created? Delete the rec sound file. -      if(s.tick() >= e.tick()) +      if(s.frame() >= e.frame())        {          QString st = f->path();          // The function which calls this function already does this immediately after. But do it here anyway. @@ -992,19 +1008,30 @@ void Song::cmdAddRecordedWave(MusECore::WaveTrack* track, MusECore::Pos s, MusEC                                   // counter has dropped by 2 and _recFile will probably deleted then          remove(st.toLatin1().constData());          if(MusEGlobal::debugMsg) -          printf("Song::cmdAddRecordedWave: remove file %s - start=%d end=%d\n", st.toLatin1().constData(), s.tick(), e.tick()); +          printf("Song::cmdAddRecordedWave: remove file %s - startframe=%d endframe=%d\n", st.toLatin1().constData(), s.frame(), e.frame());   +       +        // Restore master flag.  +        if(MusEGlobal::extSyncFlag.value() && !master_was_on) +          MusEGlobal::tempomap.setMasterFlag(0, false); +          return;        }        // Round the start down using the Arranger part snap raster value.  -      unsigned startTick = AL::sigmap.raster1(s.tick(), MusEGlobal::song->arrangerRaster()); +      int a_rast = MusEGlobal::song->arrangerRaster(); +      unsigned sframe = (a_rast == 1) ? s.frame() : Pos(AL::sigmap.raster1(s.tick(), MusEGlobal::song->arrangerRaster())).frame();           // Round the end up using the Arranger part snap raster value.  -      unsigned endTick   = AL::sigmap.raster2(e.tick(), MusEGlobal::song->arrangerRaster()); +      unsigned eframe = (a_rast == 1) ? e.frame() : Pos(AL::sigmap.raster2(e.tick(), MusEGlobal::song->arrangerRaster())).frame(); +      unsigned etick = Pos(eframe).tick(); + +      // Done using master tempo map. Restore master flag.  +      if(MusEGlobal::extSyncFlag.value() && !master_was_on) +        MusEGlobal::tempomap.setMasterFlag(0, false);        f->update();        MusECore::WavePart* part = new MusECore::WavePart(track); -      part->setTick(startTick); -      part->setLenTick(endTick - startTick); +      part->setFrame(sframe); +      part->setLenFrame(eframe - sframe);        part->setName(track->name());        // create Event @@ -1015,20 +1042,18 @@ void Song::cmdAddRecordedWave(MusECore::WaveTrack* track, MusECore::Pos s, MusEC        track->setRecFile(0);        event.setSpos(0); -              // Since the part start was snapped down, we must apply the difference so that the        //  wave event tick lines up with when the user actually started recording. -      // Added by Tim. p3.3.8 -      event.setTick(s.tick() - startTick); -       -       +      event.setFrame(s.frame() - sframe);    +      // NO Can't use this. SF reports too long samples at first part recorded in sequence. See samples() - funny business with SEEK ? +      //event.setLenFrame(f.samples());         event.setLenFrame(e.frame() - s.frame());        part->addEvent(event);        MusEGlobal::song->cmdAddPart(part); -      if (MusEGlobal::song->len() < endTick) -            MusEGlobal::song->setLen(endTick); +      if (MusEGlobal::song->len() < etick) +            MusEGlobal::song->setLen(etick);        }  //--------------------------------------------------------- diff --git a/muse2/muse/wavetrack.cpp b/muse2/muse/wavetrack.cpp index dd890b42..b55a67d6 100644 --- a/muse2/muse/wavetrack.cpp +++ b/muse2/muse/wavetrack.cpp @@ -223,7 +223,17 @@ bool WaveTrack::getData(unsigned framePos, int channels, unsigned nframe, float*                          if (MusEGlobal::audio->freewheel()) {                                }                          else { -                              if (fifo.put(channels, nframe, bp, MusEGlobal::audio->pos().frame())) +#ifdef _AUDIO_USE_TRUE_FRAME_ +                              // TODO: Tested: This is the line that would be needed for Audio Inputs,  +                              //  because the data arrived in the previous period! Test OK, the waves are in sync. +                              // So we need to do Audio Inputs separately above, AND find a way to mix two overlapping +                              //  periods into the file! Nothing wrong with the FIFO per se, we could stamp overlapping +                              //  times. But the soundfile just writes, does not mix. +                              //if (fifo.put(channels, nframe, bp, MusEGlobal::audio->previousPos().frame())) +                              // +                              // Tested: This line is OK for track-to-track recording, the waves are in sync: +#endif                               +                              if (fifo.put(channels, nframe, bp, MusEGlobal::audio->pos().frame()))                                        printf("WaveTrack::getData(%d, %d, %d): fifo overrun\n",                                         framePos, channels, nframe);                                } diff --git a/muse2/muse/widgets/CMakeLists.txt b/muse2/muse/widgets/CMakeLists.txt index fae0d614..88706339 100644 --- a/muse2/muse/widgets/CMakeLists.txt +++ b/muse2/muse/widgets/CMakeLists.txt @@ -57,6 +57,7 @@ QT4_WRAP_CPP (widget_mocs        menutitleitem.h        meter.h        metronome.h   +      midi_audio_control.h          midisyncimpl.h          mixdowndialog.h          mlabel.h   @@ -122,6 +123,7 @@ file (GLOB widgets_ui_files        itransformbase.ui          metronomebase.ui          midisync.ui   +      midi_audio_control_base.ui          mittransposebase.ui          mixdowndialogbase.ui          mtrackinfobase.ui @@ -169,6 +171,7 @@ file (GLOB widgets_source_files        menutitleitem.cpp        meter.cpp        metronome.cpp  +      midi_audio_control.cpp         midisyncimpl.cpp         mixdowndialog.cpp         mlabel.cpp  diff --git a/muse2/muse/widgets/aboutbox.ui b/muse2/muse/widgets/aboutbox.ui index 250f656f..8b4d5b37 100644 --- a/muse2/muse/widgets/aboutbox.ui +++ b/muse2/muse/widgets/aboutbox.ui @@ -48,7 +48,7 @@     <item>      <widget class="QLabel" name="versionLabel">       <property name="text"> -      <string>Version 2 pre-alpha</string> +      <string>Version 2</string>       </property>       <property name="wordWrap">        <bool>false</bool> @@ -58,7 +58,7 @@     <item>      <widget class="QLabel" name="textLabel1">       <property name="text"> -      <string>(C) Copyright 1999-2010 Werner Schweer and others. +      <string>(C) Copyright 1999-2012 Werner Schweer and others.  See http://www.muse-sequencer.org for new versions and  more information. diff --git a/muse2/muse/widgets/bigtime.cpp b/muse2/muse/widgets/bigtime.cpp index 0b213f28..5adf4966 100644 --- a/muse2/muse/widgets/bigtime.cpp +++ b/muse2/muse/widgets/bigtime.cpp @@ -32,6 +32,7 @@  #include "song.h"  #include "app.h"  #include "gconfig.h" +#include "audio.h"  namespace MusEGlobal {  extern int mtcType; @@ -229,7 +230,9 @@ bool BigTime::setString(unsigned v)              return true;              } -      unsigned absFrame = MusEGlobal::tempomap.tick2frame(v); +      // Quick fix: Not much to do but ignore the supplied tick: We need the exact frame here. +      unsigned absFrame = MusEGlobal::audio->pos().frame(); +              int bar, beat;        unsigned tick;        AL::sigmap.tickValues(v, &bar, &beat, &tick); diff --git a/muse2/muse/widgets/filedialog.cpp b/muse2/muse/widgets/filedialog.cpp index 6e7d6882..aa8c5df1 100644 --- a/muse2/muse/widgets/filedialog.cpp +++ b/muse2/muse/widgets/filedialog.cpp @@ -102,6 +102,7 @@ void MFileDialog::globalToggled(bool flag)        {        if (flag) {              buttons.readMidiPortsButton->setChecked(false); +            readMidiPortsSaved = false;              if (lastGlobalDir.isEmpty())                    lastGlobalDir = MusEGlobal::museGlobalShare + QString("/") + baseDir; // Initialize if first time              setDirectory(lastGlobalDir); @@ -117,6 +118,7 @@ void MFileDialog::userToggled(bool flag)        {        if (flag) {              buttons.readMidiPortsButton->setChecked(true); +            readMidiPortsSaved = true;              if (lastUserDir.isEmpty()) {                    //lastUserDir = MusEGlobal::museUser + QString("/") + baseDir; // Initialize if first time                    lastUserDir = MusEGlobal::configPath + QString("/") + baseDir; // Initialize if first time    // p4.0.39 @@ -140,6 +142,7 @@ void MFileDialog::projectToggled(bool flag)        {        if (flag) {              buttons.readMidiPortsButton->setChecked(true); +            readMidiPortsSaved = true;              QString s;              if (MusEGlobal::museProject == MusEGlobal::museProjectInitPath ) {                    // if project path is uninitialized, meaning it is still set to museProjectInitPath. @@ -158,6 +161,29 @@ void MFileDialog::projectToggled(bool flag)              }        } +void MFileDialog::fileChanged(const QString& path) +{ +  bool is_mid = path.endsWith(".mid", Qt::CaseInsensitive) || +                path.endsWith(".midi", Qt::CaseInsensitive) || +                path.endsWith(".kar", Qt::CaseInsensitive); +   +  if (is_mid) +  { +    readMidiPortsSaved=buttons.readMidiPortsButton->isChecked(); +    buttons.readMidiPortsButton->setEnabled(false); +    buttons.readMidiPortsButton->setChecked(false); +  } +  else +  { +    if (!buttons.readMidiPortsButton->isEnabled()) +    { +      buttons.readMidiPortsButton->setEnabled(true); +      buttons.readMidiPortsButton->setChecked(readMidiPortsSaved); +    } +  } +   +} +  //---------------------------------------------------------  //   MFileDialog @@ -167,6 +193,7 @@ MFileDialog::MFileDialog(const QString& dir,     const QString& filter, QWidget* parent, bool writeFlag)    : QFileDialog(parent, QString(), QString("."), filter)        { +      readMidiPortsSaved = true;        showButtons = false;        lastUserDir = "";        lastGlobalDir = ""; @@ -201,10 +228,11 @@ MFileDialog::MFileDialog(const QString& dir,              buttons.userButton->setAutoExclusive(true);              buttons.projectButton->setAutoExclusive(true); -	    connect(buttons.globalButton, SIGNAL(toggled(bool)), this, SLOT(globalToggled(bool))); +            connect(buttons.globalButton, SIGNAL(toggled(bool)), this, SLOT(globalToggled(bool)));              connect(buttons.userButton, SIGNAL(toggled(bool)), this, SLOT(userToggled(bool)));              connect(buttons.projectButton, SIGNAL(toggled(bool)), this, SLOT(projectToggled(bool)));              connect(this, SIGNAL(directoryEntered(const QString&)), SLOT(directoryChanged(const QString&))); +            connect(this, SIGNAL(currentChanged(const QString&)), SLOT(fileChanged(const QString&)));              if (writeFlag) {                    setAcceptMode(QFileDialog::AcceptSave); diff --git a/muse2/muse/widgets/filedialog.h b/muse2/muse/widgets/filedialog.h index 1e2616da..582e943d 100644 --- a/muse2/muse/widgets/filedialog.h +++ b/muse2/muse/widgets/filedialog.h @@ -52,9 +52,12 @@ class MFileDialog : public QFileDialog {        QString  lastUserDir, lastGlobalDir;        bool showButtons;        QString baseDir; +       +      bool readMidiPortsSaved;     private slots:        void directoryChanged(const QString& directory); +      void fileChanged(const QString&);     public slots:        void globalToggled(bool);        void userToggled(bool); diff --git a/muse2/muse/widgets/midi_audio_control.cpp b/muse2/muse/widgets/midi_audio_control.cpp new file mode 100644 index 00000000..78c8de3c --- /dev/null +++ b/muse2/muse/widgets/midi_audio_control.cpp @@ -0,0 +1,340 @@ +//========================================================= +//  MusE +//  Linux Music Editor +// +//  midi_audio_control.cpp +//  Copyright (C) 2012 by Tim E. Real (terminator356 at users.sourceforge.net) +// +//  This program is free software; you can redistribute it and/or +//  modify it under the terms of the GNU General Public License +//  as published by the Free Software Foundation; version 2 of +//  the License, or (at your option) any later version. +// +//  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., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA. +// +//========================================================= +#include "midi_audio_control.h" + +#include "globals.h" +#include "globaldefs.h" +#include "mididev.h" +#include "midiport.h" +#include "midictrl.h" +#include "audio.h" +#include "app.h" + +#include <QTimer> + +namespace MusEGui { + +// ----------------------------------- +//   MidiAudioControl +//   Set port to -1 to automatically set it to the port of  +//    the first combo box item (the first readable port). +// ----------------------------------- + +MidiAudioControl::MidiAudioControl(int port, int chan, int ctrl, QWidget* parent) +  : QDialog(parent) +{ +  setupUi(this); + +  _port = port; +  _chan = chan; +  _ctrl = ctrl; +  _is_learning = false; +   +  update(); +   +  connect(learnPushButton, SIGNAL(clicked(bool)), SLOT(learnChanged(bool))); +  connect(portComboBox, SIGNAL(currentIndexChanged(int)), SLOT(portChanged(int))); +  connect(channelSpinBox, SIGNAL(valueChanged(int)), SLOT(chanChanged())); +  connect(controlTypeComboBox, SIGNAL(currentIndexChanged(int)), SLOT(ctrlTypeChanged(int))); +  connect(ctrlHiSpinBox, SIGNAL(valueChanged(int)), SLOT(ctrlHChanged())); +  connect(ctrlLoSpinBox, SIGNAL(valueChanged(int)), SLOT(ctrlLChanged())); +  connect(MusEGlobal::muse, SIGNAL(configChanged()), SLOT(configChanged())); +  connect(MusEGlobal::heartBeatTimer, SIGNAL(timeout()), SLOT(heartbeat())); +} + +void MidiAudioControl::heartbeat() +{ +  if(_is_learning) +  { +    if(MusEGlobal::midiLearnPort != -1)  +    { +      int port_item = portComboBox->findData(MusEGlobal::midiLearnPort); +      if(port_item != -1 && port_item != portComboBox->currentIndex()) +      { +        _port = MusEGlobal::midiLearnPort; +        portComboBox->blockSignals(true); +        portComboBox->setCurrentIndex(port_item); +        portComboBox->blockSignals(false); +      } +    } +     +    if(MusEGlobal::midiLearnChan != -1 && (MusEGlobal::midiLearnChan + 1) != channelSpinBox->value()) +    { +      _chan = MusEGlobal::midiLearnChan; +      channelSpinBox->blockSignals(true); +      channelSpinBox->setValue(_chan + 1); +      channelSpinBox->blockSignals(false); +    } + +    if(MusEGlobal::midiLearnCtrl != -1) +    { +      int type = MusECore::midiControllerType(MusEGlobal::midiLearnCtrl); +      if(type < controlTypeComboBox->count() && type != controlTypeComboBox->currentIndex()) +      { +        controlTypeComboBox->blockSignals(true); +        controlTypeComboBox->setCurrentIndex(type); +        controlTypeComboBox->blockSignals(false); +      } + +      int hv = (MusEGlobal::midiLearnCtrl >> 8) & 0xff; +      int lv = MusEGlobal::midiLearnCtrl & 0xff; +      if(type == MusECore::MidiController::Program || type == MusECore::MidiController::Pitch) +      { +        ctrlHiSpinBox->setEnabled(false); +        ctrlLoSpinBox->setEnabled(false); +        ctrlHiSpinBox->blockSignals(true); +        ctrlLoSpinBox->blockSignals(true); +        ctrlHiSpinBox->setValue(0); +        ctrlLoSpinBox->setValue(0); +        ctrlHiSpinBox->blockSignals(false); +        ctrlLoSpinBox->blockSignals(false); +      } +      else if(type == MusECore::MidiController::Controller7) +      { +        ctrlHiSpinBox->setEnabled(false); +        ctrlLoSpinBox->setEnabled(true); + +        ctrlHiSpinBox->blockSignals(true); +        ctrlHiSpinBox->setValue(0); +        ctrlHiSpinBox->blockSignals(false); +         +        if(lv != ctrlLoSpinBox->value()) +        { +          ctrlLoSpinBox->blockSignals(true); +          ctrlLoSpinBox->setValue(lv); +          ctrlLoSpinBox->blockSignals(false); +        } +      } +      else +      { +        ctrlHiSpinBox->setEnabled(true); +        ctrlLoSpinBox->setEnabled(true); +        if(hv != ctrlHiSpinBox->value()) +        { +          ctrlHiSpinBox->blockSignals(true); +          ctrlHiSpinBox->setValue(hv); +          ctrlHiSpinBox->blockSignals(false); +        } +        if(lv != ctrlLoSpinBox->value()) +        { +          ctrlLoSpinBox->blockSignals(true); +          ctrlLoSpinBox->setValue(lv); +          ctrlLoSpinBox->blockSignals(false); +        } +      }   +       +      _ctrl = MusECore::midiCtrlTerms2Number(type, (ctrlHiSpinBox->value() << 8) + ctrlLoSpinBox->value()); +    } +  } +} + +void MidiAudioControl::learnChanged(bool v) +{ +  _is_learning = v; +  if(_is_learning) +    MusEGlobal::audio->msgStartMidiLearn();  // Resets the learn values to -1. +} + +void MidiAudioControl::resetLearn() +{ +  _is_learning = false; +  learnPushButton->blockSignals(true); +  learnPushButton->setChecked(false); +  learnPushButton->blockSignals(false); +  MusEGlobal::audio->msgStartMidiLearn();  // Resets the learn values to -1. +} + +void MidiAudioControl::portChanged(int idx) +{ +  if(idx == -1) +    return; +  int port_num = portComboBox->itemData(idx).toInt(); +  if(port_num < 0 || port_num >= MIDI_PORTS) +    return; + +  _port = port_num; +  resetLearn(); +} + +void MidiAudioControl::chanChanged() +{ +  _chan = channelSpinBox->value() - 1; +  resetLearn(); +} + +void MidiAudioControl::updateCtrlBoxes() +{ +  int idx = controlTypeComboBox->currentIndex(); +  if(idx == -1) +    return; +   +  if(idx == MusECore::MidiController::Program || idx == MusECore::MidiController::Pitch) +  { +    ctrlHiSpinBox->setEnabled(false); +    ctrlLoSpinBox->setEnabled(false); +    ctrlHiSpinBox->blockSignals(true); +    ctrlLoSpinBox->blockSignals(true); +    ctrlHiSpinBox->setValue(0); +    ctrlLoSpinBox->setValue(0); +    ctrlHiSpinBox->blockSignals(false); +    ctrlLoSpinBox->blockSignals(false); +  } +  else if(idx == MusECore::MidiController::Controller7) +  { +    ctrlHiSpinBox->setEnabled(false); +    ctrlLoSpinBox->setEnabled(true); + +    ctrlHiSpinBox->blockSignals(true); +    ctrlHiSpinBox->setValue(0); +    ctrlHiSpinBox->blockSignals(false); +  } +  else +  { +    ctrlHiSpinBox->setEnabled(true); +    ctrlLoSpinBox->setEnabled(true); +  }   +} + +void MidiAudioControl::ctrlTypeChanged(int idx) +{ +  if(idx == -1) +    return; +   +  updateCtrlBoxes(); +   +  _ctrl = (ctrlHiSpinBox->value() << 8) + ctrlLoSpinBox->value(); +  _ctrl = MusECore::midiCtrlTerms2Number(idx, _ctrl); + +  resetLearn(); +} + +void MidiAudioControl::ctrlHChanged() +{ +  if(controlTypeComboBox->currentIndex() == -1) +    return; +  _ctrl = (ctrlHiSpinBox->value() << 8) + ctrlLoSpinBox->value(); +  _ctrl = MusECore::midiCtrlTerms2Number(controlTypeComboBox->currentIndex(), _ctrl); + +  resetLearn(); +} + +void MidiAudioControl::ctrlLChanged() +{ +  if(controlTypeComboBox->currentIndex() == -1) +    return; +  _ctrl = (ctrlHiSpinBox->value() << 8) + ctrlLoSpinBox->value(); +  _ctrl = MusECore::midiCtrlTerms2Number(controlTypeComboBox->currentIndex(), _ctrl); + +  resetLearn(); +} + +void MidiAudioControl::configChanged() +{ +  update(); +} + +void MidiAudioControl::update() +{ +  portComboBox->blockSignals(true); +  portComboBox->clear(); + +  int item_idx = 0; +  for (int i = 0; i < MIDI_PORTS; ++i) { +        MusECore::MidiDevice* md = MusEGlobal::midiPorts[i].device();  +        if(!md)  // In the case of this combo box, don't bother listing empty ports.              +          continue; +        //if(!(md->rwFlags() & 1 || md->isSynti()) && (i != outPort))   +        if(!(md->rwFlags() & 2) && (i != _port))   // Only readable ports, or current one.     +          continue; +        QString name; +        name.sprintf("%d:%s", i+1, MusEGlobal::midiPorts[i].portname().toLatin1().constData()); +        portComboBox->insertItem(item_idx, name, i); +        if(_port == -1) +          _port = i;      // Initialize +        if(i == _port) +          portComboBox->setCurrentIndex(item_idx); +        item_idx++; +        } +  portComboBox->blockSignals(false); +   +  channelSpinBox->blockSignals(true); +  channelSpinBox->setValue(_chan + 1); +  channelSpinBox->blockSignals(false); + +  int type = MusECore::midiControllerType(_ctrl); +  if(type < controlTypeComboBox->count()) +  { +    controlTypeComboBox->blockSignals(true); +    controlTypeComboBox->setCurrentIndex(type); +    controlTypeComboBox->blockSignals(false); +  } + +  int hv = (_ctrl >> 8) & 0xff; +  int lv = _ctrl & 0xff; +  if(type == MusECore::MidiController::Program || type == MusECore::MidiController::Pitch) +  { +    ctrlHiSpinBox->setEnabled(false); +    ctrlLoSpinBox->setEnabled(false); +    ctrlHiSpinBox->blockSignals(true); +    ctrlLoSpinBox->blockSignals(true); +    ctrlHiSpinBox->setValue(0); +    ctrlLoSpinBox->setValue(0); +    ctrlHiSpinBox->blockSignals(false); +    ctrlLoSpinBox->blockSignals(false); +  } +  else if(type == MusECore::MidiController::Controller7) +  { +    ctrlHiSpinBox->setEnabled(false); +    ctrlLoSpinBox->setEnabled(true); + +    ctrlHiSpinBox->blockSignals(true); +    ctrlHiSpinBox->setValue(0); +    ctrlHiSpinBox->blockSignals(false); +     +    if(lv != ctrlLoSpinBox->value()) +    { +      ctrlLoSpinBox->blockSignals(true); +      ctrlLoSpinBox->setValue(lv); +      ctrlLoSpinBox->blockSignals(false); +    } +  } +  else +  { +    ctrlHiSpinBox->setEnabled(true); +    ctrlLoSpinBox->setEnabled(true); +    if(hv != ctrlHiSpinBox->value()) +    { +      ctrlHiSpinBox->blockSignals(true); +      ctrlHiSpinBox->setValue(hv); +      ctrlHiSpinBox->blockSignals(false); +    } +    if(lv != ctrlLoSpinBox->value()) +    { +      ctrlLoSpinBox->blockSignals(true); +      ctrlLoSpinBox->setValue(lv); +      ctrlLoSpinBox->blockSignals(false); +    } +  } +} + +} diff --git a/muse2/muse/widgets/midi_audio_control.h b/muse2/muse/widgets/midi_audio_control.h new file mode 100644 index 00000000..887de942 --- /dev/null +++ b/muse2/muse/widgets/midi_audio_control.h @@ -0,0 +1,60 @@ +//========================================================= +//  MusE +//  Linux Music Editor +// +//  midi_audio_control.h +//  Copyright (C) 2012 by Tim E. Real (terminator356 at users.sourceforge.net) +// +//  This program is free software; you can redistribute it and/or +//  modify it under the terms of the GNU General Public License +//  as published by the Free Software Foundation; version 2 of +//  the License, or (at your option) any later version. +// +//  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., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA. +// +//========================================================= +#ifndef MIDI_AUDIO_CONTROL_H +#define MIDI_AUDIO_CONTROL_H + +#include "ui_midi_audio_control_base.h" + +namespace MusEGui { + +class MidiAudioControl : public QDialog, public Ui::MidiAudioControlBase +{ +    Q_OBJECT + +private: +    int _port, _chan, _ctrl; +    bool _is_learning; +    void update(); +    void resetLearn(); +    void updateCtrlBoxes(); + +private slots: +    void heartbeat(); +    void learnChanged(bool); +    void portChanged(int); +    void chanChanged(); +    void ctrlTypeChanged(int); +    void ctrlHChanged(); +    void ctrlLChanged(); +    void configChanged(); + +public: +    MidiAudioControl(int port = -1, int chan = 0, int ctrl = 0, QWidget* parent = 0); +    int port() const { return _port; } +    int chan() const { return _chan; } +    int ctrl() const { return _ctrl; } +}; + +} + +#endif // MIDI_AUDIO_CONTROL_H diff --git a/muse2/muse/widgets/midi_audio_control_base.ui b/muse2/muse/widgets/midi_audio_control_base.ui new file mode 100644 index 00000000..2e341121 --- /dev/null +++ b/muse2/muse/widgets/midi_audio_control_base.ui @@ -0,0 +1,310 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>MidiAudioControlBase</class> + <widget class="QDialog" name="MidiAudioControlBase"> +  <property name="geometry"> +   <rect> +    <x>0</x> +    <y>0</y> +    <width>341</width> +    <height>148</height> +   </rect> +  </property> +  <property name="windowTitle"> +   <string>Midi control</string> +  </property> +  <layout class="QVBoxLayout" name="verticalLayout"> +   <item> +    <layout class="QHBoxLayout" name="horizontalLayout"> +     <item> +      <widget class="QLabel" name="label"> +       <property name="sizePolicy"> +        <sizepolicy hsizetype="Maximum" vsizetype="Preferred"> +         <horstretch>0</horstretch> +         <verstretch>0</verstretch> +        </sizepolicy> +       </property> +       <property name="text"> +        <string>Port:</string> +       </property> +      </widget> +     </item> +     <item> +      <widget class="QComboBox" name="portComboBox"/> +     </item> +     <item> +      <widget class="QLabel" name="label_2"> +       <property name="sizePolicy"> +        <sizepolicy hsizetype="Maximum" vsizetype="Preferred"> +         <horstretch>0</horstretch> +         <verstretch>0</verstretch> +        </sizepolicy> +       </property> +       <property name="text"> +        <string>Channel:</string> +       </property> +      </widget> +     </item> +     <item> +      <widget class="QSpinBox" name="channelSpinBox"> +       <property name="sizePolicy"> +        <sizepolicy hsizetype="Maximum" vsizetype="Fixed"> +         <horstretch>0</horstretch> +         <verstretch>0</verstretch> +        </sizepolicy> +       </property> +       <property name="minimum"> +        <number>1</number> +       </property> +       <property name="maximum"> +        <number>16</number> +       </property> +      </widget> +     </item> +    </layout> +   </item> +   <item> +    <layout class="QHBoxLayout" name="horizontalLayout_3"> +     <item> +      <widget class="QLabel" name="label_6"> +       <property name="text"> +        <string>Control type:</string> +       </property> +      </widget> +     </item> +     <item> +      <widget class="QComboBox" name="controlTypeComboBox"> +       <property name="sizePolicy"> +        <sizepolicy hsizetype="Maximum" vsizetype="Fixed"> +         <horstretch>0</horstretch> +         <verstretch>0</verstretch> +        </sizepolicy> +       </property> +       <item> +        <property name="text"> +         <string>Control7</string> +        </property> +       </item> +       <item> +        <property name="text"> +         <string>Control14</string> +        </property> +       </item> +       <item> +        <property name="text"> +         <string>RPN</string> +        </property> +       </item> +       <item> +        <property name="text"> +         <string>NRPN</string> +        </property> +       </item> +       <item> +        <property name="text"> +         <string>RPN14</string> +        </property> +       </item> +       <item> +        <property name="text"> +         <string>NRPN14</string> +        </property> +       </item> +       <item> +        <property name="text"> +         <string>Pitch</string> +        </property> +       </item> +       <item> +        <property name="text"> +         <string>Program</string> +        </property> +       </item> +      </widget> +     </item> +     <item> +      <widget class="QLabel" name="label_3"> +       <property name="sizePolicy"> +        <sizepolicy hsizetype="Maximum" vsizetype="Preferred"> +         <horstretch>0</horstretch> +         <verstretch>0</verstretch> +        </sizepolicy> +       </property> +       <property name="text"> +        <string/> +       </property> +      </widget> +     </item> +     <item> +      <widget class="QLabel" name="label_4"> +       <property name="sizePolicy"> +        <sizepolicy hsizetype="Maximum" vsizetype="Preferred"> +         <horstretch>0</horstretch> +         <verstretch>0</verstretch> +        </sizepolicy> +       </property> +       <property name="text"> +        <string>Hi:</string> +       </property> +      </widget> +     </item> +     <item> +      <widget class="QSpinBox" name="ctrlHiSpinBox"> +       <property name="sizePolicy"> +        <sizepolicy hsizetype="Maximum" vsizetype="Fixed"> +         <horstretch>0</horstretch> +         <verstretch>0</verstretch> +        </sizepolicy> +       </property> +       <property name="minimum"> +        <number>0</number> +       </property> +       <property name="maximum"> +        <number>255</number> +       </property> +       <property name="value"> +        <number>0</number> +       </property> +      </widget> +     </item> +     <item> +      <widget class="QLabel" name="label_5"> +       <property name="sizePolicy"> +        <sizepolicy hsizetype="Maximum" vsizetype="Preferred"> +         <horstretch>0</horstretch> +         <verstretch>0</verstretch> +        </sizepolicy> +       </property> +       <property name="text"> +        <string>Lo:</string> +       </property> +      </widget> +     </item> +     <item> +      <widget class="QSpinBox" name="ctrlLoSpinBox"> +       <property name="sizePolicy"> +        <sizepolicy hsizetype="Maximum" vsizetype="Fixed"> +         <horstretch>0</horstretch> +         <verstretch>0</verstretch> +        </sizepolicy> +       </property> +       <property name="minimum"> +        <number>0</number> +       </property> +       <property name="maximum"> +        <number>255</number> +       </property> +       <property name="value"> +        <number>0</number> +       </property> +      </widget> +     </item> +     <item> +      <spacer name="horizontalSpacer_2"> +       <property name="orientation"> +        <enum>Qt::Horizontal</enum> +       </property> +       <property name="sizeHint" stdset="0"> +        <size> +         <width>40</width> +         <height>20</height> +        </size> +       </property> +      </spacer> +     </item> +    </layout> +   </item> +   <item> +    <layout class="QHBoxLayout" name="horizontalLayout_4"> +     <item> +      <widget class="QPushButton" name="learnPushButton"> +       <property name="sizePolicy"> +        <sizepolicy hsizetype="Maximum" vsizetype="Fixed"> +         <horstretch>0</horstretch> +         <verstretch>0</verstretch> +        </sizepolicy> +       </property> +       <property name="text"> +        <string>Learn</string> +       </property> +       <property name="checkable"> +        <bool>true</bool> +       </property> +      </widget> +     </item> +     <item> +      <spacer name="horizontalSpacer_3"> +       <property name="orientation"> +        <enum>Qt::Horizontal</enum> +       </property> +       <property name="sizeHint" stdset="0"> +        <size> +         <width>40</width> +         <height>20</height> +        </size> +       </property> +      </spacer> +     </item> +    </layout> +   </item> +   <item> +    <spacer name="verticalSpacer"> +     <property name="orientation"> +      <enum>Qt::Vertical</enum> +     </property> +     <property name="sizeHint" stdset="0"> +      <size> +       <width>20</width> +       <height>18</height> +      </size> +     </property> +    </spacer> +   </item> +   <item> +    <widget class="QDialogButtonBox" name="buttonBox"> +     <property name="orientation"> +      <enum>Qt::Horizontal</enum> +     </property> +     <property name="standardButtons"> +      <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set> +     </property> +    </widget> +   </item> +  </layout> + </widget> + <resources/> + <connections> +  <connection> +   <sender>buttonBox</sender> +   <signal>accepted()</signal> +   <receiver>MidiAudioControlBase</receiver> +   <slot>accept()</slot> +   <hints> +    <hint type="sourcelabel"> +     <x>248</x> +     <y>254</y> +    </hint> +    <hint type="destinationlabel"> +     <x>157</x> +     <y>274</y> +    </hint> +   </hints> +  </connection> +  <connection> +   <sender>buttonBox</sender> +   <signal>rejected()</signal> +   <receiver>MidiAudioControlBase</receiver> +   <slot>reject()</slot> +   <hints> +    <hint type="sourcelabel"> +     <x>316</x> +     <y>260</y> +    </hint> +    <hint type="destinationlabel"> +     <x>286</x> +     <y>274</y> +    </hint> +   </hints> +  </connection> + </connections> +</ui> diff --git a/muse2/muse/widgets/midisync.ui b/muse2/muse/widgets/midisync.ui index a7464aaf..942a4e59 100644 --- a/muse2/muse/widgets/midisync.ui +++ b/muse2/muse/widgets/midisync.ui @@ -10,7 +10,7 @@ configuration dialog</comment>      <x>0</x>      <y>0</y>      <width>655</width> -    <height>419</height> +    <height>445</height>     </rect>    </property>    <property name="windowTitle"> @@ -337,6 +337,100 @@ Enabled inputs in the list will        <item row="3" column="0">         <layout class="QHBoxLayout">          <item> +         <widget class="QComboBox" name="syncRecFilterPreset"> +          <property name="toolTip"> +           <string>Averaging applied to recorded external tempo changes.</string> +          </property> +          <property name="whatsThis"> +           <string>External midi clock can be very jittery. +Tempo is derived from it and recorded. +It is usually desirable to average it and  + limit the number of recorded changes. + +Tiny: 2 section 4/4 = 8 stages. +1/8T note averaging, may produce jitter. + +Small: 3 section 12/8/4 = 24 stages. +1/4 note averaging, may still produce jitter. + +Medium: 3 section 28/12/8 = 48 stages. +1/2 note averaging. Less jitter. + +Large: 4 section 48/48/48/48 = 192 stages. +Use this if the song has only one tempo. +Very low quantization values can be used.  + +Large pre-detect: 4 section 8/48/48/48 = 152 + stages + first stage large step pre-detector. +Use this if you expect sudden large tempo steps. + +None: Use only if high accuracy is needed for  + audio alignment on playback. Caution: Records  + thousands of tempo changes per minute. MusE  + may slow and the song file will be large.</string> +          </property> +         </widget> +        </item> +        <item> +         <widget class="QLabel" name="syncRecFilterLabel"> +          <property name="text"> +           <string>Tempo record averaging</string> +          </property> +          <property name="alignment"> +           <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set> +          </property> +          <property name="wordWrap"> +           <bool>false</bool> +          </property> +         </widget> +        </item> +       </layout> +      </item> +      <item row="4" column="0"> +       <layout class="QHBoxLayout"> +        <item> +         <widget class="QDoubleSpinBox" name="syncRecTempoValQuant"> +          <property name="toolTip"> +           <string/> +          </property> +          <property name="whatsThis"> +           <string/> +          </property> +          <property name="suffix"> +           <string>bpm</string> +          </property> +          <property name="minimum"> +           <double>0.000000000000000</double> +          </property> +          <property name="maximum"> +           <double>100.000000000000000</double> +          </property> +          <property name="singleStep"> +           <double>0.010000000000000</double> +          </property> +          <property name="value"> +           <double>1.000000000000000</double> +          </property> +         </widget> +        </item> +        <item> +         <widget class="QLabel" name="syncRecTempoValQuantLabel"> +          <property name="text"> +           <string>Tempo record quantization</string> +          </property> +          <property name="alignment"> +           <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set> +          </property> +          <property name="wordWrap"> +           <bool>false</bool> +          </property> +         </widget> +        </item> +       </layout> +      </item> +      <item row="5" column="0"> +       <layout class="QHBoxLayout"> +        <item>           <widget class="QSpinBox" name="syncDelaySpinBox">            <property name="toolTip">             <string>Send start to first clock delay</string> @@ -379,7 +473,7 @@ Enabled inputs in the list will          </item>         </layout>        </item> -      <item row="4" column="0"> +      <item row="6" column="0">         <widget class="QTreeWidget" name="devicesListView">          <column>           <property name="text"> @@ -388,18 +482,18 @@ Enabled inputs in the list will          </column>         </widget>        </item> -      <item row="5" column="0"> -        <widget class="QLabel" name="toBeDoneLabel"> +      <item row="7" column="0"> +       <widget class="QLabel" name="toBeDoneLabel">          <property name="text"> -          <string>Note: Sync delay and MTC sync currently not fully implemented</string> +         <string>Note: Sync delay and MTC sync currently not fully implemented</string>          </property>          <property name="alignment"> -          <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set> +         <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>          </property>          <property name="wordWrap"> -          <bool>false</bool> +         <bool>false</bool>          </property> -        </widget> +       </widget>        </item>       </layout>      </widget> diff --git a/muse2/muse/widgets/midisyncimpl.cpp b/muse2/muse/widgets/midisyncimpl.cpp index 904e8759..e286ca74 100644 --- a/muse2/muse/widgets/midisyncimpl.cpp +++ b/muse2/muse/widgets/midisyncimpl.cpp @@ -28,6 +28,7 @@  #include <QTimer>  #include <QTreeWidgetItem>  #include <QHeaderView> +#include <QComboBox>  #include "app.h"  #include "song.h" @@ -137,32 +138,12 @@ void MidiSyncConfig::addDevice(QTreeWidgetItem *item, QTreeWidget *tree)    tree->addTopLevelItem(item);  } -/* -//--------------------------------------------------------- -//   MidiSyncLViewItem -//    setDevice -//--------------------------------------------------------- - -void MidiSyncLViewItem::setDevice(MusECore::MidiDevice* d) -{  -  _device = d;  -  if(_device) -    _syncInfo.copyParams(_device->syncInfo()); -} -*/ - -//--------------------------------------------------------- -//   MidiSyncLViewItem -//    setPort -//--------------------------------------------------------- -  void MidiSyncLViewItem::setPort(int port)  {     _port = port;     if(_port < 0 || port > MIDI_PORTS)      return; -  //_syncInfo.copyParams(MusEGlobal::midiPorts[port].syncInfo());    copyFromSyncInfo(MusEGlobal::midiPorts[port].syncInfo());  } @@ -221,41 +202,6 @@ MidiSyncConfig::MidiSyncConfig(QWidget* parent)        _dirty = false;        applyButton->setEnabled(false); -      //inHeartBeat = true; -       -      //for(int i = 0; i < MIDI_PORTS; ++i) -      //  tmpMidiSyncPorts[i] = midiSyncPorts[i]; -         -      //bool ext = MusEGlobal::extSyncFlag.value(); -      //syncMode->setButton(int(ext)); -      //syncChanged(ext); -//      extSyncCheckbox->setChecked(MusEGlobal::extSyncFlag.value()); - -//      dstDevId->setValue(txDeviceId); -//      srcDevId->setValue(rxDeviceId); -//      srcSyncPort->setValue(rxSyncPort + 1); -//      dstSyncPort->setValue(txSyncPort + 1); - -//      mtcSync->setChecked(genMTCSync); -//      mcSync->setChecked(genMCSync); -//      midiMachineControl->setChecked(genMMC); - -//      acceptMTCCheckbox->setChecked(acceptMTC); -      //acceptMTCCheckbox->setChecked(false); -//      acceptMCCheckbox->setChecked(acceptMC); -//      acceptMMCCheckbox->setChecked(acceptMMC); - -//      mtcSyncType->setCurrentItem(MusEGlobal::mtcType); - -//      mtcOffH->setValue(MusEGlobal::mtcOffset.h()); -//      mtcOffM->setValue(MusEGlobal::mtcOffset.m()); -//      mtcOffS->setValue(MusEGlobal::mtcOffset.s()); -//      mtcOffF->setValue(MusEGlobal::mtcOffset.f()); -//      mtcOffSf->setValue(MusEGlobal::mtcOffset.sf()); - -       -       -              devicesListView->setAllColumnsShowFocus(true);        QStringList columnnames;        columnnames << tr("Port") @@ -284,9 +230,11 @@ MidiSyncConfig::MidiSyncConfig(QWidget* parent)        setToolTips(devicesListView->headerItem());        devicesListView->setFocusPolicy(Qt::NoFocus); -      //MSyncHeaderTip::add(devicesListView->header(), QString("Midi sync ports")); - -//      updateSyncInfoLV(); +      syncRecFilterPreset->addItem(tr("None"), MusECore::MidiSyncInfo::NONE); +      syncRecFilterPreset->addItem(tr("Tiny"), MusECore::MidiSyncInfo::TINY); +      syncRecFilterPreset->addItem(tr("Small"), MusECore::MidiSyncInfo::SMALL); +      syncRecFilterPreset->addItem(tr("Large"), MusECore::MidiSyncInfo::LARGE); +      syncRecFilterPreset->addItem(tr("Large with pre-detect"), MusECore::MidiSyncInfo::LARGE_WITH_PRE_DETECT);        songChanged(-1); @@ -308,14 +256,14 @@ MidiSyncConfig::MidiSyncConfig(QWidget* parent)        connect(mtcSyncType, SIGNAL(activated(int)), SLOT(syncChanged()));        connect(useJackTransportCheckbox, SIGNAL(clicked()), SLOT(syncChanged()));        connect(jackTransportMasterCheckbox, SIGNAL(clicked()), SLOT(syncChanged())); +      connect(syncRecFilterPreset, SIGNAL(currentIndexChanged(int)), SLOT(syncChanged())); +      connect(syncRecTempoValQuant, SIGNAL(valueChanged(double)), SLOT(syncChanged()));        connect(&MusEGlobal::extSyncFlag, SIGNAL(valueChanged(bool)), SLOT(extSyncChanged(bool)));        connect(syncDelaySpinBox, SIGNAL(valueChanged(int)), SLOT(syncChanged()));        // Done in show().        //connect(MusEGlobal::song, SIGNAL(songChanged(int)), SLOT(songChanged(int)));        //connect(MusEGlobal::heartBeatTimer, SIGNAL(timeout()), SLOT(heartBeat())); -       -      //inHeartBeat = false;  }  MidiSyncConfig::~MidiSyncConfig() @@ -356,6 +304,17 @@ void MidiSyncConfig::songChanged(int flags)        jackTransportMasterCheckbox->blockSignals(false);        useJackTransportCheckbox->blockSignals(false);        extSyncCheckbox->blockSignals(false); + +      int fp_idx = syncRecFilterPreset->findData(MusEGlobal::syncRecFilterPreset); +      if(fp_idx != -1) +      { +        syncRecFilterPreset->blockSignals(true); +        syncRecFilterPreset->setCurrentIndex(fp_idx); +        syncRecFilterPreset->blockSignals(false); +      } +      syncRecTempoValQuant->blockSignals(true); +      syncRecTempoValQuant->setValue(MusEGlobal::syncRecTempoValQuant); +      syncRecTempoValQuant->blockSignals(false);        mtcSyncType->setCurrentIndex(MusEGlobal::mtcType); @@ -400,9 +359,6 @@ void MidiSyncConfig::heartBeat()              {                if(!lvi->_curDet)                { -                // Added by Tim. p3.3.6 -                //printf("MidiSyncConfig::heartBeat setting current red icon\n"); -                              lvi->_curDet = true;                  lvi->_inDet = false;                  lvi->setIcon(DEVCOL_IN, QIcon( *record1_Icon)); @@ -411,9 +367,6 @@ void MidiSyncConfig::heartBeat()              else              if(!lvi->_inDet)              { -              // Added by Tim. p3.3.6 -              //printf("MidiSyncConfig::heartBeat setting non-current green icon\n"); -                          lvi->_inDet = true;                lvi->_curDet = false;                lvi->setIcon(DEVCOL_IN, QIcon( *dotIcon)); @@ -423,9 +376,6 @@ void MidiSyncConfig::heartBeat()            {              if(lvi->_curDet || lvi->_inDet)              { -              // Added by Tim. p3.3.6 -              //printf("MidiSyncConfig::heartBeat setting off icon\n"); -                          lvi->_curDet = false;                lvi->_inDet = false;                lvi->setIcon(DEVCOL_IN, QIcon( *dothIcon)); @@ -437,9 +387,6 @@ void MidiSyncConfig::heartBeat()            {              if(!lvi->_tickDet)              { -              // Added by Tim. p3.3.6 -              //printf("MidiSyncConfig::heartBeat setting tick on icon\n"); -                          lvi->_tickDet = true;                lvi->setIcon(DEVCOL_TICKIN, QIcon( *dotIcon));              }   @@ -448,9 +395,6 @@ void MidiSyncConfig::heartBeat()            {              if(lvi->_tickDet)              { -              // Added by Tim. p3.3.6 -              //printf("MidiSyncConfig::heartBeat setting tick off icon\n"); -                          lvi->_tickDet = false;                lvi->setIcon(DEVCOL_TICKIN, QIcon( *dothIcon));              }   @@ -461,9 +405,6 @@ void MidiSyncConfig::heartBeat()            {              if(!lvi->_MRTDet)              { -              // Added by Tim. p3.3.6 -              //printf("MidiSyncConfig::heartBeat setting MRT on icon\n"); -                          lvi->_MRTDet = true;                lvi->setIcon(DEVCOL_MRTIN, QIcon( *dotIcon));              }   @@ -472,9 +413,6 @@ void MidiSyncConfig::heartBeat()            {              if(lvi->_MRTDet)              { -              // Added by Tim. p3.3.6 -              //printf("MidiSyncConfig::heartBeat setting MRT off icon\n"); -                          lvi->_MRTDet = false;                lvi->setIcon(DEVCOL_MRTIN, QIcon( *dothIcon));              }   @@ -487,9 +425,6 @@ void MidiSyncConfig::heartBeat()            {              if(!lvi->_MMCDet)              { -              // Added by Tim. p3.3.6 -              //printf("MidiSyncConfig::heartBeat setting MMC on icon\n"); -                          lvi->_MMCDet = true;                lvi->setIcon(DEVCOL_MMCIN, QIcon( *dotIcon));              } @@ -521,9 +456,6 @@ void MidiSyncConfig::heartBeat()            {              if(lvi->_MMCDet)              { -              // Added by Tim. p3.3.6 -              //printf("MidiSyncConfig::heartBeat setting MMC off icon\n"); -                          lvi->_MMCDet = false;                lvi->setIcon(DEVCOL_MMCIN, QIcon( *dothIcon));              }   @@ -535,9 +467,6 @@ void MidiSyncConfig::heartBeat()              {                if(!lvi->_curMTCDet)                { -                // Added by Tim. p3.3.6 -                //printf("MidiSyncConfig::heartBeat setting current red icon\n"); -                              lvi->_curMTCDet = true;                  lvi->_MTCDet = false;                  lvi->setIcon(DEVCOL_MTCIN, QIcon( *record1_Icon)); @@ -546,9 +475,6 @@ void MidiSyncConfig::heartBeat()              else              if(!lvi->_MTCDet)              { -              // Added by Tim. p3.3.6 -              //printf("MidiSyncConfig::heartBeat setting MTC on icon\n"); -                          lvi->_MTCDet = true;                lvi->_curMTCDet = false;                lvi->setIcon(DEVCOL_MTCIN, QIcon( *dotIcon)); @@ -581,9 +507,6 @@ void MidiSyncConfig::heartBeat()            {              if(lvi->_curMTCDet || lvi->_MTCDet)              { -              // Added by Tim. p3.3.6 -              //printf("MidiSyncConfig::heartBeat setting MTC off icon\n"); -                          lvi->_MTCDet = false;                lvi->_curMTCDet = false;                lvi->setIcon(DEVCOL_MTCIN, QIcon( *dothIcon)); @@ -610,13 +533,6 @@ void MidiSyncConfig::heartBeat()  void MidiSyncConfig::syncChanged()        {        setDirty(); -       -      //MusEGlobal::jackTransportMasterCheckbox->setEnabled(MusEGlobal::useJackTransport); -       -      //acceptMTCCheckbox->setEnabled(val); -//      acceptMTCCheckbox->setEnabled(false); -//      acceptMCCheckbox->setEnabled(val); -//      acceptMMCCheckbox->setEnabled(val);        }  //--------------------------------------------------------- @@ -702,24 +618,14 @@ void MidiSyncConfig::closeEvent(QCloseEvent* e)  void MidiSyncConfig::apply()  { -//      txDeviceId  = dstDevId->value(); -//      rxDeviceId  = srcDevId->value(); -//      rxSyncPort  = srcSyncPort->value() - 1; -//      txSyncPort  = dstSyncPort->value() - 1; - -//      genMTCSync  = mtcSync->isChecked(); -//      genMCSync   = mcSync->isChecked(); -//      genMMC      = midiMachineControl->isChecked(); +      // Protect all structures. +      if(MusEGlobal::audio && MusEGlobal::audio->isRunning()) +        MusEGlobal::audio->msgIdle(true);        MusEGlobal::syncSendFirstClockDelay = syncDelaySpinBox->value();        MusEGlobal::mtcType     = mtcSyncType->currentIndex(); -      //MusEGlobal::extSyncFlag.setValue(syncMode->id(syncMode->selected())); -      //MusEGlobal::extSyncFlag.blockSignals(true);        MusEGlobal::extSyncFlag.setValue(extSyncCheckbox->isChecked()); -//      if(MusEGlobal::extSyncFlag.value()) -//        MusEGlobal::song->setMasterFlag(false); -      //MusEGlobal::extSyncFlag.blockSignals(false);        MusEGlobal::useJackTransport.setValue(useJackTransportCheckbox->isChecked());  //      if(MusEGlobal::useJackTransport)          MusEGlobal::jackTransportMaster = jackTransportMasterCheckbox->isChecked(); @@ -729,33 +635,38 @@ void MidiSyncConfig::apply()        if(MusEGlobal::audioDevice)          MusEGlobal::audioDevice->setMaster(MusEGlobal::jackTransportMaster);       +      if(syncRecFilterPreset->currentIndex() != -1) +      { +        int fp_idx = syncRecFilterPreset->itemData(syncRecFilterPreset->currentIndex()).toInt(); +        if(fp_idx >= 0 && fp_idx < MusECore::MidiSyncInfo::TYPE_END) +        { +          MusEGlobal::syncRecFilterPreset = MusECore::MidiSyncInfo::SyncRecFilterPresetType(fp_idx); +          if(MusEGlobal::midiSeq) +            MusEGlobal::midiSeq->setSyncRecFilterPreset(MusEGlobal::syncRecFilterPreset); +        } +      } +      MusEGlobal::syncRecTempoValQuant = syncRecTempoValQuant->value(); +      if(MusEGlobal::midiSeq) +        MusEGlobal::midiSeq->setRecTempoValQuant(MusEGlobal::syncRecTempoValQuant); +              MusEGlobal::mtcOffset.setH(mtcOffH->value());        MusEGlobal::mtcOffset.setM(mtcOffM->value());        MusEGlobal::mtcOffset.setS(mtcOffS->value());        MusEGlobal::mtcOffset.setF(mtcOffF->value());        MusEGlobal::mtcOffset.setSf(mtcOffSf->value()); -//      acceptMC  = acceptMCCheckbox->isChecked(); -//      acceptMMC = acceptMMCCheckbox->isChecked(); -//      acceptMTC = acceptMTCCheckbox->isChecked(); - -       -	  //MidiSyncLViewItem* lvi = (MidiSyncLViewItem*)devicesListView->firstChild(); -	  //while(lvi)         for (int i = MIDI_PORTS-1; i >= 0; --i)	            {  	MidiSyncLViewItem* lvi = (MidiSyncLViewItem*)devicesListView->topLevelItem(i); -        //MusECore::MidiDevice* dev = lvi->device(); -        // Does the device really exist? -        //if(midiDevices.find(dev) != midiDevices.end()) -        //  dev->syncInfo().copyParams(lvi->syncInfo());          int port = lvi->port();          if(port >= 0 && port < MIDI_PORTS) -          //MusEGlobal::midiPorts[port].syncInfo().copyParams(lvi->syncInfo());            lvi->copyToSyncInfo(MusEGlobal::midiPorts[port].syncInfo());        } +  if(MusEGlobal::audio && MusEGlobal::audio->isRunning()) +    MusEGlobal::audio->msgIdle(false); +      //muse->changeConfig(true);    // save settings    _dirty = false; @@ -777,7 +688,6 @@ void MidiSyncConfig::updateSyncInfoLV()        {              MusECore::MidiPort* port  = &MusEGlobal::midiPorts[i];              MusECore::MidiDevice* dev = port->device(); -            // p3.3.31              // Don't show if it is a synthesizer device.              // Hmm, some synths might support transport commands or even sync?              // If anything, the DSSI or VST synths just might...  @@ -791,9 +701,6 @@ void MidiSyncConfig::updateSyncInfoLV()              s.setNum(i+1);              MidiSyncLViewItem* lvi = new MidiSyncLViewItem(devicesListView);              lvi->setPort(i); // setPort will copy parameters. -            //MusECore::MidiSyncInfo& si = lvi->syncInfo(); -            //si.copyParams(port->syncInfo()); -            //lvi.copyFromSyncInfo(port->syncInfo());              MusECore::MidiSyncInfo& portsi = port->syncInfo();              lvi->setText(DEVCOL_NO, s); @@ -925,11 +832,6 @@ void MidiSyncConfig::updateSyncInfoLV()                //lvi->setText(DEVCOL_MTCTYPE, "--");              } -            //lvi->setText(DEVCOL_RID,    QString().setNum(si.idIn()) ); -            //lvi->setRenameEnabled(DEVCOL_RID, true); -            //lvi->setIcon(DEVCOL_RCLK, QIcon( si.MCIn() ? *dotIcon : *dothIcon)); -            //lvi->setIcon(DEVCOL_RMMC, QIcon( si.MMCIn() ? *dotIcon : *dothIcon)); -            //lvi->setIcon(DEVCOL_RMTC, QIcon( si.MTCIn() ? *dotIcon : *dothIcon));              lvi->setText(DEVCOL_RID,    QString().setNum(lvi->_idIn) );              lvi->setIcon(DEVCOL_RCLK, QIcon( lvi->_recMC ? *dotIcon : *dothIcon));              lvi->setIcon(DEVCOL_RMRT, QIcon( lvi->_recMRT ? *dotIcon : *dothIcon)); @@ -937,11 +839,6 @@ void MidiSyncConfig::updateSyncInfoLV()              lvi->setIcon(DEVCOL_RMTC, QIcon( lvi->_recMTC ? *dotIcon : *dothIcon));              lvi->setIcon(DEVCOL_RREWSTART, QIcon( lvi->_recRewOnStart ? *dotIcon : *dothIcon)); -            //lvi->setText(DEVCOL_TID,    QString().setNum(si.idOut()) ); -            //lvi->setRenameEnabled(DEVCOL_TID, true); -            //lvi->setIcon(DEVCOL_TCLK, QIcon( si.MCOut() ? *dotIcon : *dothIcon)); -            //lvi->setIcon(DEVCOL_TMMC, QIcon( si.MMCOut() ? *dotIcon : *dothIcon)); -            //lvi->setIcon(DEVCOL_TMTC, QIcon( si.MTCOut() ? *dotIcon : *dothIcon));              lvi->setText(DEVCOL_TID,          QString().setNum(lvi->_idOut) );              lvi->setIcon(DEVCOL_TCLK, QIcon(lvi->_sendMC ? *dotIcon : *dothIcon));              lvi->setIcon(DEVCOL_TMRT, QIcon(lvi->_sendMRT ? *dotIcon : *dothIcon)); @@ -988,38 +885,6 @@ void MidiSyncConfig::updateSyncInfoLV()  	    devicesListView->header()->setResizeMode(DEVCOL_TMRT, QHeaderView::Fixed);  	    devicesListView->header()->setResizeMode(DEVCOL_TMMC, QHeaderView::Fixed); -       -      /* -      for(MusECore::iMidiDevice id = midiDevices.begin(); id != midiDevices.end(); ++id)  -      { -            MusECore::MidiDevice* dev = *id; -       -            //MusECore::MidiPort* port  = &MusEGlobal::midiPorts[i]; -            //MusECore::MidiDevice* dev = port->device(); -            MidiSyncLViewItem* lvi = new MidiSyncLViewItem(devicesListView); -            //lvi->setPort(i); -            // setDevice will copy parameters. -            lvi->setDevice(dev); -            MusECore::MidiSyncInfo& si = lvi->syncInfo(); -            //si.copyParams(dev->syncInfo()); -             -            lvi->setText(DEVCOL_NAME, dev->name()); -             -            lvi->setIcon(DEVCOL_IN, QIcon(   si.MCSyncDetect() ? *dotIcon : *dothIcon)); -             -            lvi->setText(DEVCOL_RID,    QString().setNum(si.idIn()) ); -            lvi->setIcon(DEVCOL_RCLK, QIcon( si.MCIn() ? *dotIcon : *dothIcon)); -            lvi->setIcon(DEVCOL_RMMC, QIcon( si.MMCIn() ? *dotIcon : *dothIcon)); -            lvi->setIcon(DEVCOL_RMTC, QIcon( si.MTCIn() ? *dotIcon : *dothIcon)); -             -            lvi->setText(DEVCOL_TID,    QString().setNum(si.idOut()) ); -            lvi->setIcon(DEVCOL_TCLK, QIcon( si.MCOut() ? *dotIcon : *dothIcon)); -            lvi->setIcon(DEVCOL_TMMC, QIcon( si.MMCOut() ? *dotIcon : *dothIcon)); -            lvi->setIcon(DEVCOL_TMTC, QIcon( si.MTCOut() ? *dotIcon : *dothIcon)); -             -            devicesListView->insertItem(lvi); -      } -      */        } @@ -1027,7 +892,6 @@ void MidiSyncConfig::updateSyncInfoLV()  //   dlvClicked  //--------------------------------------------------------- -//void MidiSyncConfig::dlvClicked(QListViewItem* item, const QPoint&, int col)  void MidiSyncConfig::dlvClicked(QTreeWidgetItem* item, int col)  {        if (item == 0) @@ -1042,14 +906,6 @@ void MidiSyncConfig::dlvClicked(QTreeWidgetItem* item, int col)        //if(midiDevices.find(dev) == midiDevices.end())        //  return; -      //int n; -      //MusECore::MidiPort* port      = &MusEGlobal::midiPorts[no]; -      //MusECore::MidiDevice* dev     = port->device(); -      //int rwFlags         = dev ? dev->rwFlags() : 0; -      //int openFlags       = dev ? dev->openFlags() : 0; -      //MusECore::MidiSyncInfo& si    = lvi->syncInfo(); -      //MusECore::MidiSyncInfo& portsi  = MusEGlobal::midiPorts[no].syncInfo(); -        switch (col)         {              case DEVCOL_NO: @@ -1060,8 +916,6 @@ void MidiSyncConfig::dlvClicked(QTreeWidgetItem* item, int col)                    // If this is not the current midi sync in port, and sync in from this port is enabled,                    //  and sync is in fact detected on this port, allow the user to force this port to now be the                    //  current sync in port.  -                  //if(no != MusEGlobal::curMidiSyncInPort && si.MCIn() && MusEGlobal::midiPorts[no].syncInfo().MCSyncDetect()) -                  //if(no != MusEGlobal::curMidiSyncInPort && lvi->_recMC && MusEGlobal::midiPorts[no].syncInfo().MCSyncDetect())                    if(no != MusEGlobal::curMidiSyncInPort)                    {                      if(lvi->_recMC && MusEGlobal::midiPorts[no].syncInfo().MCSyncDetect()) @@ -1084,8 +938,6 @@ void MidiSyncConfig::dlvClicked(QTreeWidgetItem* item, int col)                    // If this is not the current midi sync in port, and sync in from this port is enabled,                    //  and sync is in fact detected on this port, allow the user to force this port to now be the                    //  current sync in port.  -                  //if(no != MusEGlobal::curMidiSyncInPort && si.MTCIn() && MusEGlobal::midiPorts[no].syncInfo().MTCDetect()) -                  //if(no != MusEGlobal::curMidiSyncInPort && lvi->_recMTC && MusEGlobal::midiPorts[no].syncInfo().MTCDetect())                    if(no != MusEGlobal::curMidiSyncInPort)                    {                      if(lvi->_recMTC && MusEGlobal::midiPorts[no].syncInfo().MTCDetect()) @@ -1105,8 +957,6 @@ void MidiSyncConfig::dlvClicked(QTreeWidgetItem* item, int col)              case DEVCOL_RID:                    break;              case DEVCOL_RCLK: -                  //si.setMCIn(si.MCIn() ? false : true); -                  //lvi->setIcon(DEVCOL_RCLK, QIcon( si.MCIn() ? *dotIcon : *dothIcon));                    lvi->_recMC = (lvi->_recMC ? false : true);                    lvi->setIcon(DEVCOL_RCLK, QIcon( lvi->_recMC ? *dotIcon : *dothIcon));                    setDirty(); @@ -1117,15 +967,11 @@ void MidiSyncConfig::dlvClicked(QTreeWidgetItem* item, int col)                    setDirty();                    break;              case DEVCOL_RMMC: -                  //si.setMMCIn(si.MMCIn() ? false : true); -                  //lvi->setIcon(DEVCOL_RMMC, QIcon( si.MMCIn() ? *dotIcon : *dothIcon));                    lvi->_recMMC = (lvi->_recMMC ? false : true);                    lvi->setIcon(DEVCOL_RMMC, QIcon( lvi->_recMMC ? *dotIcon : *dothIcon));                    setDirty();                    break;              case DEVCOL_RMTC: -                  //si.setMTCIn(si.MTCIn() ? false : true); -                  //lvi->setIcon(DEVCOL_RMTC, QIcon( si.MTCIn() ? *dotIcon : *dothIcon));                    lvi->_recMTC = (lvi->_recMTC ? false : true);                    lvi->setIcon(DEVCOL_RMTC, QIcon( lvi->_recMTC ? *dotIcon : *dothIcon));                    setDirty(); @@ -1138,8 +984,6 @@ void MidiSyncConfig::dlvClicked(QTreeWidgetItem* item, int col)              case DEVCOL_TID:                    break;              case DEVCOL_TCLK: -                  //si.setMCOut(si.MCOut() ? false : true); -                  //lvi->setIcon(DEVCOL_TCLK, QIcon( si.MCOut() ? *dotIcon : *dothIcon));                    lvi->_sendMC = (lvi->_sendMC ? false : true);                    lvi->setIcon(DEVCOL_TCLK, QIcon( lvi->_sendMC ? *dotIcon : *dothIcon));                    setDirty(); @@ -1150,15 +994,11 @@ void MidiSyncConfig::dlvClicked(QTreeWidgetItem* item, int col)                    setDirty();                    break;              case DEVCOL_TMMC: -                  //si.setMMCOut(si.MMCOut() ? false : true); -                  //lvi->setIcon(DEVCOL_TMMC, QIcon( si.MMCOut() ? *dotIcon : *dothIcon));                    lvi->_sendMMC = (lvi->_sendMMC ? false : true);                    lvi->setIcon(DEVCOL_TMMC, QIcon( lvi->_sendMMC ? *dotIcon : *dothIcon));                    setDirty();                    break;              case DEVCOL_TMTC: -                  //si.setMTCOut(si.MTCOut() ? false : true); -                  //lvi->setIcon(DEVCOL_TMTC, QIcon( si.MTCOut() ? *dotIcon : *dothIcon));                    lvi->_sendMTC = (lvi->_sendMTC ? false : true);                    lvi->setIcon(DEVCOL_TMTC, QIcon( lvi->_sendMTC ? *dotIcon : *dothIcon));                    setDirty(); @@ -1183,21 +1023,13 @@ void MidiSyncConfig::dlvDoubleClicked(QTreeWidgetItem* item, int col)        MidiSyncLViewItem* lvi = (MidiSyncLViewItem*)item; -      //if(col == DEVCOL_RID) -      //  lvi->startRename(DEVCOL_RID);  -      //else -      //if(col == DEVCOL_TID) -      //  lvi->startRename(DEVCOL_TID);  -              bool ok = false;        if(col == DEVCOL_RID)        { -        //int val = lvi->syncInfo().idIn();          int val = lvi->_idIn;          int newval = QInputDialog::getInteger(this, "Muse: Sync info" , "Enter new id number (127 = all):", val, 0, 127, 1, &ok);          if(ok)          { -          //lvi->syncInfo().setIdIn(newval);            lvi->_idIn = newval;            lvi->setText(DEVCOL_RID, QString().setNum(newval));           }   @@ -1205,12 +1037,10 @@ void MidiSyncConfig::dlvDoubleClicked(QTreeWidgetItem* item, int col)        else        if(col == DEVCOL_TID)        { -        //int val = lvi->syncInfo().idOut();          int val = lvi->_idOut;          int newval = QInputDialog::getInteger(this, "Muse: Sync info" , "Enter new id number (127 = global):", val, 0, 127, 1, &ok);          if(ok)          { -          //lvi->syncInfo().setIdOut(newval);            lvi->_idOut = newval;            lvi->setText(DEVCOL_TID, QString().setNum(newval));           }   @@ -1220,41 +1050,6 @@ void MidiSyncConfig::dlvDoubleClicked(QTreeWidgetItem* item, int col)          setDirty();  } -/* -//--------------------------------------------------------- -//   renameOk -//--------------------------------------------------------- -//void MidiSyncConfig::renameOk(QListViewItem* item, int col) -void MidiSyncConfig::renameOk(QListViewItem* item, int col, const QString & text) -{ -      if(!item) -        return; -       -      MidiSyncLViewItem* lvi = (MidiSyncLViewItem*)item; -      QString t = text; -      bool ok; -      int id = text.toInt(&ok); -      if(!ok) -      { -        lvi->setText(t); -        return; -      }   -      if(col == DEVCOL_RID) -      { -        //lvi->syncInfo().setIdIn(id); -        lvi->_idIn = id; -        setDirty(); -      }   -      else -      if(col == DEVCOL_TID) -      { -        //lvi->syncInfo().setIdOut(id); -        lvi->_idOut = id; -        setDirty(); -      }   -} -*/ -  //---------------------------------------------------------  //   MidiSyncConfig::setDirty  //--------------------------------------------------------- diff --git a/muse2/muse/widgets/musewidgetsplug.cpp b/muse2/muse/widgets/musewidgetsplug.cpp index bee05d51..9c82b5f5 100644 --- a/muse2/muse/widgets/musewidgetsplug.cpp +++ b/muse2/muse/widgets/musewidgetsplug.cpp @@ -215,7 +215,7 @@ MusEGlobal::GlobalConfigValues config = {        QString("./"),                // projectBaseFolder        true,                         // projectStoreInFolder        true,                         // useProjectSaveDialog -      64,                           // minControlProcessPeriod +      256,                          // minControlProcessPeriod        false,                        // popupsDefaultStayOpen        false,                        // leftMouseButtonCanDecrease        false,                        // rangeMarkerWithoutMMB diff --git a/muse2/muse/widgets/scldraw.cpp b/muse2/muse/widgets/scldraw.cpp index 38adff25..aec769a0 100644 --- a/muse2/muse/widgets/scldraw.cpp +++ b/muse2/muse/widgets/scldraw.cpp @@ -636,7 +636,7 @@ int ScaleDraw::maxHeight(QPainter *p) const  //------------------------------------------------------------  QRect ScaleDraw::maxBoundingRect(QPainter *p) const  { -    int i, wl,h,wmax; +    int i, wl; //,wmax;      int a, ar, amin, amax;      double arc; @@ -645,7 +645,6 @@ QRect ScaleDraw::maxBoundingRect(QPainter *p) const      QFontMetrics fm = p->fontMetrics();      wl = maxLabelWidth(p, TRUE); -    h = fm.height();      switch(d_orient)      { @@ -722,7 +721,7 @@ QRect ScaleDraw::maxBoundingRect(QPainter *p) const  	r.setBottom(MusECore::qwtInt(d_yCenter - (d_radius + double(d_majLen + d_vpad)) * cos(arc))  		    + fm.height() ); -	wmax = d_len + d_majLen + d_hpad + wl; +	//wmax = d_len + d_majLen + d_hpad + wl; DELETETHIS  	r.setLeft(d_xorg - d_majLen - d_hpad - wl);  	r.setWidth(d_len + 2*(d_majLen + d_hpad + wl)); diff --git a/muse2/muse/widgets/sliderbase.cpp b/muse2/muse/widgets/sliderbase.cpp index 15497235..5909c64d 100644 --- a/muse2/muse/widgets/sliderbase.cpp +++ b/muse2/muse/widgets/sliderbase.cpp @@ -118,6 +118,7 @@ void SliderBase::wheelEvent(QWheelEvent *e)              setValue(value()-inc);       emit sliderMoved(value(), _id); +     emit sliderMoved(value(), _id, (bool)(e->modifiers() & Qt::ShiftModifier));  } @@ -184,6 +185,7 @@ void SliderBase::mousePressEvent(QMouseEvent *e)                    d_mouseOffset = 0;                    DoubleRange::incPages(d_direction);                    emit sliderMoved(value(), _id); +                  emit sliderMoved(value(), _id, (bool)(e->modifiers() & Qt::ShiftModifier));                    d_tmrID = startTimer(MusECore::qwtMax(250, 2 * d_updTime));                    break; @@ -394,6 +396,7 @@ void SliderBase::mouseMoveEvent(QMouseEvent *e)    }    if (value() != prevValue())       emit sliderMoved(value(), _id); +     emit sliderMoved(value(), _id, (bool)(e->modifiers() & Qt::ShiftModifier));      }  } @@ -444,7 +447,10 @@ void SliderBase::timerEvent(QTimerEvent*)    DoubleRange::incPages(d_direction);    if (value() != prevValue()) +  {       emit sliderMoved(value(), _id); +     emit sliderMoved(value(), _id, false); +  }    if (!d_timerTick)    { @@ -456,7 +462,10 @@ void SliderBase::timerEvent(QTimerEvent*)    DoubleRange::fitValue(value() +  double(d_direction) * inc);    if (value() != prevValue()) +  {       emit sliderMoved(value(), _id); +     emit sliderMoved(value(), _id, false); +  }    if (!d_timerTick)    { @@ -620,6 +629,7 @@ void SliderBase::stepPages(int pages)  {    DoubleRange::incPages(pages);    emit sliderMoved(value(), _id); +  emit sliderMoved(value(), _id, false);  } @@ -722,7 +732,7 @@ void SliderBase::stepPages(int pages)  //  slider with the mouse.  //  //.u  Syntax -//.f  void SliderBase::sliderMoved(double value, int _id) +//.f  void SliderBase::sliderMoved(double value, int _id [, bool shift])  //  //.u  Parameters  //.p  double value  -- new value diff --git a/muse2/muse/widgets/sliderbase.h b/muse2/muse/widgets/sliderbase.h index 56c7a586..abea5dd6 100644 --- a/muse2/muse/widgets/sliderbase.h +++ b/muse2/muse/widgets/sliderbase.h @@ -86,6 +86,7 @@ class SliderBase : public QWidget, public DoubleRange    void sliderPressed(int id);    void sliderReleased(int id);    void sliderMoved(double value, int id); +  void sliderMoved(double value, int id, bool shift);    void sliderRightClicked(const QPoint &p, int id);   public: | 
