summaryrefslogtreecommitdiff
path: root/muse2/muse
diff options
context:
space:
mode:
authorFlorian Jung <flo@windfisch.org>2012-07-01 16:42:16 +0000
committerFlorian Jung <flo@windfisch.org>2012-07-01 16:42:16 +0000
commit9c4664d162c537ba4dd4fd8220971c0fb727103a (patch)
tree37a28b7cd4e4d8984ad4934a4884cd7b4da0505c /muse2/muse
parente87fedf1be804f7ec774071d844b1f163be30b96 (diff)
final merge
Diffstat (limited to 'muse2/muse')
-rw-r--r--muse2/muse/arranger/arranger.cpp8
-rw-r--r--muse2/muse/arranger/arranger.h2
-rw-r--r--muse2/muse/arranger/pcanvas.cpp88
-rw-r--r--muse2/muse/arranger/pcanvas.h2
-rw-r--r--muse2/muse/arranger/tlist.cpp186
-rw-r--r--muse2/muse/audio.cpp110
-rw-r--r--muse2/muse/audio.h22
-rw-r--r--muse2/muse/audiotrack.cpp339
-rw-r--r--muse2/muse/conf.cpp19
-rw-r--r--muse2/muse/confmport.cpp41
-rw-r--r--muse2/muse/ctrl.cpp471
-rw-r--r--muse2/muse/ctrl.h68
-rw-r--r--muse2/muse/driver/alsamidi.cpp10
-rw-r--r--muse2/muse/driver/jack.cpp182
-rw-r--r--muse2/muse/driver/jackaudio.h2
-rw-r--r--muse2/muse/driver/jackmidi.cpp38
-rw-r--r--muse2/muse/dssihost.cpp173
-rw-r--r--muse2/muse/dssihost.h4
-rw-r--r--muse2/muse/gconfig.cpp2
-rw-r--r--muse2/muse/globals.cpp6
-rw-r--r--muse2/muse/globals.h4
-rw-r--r--muse2/muse/midi.cpp199
-rw-r--r--muse2/muse/midictrl.cpp33
-rw-r--r--muse2/muse/midictrl.h1
-rw-r--r--muse2/muse/mididev.cpp15
-rw-r--r--muse2/muse/midiseq.cpp74
-rw-r--r--muse2/muse/midiseq.h19
-rw-r--r--muse2/muse/mixer/astrip.cpp53
-rw-r--r--muse2/muse/mixer/astrip.h4
-rw-r--r--muse2/muse/mixer/panknob.cpp1
-rw-r--r--muse2/muse/mpevent.cpp8
-rw-r--r--muse2/muse/mpevent.h14
-rw-r--r--muse2/muse/node.cpp16
-rw-r--r--muse2/muse/osc.cpp4
-rw-r--r--muse2/muse/part.cpp61
-rw-r--r--muse2/muse/part.h13
-rw-r--r--muse2/muse/plugin.cpp624
-rw-r--r--muse2/muse/plugin.h11
-rw-r--r--muse2/muse/seqmsg.cpp19
-rw-r--r--muse2/muse/song.cpp179
-rw-r--r--muse2/muse/song.h11
-rw-r--r--muse2/muse/sync.cpp174
-rw-r--r--muse2/muse/sync.h7
-rw-r--r--muse2/muse/tempo.cpp86
-rw-r--r--muse2/muse/tempo.h56
-rw-r--r--muse2/muse/thread.cpp53
-rw-r--r--muse2/muse/track.cpp17
-rw-r--r--muse2/muse/track.h5
-rw-r--r--muse2/muse/undo.h6
-rw-r--r--muse2/muse/wave.cpp55
-rw-r--r--muse2/muse/wavetrack.cpp12
-rw-r--r--muse2/muse/widgets/CMakeLists.txt3
-rw-r--r--muse2/muse/widgets/aboutbox.ui4
-rw-r--r--muse2/muse/widgets/bigtime.cpp5
-rw-r--r--muse2/muse/widgets/filedialog.cpp30
-rw-r--r--muse2/muse/widgets/filedialog.h3
-rw-r--r--muse2/muse/widgets/midi_audio_control.cpp340
-rw-r--r--muse2/muse/widgets/midi_audio_control.h60
-rw-r--r--muse2/muse/widgets/midi_audio_control_base.ui310
-rw-r--r--muse2/muse/widgets/midisync.ui110
-rw-r--r--muse2/muse/widgets/midisyncimpl.cpp283
-rw-r--r--muse2/muse/widgets/musewidgetsplug.cpp2
-rw-r--r--muse2/muse/widgets/scldraw.cpp5
-rw-r--r--muse2/muse/widgets/sliderbase.cpp12
-rw-r--r--muse2/muse/widgets/sliderbase.h1
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", &parameter);
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", &parameter);
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 = &params[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 = &params[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: